PDF文档处理工作中,总是绕不开对文本提取的需求。很多用户觉得我们PDFlux好用,所以对其中的底层技术也非常感兴趣。也有人为认为,从PDF里抽取文本段落和表格,应该非常简单!

近期,我们会对PDF文档的前世今生和我们对底层技术的认识做一些分享。今天这篇是这一系列技术分享的第一篇。

我们关注到国外的一家公司FillingDB近期发表了一篇技术性的博文:

What's so hard about PDF text extraction?

There is a common view that extracting text from a PDF document should not be too difficult. After all, the text is right there in front of our eyes and humans consume PDF content all the time with great success. Why would it be difficult to automatically extract the text data? https://www.filingdb.com/pdf-text-extraction

这篇文章在Hacker News上引发了非常广泛的讨论,不仅引起技术大咖们的共鸣,还得到了很多追评。借此机会,我们将这篇文章进行了翻译及整理,希望能抛砖引玉,与大家一起分享讨论,以下是原文翻译:

为什么说从PDF中提取文本是一件困难的事?

通常人们会认为,从PDF中进行文本提取,这绝非难事。毕竟这些信息已经非常直观地展现在了我们眼前,而且人们也一直成功地从PDF中取得自己想要的内容,那么想要自动的提取这些文本信息怎么会是一件困难的事情呢?

事实证明,由于大量的极端案例和错误的假设使得我们得出一个结论:“计算机系统很难去辨认以及处理人们的姓名”(Ref:https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/);同上述结论类似,由于PDF的格式过于灵活不统一,这使得PDF中进行自动文本提取变得格外困难。

最主要的原因就是,在设计PDF的最初,它就被设计成一种可以准确地控制生成文档格式的输出模式;而非一种数据输入模式。

PDF格式的核心,包含了一系列描述如何在页面上进行绘制的指令流。尤其是其中的文本数据并非是以段落或文字的形式进行存储,而是以记录了页面特定位置信息的字符的形式进行存储。所以,当文本或Word文档被转换为PDF时,会丧失大部分文本原有的语义信息,所有隐含的文档结构都会在页面上被转化为空泛的字符。

为了构建FilingDB,我们从大量的PDF中进行了数据提取。在这个过程中,我们发现,我们对于之前对PDF文档结构提出的所有假设都几乎是不成立的。我们的任务非常艰巨,因为我们需要处理来源繁多的PDF文档(其中包括各种样式、排版以及表达方式)。

以下记录了一些让PDF文件的文本提取变得困难(甚至不可能)的原因;

1、PDF的读取保护

您可能曾经也遇到过部分PDF文档拒绝让您复制其中的文本内容。以下图为例,当用户尝试着从“防拷贝文档”中复制文本时,SumatraPDF 就会提示以下内容:

有趣的是,虽然文本已经被直观地展示出来,但PDF阅读器仍会拒绝将高亮的文本添加至剪贴板,使其无法进行复制。

实现该功能的方法是存在着几个“访问权限”,其中之一就是控制是否允许复制内容。最重要的是要记住,这个限制不是由PDF文件强制去执行的(即PDF文件中的实际内容不受影响),而是由PDF renderer去主动地遵守这几个权限。

然而,这并没有为防止从PDF中提取文本提供真正的保护,因为任何成熟的PDF处理库都允许用户去自行切换权限,或者是忽略这些权限。

2、页面外的字符

PDF文档中包含的文本数据经常比实际页面上展示出来的文本数据要多,以2010年雀巢年报为例:

对图中这一页而言,实际相关的文本其实要比我们直观看到的要多。尤其是实际关联的内容数据中可以找到以下内容:

KitKat celebrated its 75th anniversary in 2010 but remains young and in touch with trends, having over 2.5 million Facebook fans. It is sold in over 70 countries and enjoys good growth in the developed world and emerging markets, such as the Middle East, India and Russia. Japan is its second biggest market.

因为这段文本实际上处于该页面的边界框外,所以大多数的PDF阅读器不会将它直观地展现出来。然而,因数据本身是存在在这个页面的,所以在用程序提取文本时,这段文字就会显示出来。

由于在定稿过程中的最后一刻才决定删除或替换该相关文本,所以导致这种情况会时不时的发生。

3、小字符/隐藏字符

PDF有时会在页面中存在一些非常小或隐藏的文本。以2012年雀巢年报的其中一页例,该页面包含了部分在白色背景中的白色小文本,内容如下:

Wyeth Nutrition logo Identity Guidance to markets Vevey October 2012 RCC/CI&D

有时这样做是为了便于访问,这样的功能类似于在HTML中如何使用alt。

4、过多的空格

PDF有时候会在一个单词的字母间夹杂了额外的空格,这大部分原因是为了字距调整。(“字距调整”是指在排版过程中调整字符之间距离的过程)

例子:Hikma Pharma2013年年报,包含以下文本:

提取图中文本得到:

“ch a i r m a n ' s s tat em en t”

重构原文是一个公认难以解决的问题,目前最成功的解决方法就是采用OCR技术。

5、空格不足

PDF有时候不包含空格或将其替代为其他字符。

例1:以下图片来源于SEB2017年年度报告

提取后的文本显示为:

“Tenyearsafterthefinancialcrisisstarted”

例2:以下图片来源于Eurobank 2013年年度报告

提取文本后显示:

“On_April_7,_2013,_the_competent_authorities”

同样,对应这个问题我们最成功的解决方法也是采用OCR技术。

6、嵌入式字体

PDF的字体处理方式是十分复杂的。想要明白PDF文件如何存储文本数据,我们首先需要了解文本的字形(glyphs)、字形名称(glyphs name)和字体。

  • 字形是一组描述如何画出一个符号或一个字符的指令。
  • 字形名称是一个字形的名字。例如,“™”字形的字形名称是“trademark”, “a”字形的字形名称是“a”。
  • 字体是包含一组字形和其相对应的字形名称的集合。比如说,大多数字体中都包含能被大多数人识别为字母“a”的字形,而不同字体中,画字母“a”的方式往往是不同的。

在PDF文件中,字符是以数字形式进行存储的,这些数字叫做“码位”(codepoint)。为了决定如何在屏幕上绘画,渲染器需要走下列步骤:

码位 -> 字形名称 -> 字形

比如说,一个PDF中文件包含了码位116。PDF会找到这个码位对应的字形名称“t”,然后再通过查询字形“t”来看如何在屏幕上画出“t”这个字母。

大多数PDF文件都使用了标准的码位编码方式。码位编码是一系列用来给码位指定意义的规则。例如:

  • ASCII和Unicode均使用码位116来代表字母“t”。
  • 在Unicode中,码位9786对应着“白色笑脸”,被渲染出来就是☺,而在ASCII中码位9786并没有定义。
  • 然而,PDF文档有时会同时使用常见的字体和自定义的编码方式。这听上去可能有些奇怪,但是一个文件可以将码位1映射到字符名称“c1”上,然后再把字符名称“c1”映射到描述如何画字母“t”的字形上。

对于我们人类来说,这两张图的结果是一样的,然而机器则会被它们不同的编码方式弄迷糊。如果不采取标准的编码方式定义码位,从计算器或者程序的角度来说,是无法知道码位“1”、“2”和“3”代表了什么的。

为什么有的PDF文档会有非标准的字体和编码方式呢?

  • 一个原因是为了让文字提取变得更加困难。
  • 另外一个原因是子字体。大多数字体包含着大量的码位和其对应的字形,而一个PDF文件中可能只会用到其中的一部分。为了节省空间,PDF的制作者可以去掉所有不需要的字形来创建一个子字体。这个子字体往往会使用非标准的编码方式。
  • 一个替代方案是从文件中提取出字体的字形,把提取出来的字形用OCR文字识别软件识别出来,然后再把识别出的字形和Unicode建立起映射。这样便可以从字体特有的编码方式“翻译”成Unicode。比如,码位1可以映射到字形名称“c1”,“c1”对应的字形看起来应该是“t”,也就是Unicode中的码位116。

我们刚刚创造的从1对应到116的编码映射,在PDF的行业标准中被称为ToUnicode映射。PDF文档可以提供其自有的ToUnicode映射,但这并不是必须的,很多PDF文档也并没有提供。

7、单词和段落检测

从PDF文件中将这些空泛的字符重建成段落或单词是一项十分困难的任务。

PDF文档提供了某个页面上的字符列表,并且由用户来确定单词和段落。人类可以自然有效地运用阅读这项技能做到这一点。

最常用的方式就是通过使用分组或聚类算法,来比较字母的大小、位置以及对其方式,来确定其是单词还是段落。

简单的执行会使得其复杂度很容易就大于O(n²),导致在繁忙的页面上的处理时间过长。

8、文本和段落排列顺序

在两个层面上去决定文本和段落的排列顺序是十分困难的。

首先,对于这个顺序而言,有时没有绝对的正确答案。使用传统的单列排版的文档能够展现出自然的阅读顺序,而使用复杂排版的文档就使得辨别阅读顺序更加具有挑战性。以截图为例,我们并不清楚其中的插图应该出现在文章的哪个位置,是之前、之后或者之间;

第二,即使有的时候是对于人们来说十分明显的顺序答案,对于程序而言确定一个稳定的段落顺序仍旧是一个十分困难的问题,对于人工智能而言可能更加困难。这听起来可能是一个比较极端的说法,但在某些情况下,正确的段落阅读顺序的确只能通过语义理解来决定。

下列文字主要描述了如何准备一份蔬菜沙拉,以双栏排版为例

对于西方而言,一个合理的阅读顺序是从左到右,从上到下的。因此如果我们不去阅读文字内容,那么我们能做到最好的步骤是将排列顺序的答案的范围缩小到两个选项:A B C D 和 A C B D。

然后再通过查看文本内容,去了解它实际内容的含义;我们知道切菜的前一个步骤应该是洗菜。所以我们可以通过这个,确定A C B D 是一个正确的排列顺序。但在算法上,确认这段文本的阅读的排列顺序是一个非常困难的事情。

也就是说,这种“大致有效”的方法通常是依赖于PDF文档中文本的存储顺序。也就是在文档创建时,与文本的插入顺序相对应;对于含有多个段落的文本而言,这个文本的插入顺序往往反映出了作者最初想要表达的阅读顺序。

9、嵌入图像

PDF扫描件是一种很普遍的文件。对于这部分文件,没有可以直接进行提取的文本数据,因此我们不得不借助于OCR技术。以Yell2011年年度报告的扫描件作为例子:

10、为什么不能一直使用OCR?

虽然OCR 有助于解决上述的一系列的问题,但它本身也有自己的很多缺点:

  • 处理时间长

在PDF扫描件上跑OCR 需要的时间要比直接从PDF中提取文本多至少一个数量级的时间

  • 不标准的字符和字形

OCR算法很难处理新的字符,比如笑脸、星形、圆形、正方形(用于项目符号列表)、上标以及复杂的数学符号等。

  • 没有文本阅读顺序的提示

相较而言,从PDF文档中直接提取的文本更容易将其进行排序,因为大多数情况下文本的插入顺序就直接提示了该文档正确的阅读顺序。然而从图像中提取文本就不会有这样的辅助信息了。

11、如何测试?

到目前为止,我们都还没有提到评估文本提取的正确性,或者评估提取的文本是否符合我们的预期是一件多么困难的事情。我们目前发现的最有效的方法是进行一系列广泛的测试,这些测试即关注基本指标(如文本长度、页面长度、空格和单词比率),又关注复杂的指标(如英文单词与未识别单词的百分比、数字的百分比),并且还要检查是否存在红色的标记(例如可疑字符或意料之外的字符等)。

那么,当从PDF中提取文本时,我们给出的建议是什么呢?就是在提取开始前,我们要确保已经没有更好的替代数据源可以使用了。

如果您所想要的数据全部都来源于PDF格式,那么就要注意了,这虽然是一个看似简单的过程,但想要获得100%的准确率是几乎不可能的。

linux中将文本中的单词换掉的指令_为什么说从PDF中提取文本是一件困难的事?...相关推荐

  1. linux中将文本中的单词换掉的指令_干货:Linux常用命令全称及讲解

    从事IT行业的很多人都会使用Linux常用命令,但是知道这些常用命令全称的人并不多,让我们来看看这些常用命令对应的全称吧!小编精心整理了一下,毕竟常用命令比较多,如果没有你常用的还望海涵,可以评论区补 ...

  2. linux中将文本中的单词换掉的指令_从零开始学Linux运维|19.文本处理相关命令(2)...

    1.tr tr用于替换文本文件中的字符,格式为"tr [要替换的字符] [替换后的字符]" tr不能用于替换完整字符串,只能替换对应的字符 将小写都替换成大写"cat t ...

  3. 如何判断LSTM模型中的过拟合和欠拟合 By 机器之心2017年10月02日 11:09 判断长短期记忆模型在序列预测问题上是否表现良好可能是一件困难的事。也许你会得到一个不错的模型技术得分,但了解

    判断长短期记忆模型在序列预测问题上是否表现良好可能是一件困难的事.也许你会得到一个不错的模型技术得分,但了解模型是较好的拟合,还是欠拟合/过拟合,以及模型在不同的配置条件下能否实现更好的性能是非常重要 ...

  4. 创建了一个表之后怎么再次在表中添加字段和字段显示位置_筛选数据透视表中的数据...

    [推荐阅读]资料下载QQ群:562718785(软件下载.精美PPT模板下载) [Excel视频教学]求和及快速求和 [Excel视频教学]跨表快速求和 [Excel视频教学]函数-相对绝对混合引用 ...

  5. java抽象类中的方法都是抽象方法吗_抽象方法必须在抽象类中吗

    抽象方法必须在抽象类中吗2020-07-01 15:01:18文/叶丹 1.抽象方法必须在抽象类中,所以抽象类中的方法都必须是抽象方法.2.Final类中的属性和方法都必须被final修饰符修饰.3. ...

  6. python pdfminer读取pdf表格_使用Python中的PDFMiner从PDF文件提取文本?

    富国沪深 DuckPuncher的出色回答,对于Python3,请确保您安装pdfminer2并执行以下操作:import iofrom pdfminer.pdfinterp import PDFRe ...

  7. python中可哈希是什么意思_实习小记-python中可哈希对象是个啥?what is hashable object in python?...

    废话不多说直接祭上python3.3x的文档:(原文链接) object.__hash__(self) Called by built-in function hash() and for opera ...

  8. Java中创建线程需要使用的类_如何通过使用Java中的匿名类创建线程?

    甲线程是可以同时与该程序的其他部分被执行的功能.所有Java程序都有至少一个称为主线程的线程,该线程由Java虚拟机(JVM)在程序启动时由主线程调用main()方法创建. 在Java中,我们可以通过 ...

  9. python函数定义中参数列表里的参数是_详解Python函数中参数带星号是什么意思

    函数的参数使用除了常规的位置参数和关键字参数外,还支持可变个数的函数参数,这种支持可变个数的参数方法称为参数收集,对应的参数称为收集参数. 一.参数收集的定义 Python的函数支持可变不定数量的参数 ...

最新文章

  1. mac系统下git、mysql、nginx、php的环境搭建
  2. linux下phpMyAdmin泛起1045 - Access denied for 的措置
  3. ubuntu 10.10开机自启动svn服务
  4. python 的几个内置函数(lambda ,zip, filter, map, reduce )用法
  5. 模块dll加载失败请确保该二进制_Windows漏洞利用开发 – 第3部分:偏移更改和重定位模块...
  6. 基金侧袋机制: 指引与操作规范
  7. umijs多环境配置_项目实战-UmiJS开发(附带qiankun)
  8. 2019海康威视内推
  9. 中e管家如何投资理财收益最大化
  10. 硬盘数据恢复的原理是什么?清空、格式化数据真的就没有了吗?
  11. C#框架设计之浅谈SOA与钝化模式
  12. vue使用JavaScript的Number方法或正则表达式进行表单验证,判断值是否为数字(包括整数和小数),验证值只能为小数点后一位
  13. InvalidSelectorException:Message: Locator Strategy 'css selector' is not supported for this session
  14. 登录QQ出现R6030-CRT not initialized
  15. linux登录mysql
  16. ROS实验笔记之——VINS-Mono在l515上的实现
  17. 菜鸟接触sora学编程点滴1
  18. Unity-URP 实现遮挡高亮
  19. c语言-重要的话说三遍
  20. 无人机服务器通信协议软件设计,无人机飞行管控系统及其数据处理软件设计

热门文章

  1. cuda合并访问的要求_在 CUDA C / C ++ 中使用共享内存
  2. PPIO 商业化架构解析
  3. 《Netty In Action》第二章:第一个Netty程序
  4. iOS沙盒路径及路径下数据的存储和读取
  5. mac svn的替代品CornerStone
  6. Aspose.Cells小实例
  7. IE7下元素的 'padding-top' 遇到 'clear' 特性在某些情况下复制到 'padding-bottom'
  8. LOCALALLOC和GLOBALLOC有什么区别? 全局内存和局部内存有什么区别?(转)
  9. [转] 一个OpenSource的评价图
  10. centos7升级自带的php5.4版本到php5.6