PDF识别

举报
Ragnar 发表于 2020/05/08 09:21:10 2020/05/08
【摘要】 相信大家和我一样也会经常遇到如下的情况:查找的资料是PDF格式的,无法批量处理其中的文字信息,PDF中的表格资料很难转换为方便下一步处理的格式(csv,excel,pd.dataframe)网上PDF转换工具通常是收费的,使用起来有所顾虑这里为大家介绍一种开源的解决方案……

PDF 文字&表格识别与转换

相信大家和我一样也会经常遇到如下的情况:

  • 查找的资料是PDF格式的,无法批量处理其中的文字信息

  • PDF中的表格资料很难转换为方便下一步处理的格式(csv,excel,pd.dataframe)

  • 网上PDF转换工具通常是收费的,使用起来有所顾虑

这里为大家介绍一种开源的解决方案,也是小编自己基于PDFminer,PyMuPDF这两个开源项目开发的项目。首先,这两个项目仅仅支持非扫描版的PDF文件,如果是扫描版的,属于OCR的范畴,不在此讨论。本文中一些背景和文档的翻译来自于自己的理解,如有不合适的地方,欢迎大家指正交流。

PDF 背景

最初的PDF(Portable Document Format) 是基于上世纪90年代很流行的PostScript page description language 开发的一种面向对象的“语言“,它提供了一种高效方便的机制使得文件在不同的硬件,操作系统,软件查看和打印(格式和内容的稳定)时内容和格式不会发生变化。最初的PDF设计发布于1993年,如今超过它已经成为了一种广泛的标准电子信息交换介质。

PDF基本格式

一个PDF文件包含了一系列的objects共同描述每一页的表象(appearance),它们是由自包含的一系列字节序列来表达的(self-contained sequence of bytes)。

PDF中的每一页都有可能由文字,图片组成。每一页在PDF创建时会生成一个content stream,它包含了一系列的graphics objects,而这些objects的表象(appearance)都被充分定义了。简单的来理解就是,我们所能看到的每一个信息都是由一个或者多个objects(如描述文字的stream object, 描述图片的image object等)来描述的。

PDFminer介绍

这个开源的项目最初是由一位日本老哥与2004年开始的,而他最近的一次更新是4个月前,虽然只是上来跟新了一下readme并声称“本项目不再维护了,目前正在维护并且支持python3.x的是pdfminer.six

那么这个GitHub 6K star的项目具体做了哪些工作呢?我最初打开GitHub看了30分钟源码,脑海里都是“小盆友你是否有很多问号??“。 是的,想要读懂这个项目,你首先要对那个1300多页的PDF reference manual有一定的了解。而这个著作我保证比这位日本老哥的10年大作更劝退。怎么办呢,好在网络上已经有一位高人替我们趟了坑,告诉我们这个pdfminer的项目应该怎么入手了,它的名字就是pdfminer-read the docs

这里引用一下这位作者对PDF的一番吐槽:

"""
PDF is evil. 
PDF是魔鬼。
Although it is called a PDF “document”, it’s nothing like Word or HTML document. 
尽管它被称为“文件”,但是它一点不像我们所熟悉的word文档或者HTML文档。
PDF is more like a graphic representation. 
它更像一种图形化的表达。
PDF contents are just a bunch of instructions that tell how to place the stuff at each exact position on a display or paper. 
PDF的内容只是一堆的指令集告诉(app或者打印机)如何去把东西(文字,图片)准确的放在显示屏或者纸的某个位置(坐标)上。
In most cases, it has no logical structure such as sentences or paragraphs and it cannot adapt itself when the paper size changes. 
很多时候,它是没有像句子或者段落这种逻辑结构的,而且它无法自适应纸张的变化。
PDFMiner attempts to reconstruct some of those structures by guessing from
its positioning, but there’s nothing guaranteed to work. Ugly, I know. Again, PDF is evil.
PDFMiner尝试依据坐标系统去重新组装这其中的一些指令集,但是没有什么是保证能够工作的。。丑陋,PDF是恶魔啊~~
"""

这一段其实讲出了我在面对不同格式,不同APP生成的PDF时候所面临的绝望,有时候参数的微微调整可以对使某些PDF的识别转换率飙升,然而却会直接导致其他的一些PDF中大量的objects丢失,只能通过不断的调整参数达到一种合适的转换效果。

在作者吐槽了PDF后他又介绍到

"""
Because a PDF file has such a big and complex structure, parsing a PDF file as a whole is time and memory consuming.
因为PDF文件有着如此巨大且复杂的结构,将一个PDF文件作为一个整体一次性解析是非常不划算的。
However, not every part is needed for most PDF processing tasks. Therefore PDFMiner takes a strategy of lazy parsing, which is to parse the stuff only when it’s necessary. 
但是呢,由于在PDF processing tasks中并非每个部分都是必要的。因此,PDFMiner尝试了一种lazy的策略,就是只在必要时解析内容(哈??)。
To parse PDF files, you need to use at least two classes:PDFParser and PDFDocument. 
解析PDF文件,至少需要PDFParser和PDFDocument这两个classes(好嘛,这就到class了)
These two objects are associated with each other. 
这两个objects是互相联系的(这我也猜到了)。
PDFParser fetches data from a file, and PDFDocument stores it. 
PDFParser从文件中获取数据,PDFDocument存储数据。(不然嘞...)
You’ll also need PDFPageInterpreter to process the page contents and
PDFDevice to translate it to whatever you need. 
你还需要PDFPageInterpreter去处理每页上的内容,以及PDFDevice去把他们转换为你需要的东西。(说好的2个classes呢)
PDFResourceManager is used to store shared resources such as fonts or images.
PDFResourceManager用来存储一些分享的资源,如字体和图片。(字体资源我能理解,怎么images也是分享的呢,这里存疑哈)
"""

具体的结构呢,如下图所示,可以说作者画的很明白了
image-20200507193450396.png

到此看上去非常明朗了, 我们来看一段示例代码

from pdfminer.layout import LAParamsfrom pdfminer.converter import PDFPageAggregatorfrom pdfminer.pdfpage import PDFPagefrom pdfminer.pdfinterp import PDFPageInterpreterfrom pdfminer.pdfinterp import PDFResourceManagerfrom pdfminer.layout import LTTextBoxHorizontal
document = open('myfile.pdf, 'rb')  #Create resource manager 资源管理器rsrcmgr = PDFResourceManager()  # Set parameters for analysis.laparams = LAParams()  # 这个class主要是一些参数,参数对单词的间距,上下行的间距,表格等线段的粗细做了一个定义# Create a PDF page aggregator object.device = PDFPageAggregator(rsrcmgr, laparams=laparams) # aggregator是device的一个子类,它主要是将单个的字母拼成单词甚至短语,而依据就是上一句定义的参数了,这句可以简单理解为对资源管理器中的资源以default参数进行聚合interpreter = PDFPageInterpreter(rsrcmgr, device) # 日本老大哥的脑洞太大了,花样太多了,你问我这个啥用,我也不清楚,总之是一种爱的串串的结构, 有理解的朋友欢迎告知,不过这里不太影响后面的处理for page in PDFPage.get_pages(document): # 小朋友,你是否和我一样很奇怪。作者的脑洞真的大,这里调用的page是PDFPage这个object
    interpreter.process_page(page)
    # receive the LTPage object for the page.
    layout = device.get_result()  # 这里很重要layout就是由interpreter解释出来的obj的集合
    for element in layout:
        if isinstance(element, LTTextBoxHorizontal)
        print(element.get_text())

这里给大家打个比方,PDFResourceManager是餐厅的大厨,它负责给你做饭,LAParams是用餐者的口味,咸淡啦要不要葱花放不放酱油啦,PDFPageAggregator这个是送餐的服务员,TA把大厨给你做的饭装在盘子里,但是呢大厨做了一百八十多道菜,总共3锅,服务员也不好一下子给你,他就负责把一道菜(每页的内容),按顺序(aggregate)给到你吃。PDFPageInterpreter呢就是餐具了,你用它来吃(procee_page)每道菜,这个菜呢最后就是服务员送给你的东西了(layout)。由此可见日本人的餐桌文化很严谨,每一个步骤都得到了尊重。当然我对这如此复杂的必要性产生了怀疑,但是这个不重要哈,可能是之前所说的lazy strategy的方略所必须付出的吧。

到了这一步,你应该能对着自己的屏幕上闪现着pdf文件中的文字而欢欣鼓舞了。但是呢这个只是我们长征的第一步,至于如何将文字拼成段落,将表格转为excel格式,提取图片,GUI手动选择并转化输出表格json,且听下回分解。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。