PDF解析模块-PDFMiner开发手册[翻译]
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PDF解析模块-PDFMiner开发手册[翻译]相关的知识,希望对你有一定的参考价值。
参考技术A 转 https://blog.csdn.net/robolinux/article/details/43318229原文地址: http://euske.github.io/pdfminer/programming.html
软件版本:pdfminer-20140328
翻译:robolinux
时间:20150110
PDF格式不是规范格式. 尽管它被叫做"PDF文档", 但并不像word或者html文档。PDF的表现更像一张图片。PDF更像是在一张纸的各个准确的位置上把内容都摆放出来。大部分情况下,没有逻辑结构,比如句子或段落,并且不能自适应页面大小的调整。PDFMiner尝试通过猜测它们的布局来重建它们的结构,但是不保证一定能工作。我知道这样很难看,但是,PDF确实不够规范。
更多关于PDF内部结构的技术详情,请见《如何手工提取PDF内容》。
http://www.youtube.com/watch?v=k34wRxaxA_c http://www.youtube.com/watch?v=_A1M4OdNsiQ http://www.youtube.com/watch?v=sfV_7cWPgZE
由于PDF文件有如此大和复杂的结构,完整解析PDF文件很费时费力。好吧,大多数PDF工作中,很多模块是不需要加进来的。因此 PDFMiner 采用了一个懒惰分析的策略,就是只分析所需要的部分。解析时候,至少需要2个核心类,PDFParser 和 PDFDocument。这两个模块配合其他模块来使用。
PDFParser 从文件中获取数据
PDFDocument 存储文档数据结构到内存中
PDFPageInterpreter 解析page内容
PDFDevice 把解析到的内容转化为你需要的东西
PDFResourceManager存储共享资源,例如字体或图片
下图显示了PDFMiner中各个类之间的关系。
下面是解析pdf的一个典型方法:
布局分析把pdf文档中每一页返回为一个 LTPage 对象. 该对象包含该页面中的子对象,格式化为树形结构。
下图显示了这些对象之间的关系。
LTPage
代表一个完整的页面。可以包含子对象,例如LTTextBox,LTFigure,LTImage,LTRect,LTCurve和LTLine.
LTTextBox
它包含 LTTextLine 对象的列表
代表一组被包含在矩形区域中的文本
需要注意的是,该box是根据几何学分析得到的,并不一定准确地表现为该文本的逻辑范围
get_text()方法可以返回文本内容
LTTextLine
包含一个LTChar对象的列表,表现为单行文本
字符表现为一行或一列,取决于文本书写方式
get_text()方法返回文本内容
LTChar / LTAnno
代表一个在文本中的真实的字母,作为一个unicode字符串
LTChar 对象有真实的分隔符
LTAnno 对象没有,是虚拟分隔符,按照两个字符之间的关系,布局分析器插入虚拟分隔符
LTFigure
代表一个被PDF Form对象使用的区域
pdf form适用于目前的图表(present figures)或者页面中植入的另一个pdf文档图片。LTFigure对象可以递归
LTImage
代表一个图形对象。可以是JPEG或者其他格式,但PDFMiner目前没有花太多精力在图形对象上。
LTLine
代表一根直线。用来分割文本或图表(figures)。
LTRect
代表一个矩形。
用来框住别的图片或者图表。
LTCurve
代表一个贝塞尔曲线。
也可以从下面URL获得更多完整的示例。
http://denis.papathanasiou.org/?p=343
获得目录
PDF文档没有目录时会报:
raise PDFNoOutlines
pdfminer.pdfdocument.PDFNoOutlines
一些pdf文档使用页号作为目录指向,另外的文档则使用页号和页面中的物理位置。由于pdf文档没有逻辑结构,并且不支持从外部指向页内对象,所以没有办法准确告知这些目录指向文本的哪一部分。
你可以扩展PDFPageinterpreter类和PDFDevice类以便进行不同的加工,或者获得其他信息。
在python中使用PDFMiner从PDF文件中提取文本?
【中文标题】在python中使用PDFMiner从PDF文件中提取文本?【英文标题】:Extracting text from a PDF file using PDFMiner in python? 【发布时间】:2014-12-17 02:54:44 【问题描述】:我正在寻找有关如何使用 PDFMiner 和 Python 从 PDF 文件中提取文本的文档或示例。
看起来 PDFMiner 更新了他们的 API,我发现的所有相关示例都包含过时的代码(类和方法已更改)。我发现使从 PDF 文件中提取文本的任务更容易的库正在使用旧的 PDFMiner 语法,所以我不知道该怎么做。
事实上,我只是在查看源代码,看看我是否能弄清楚。
【问题讨论】:
请查看 ***.com/help/how-to-ask 和 ***.com/help/mcve 并更新您的答案,使其格式更好并符合指南。 您使用的是哪个 Python 发行版,2.7.x 还是 3.x.x?需要注意的是,作者明确详细说明了PDFminer
不适用于 Python 3.x.x。这可能是您收到import
错误的原因。如果是这样,您应该使用pdfminer3k
,因为它是该库的常规 Python 3 导入。
@Nanashi,对不起,我忘了添加我的 Python 版本。这是2.7,所以这不是问题。我一直在查看源代码,看起来他们重组了一些东西,这就是导入中断的原因。我也找不到 PDFMiner 的任何文档,或者我只是在解决这个问题:(
我刚刚从 GitHub 安装了PDFminer
,它可以正常导入。能否请您发布您的代码并发布您的完整错误回溯?
@Nanashi,就像我在最初的问题中所说的那样,依赖 PDFMiner 的库在完成导入之前会中断以及我能找到的任何示例。这不是 PDFMiner 问题。这是我在寻找文档,或如何使用 PDFMiner 的示例。我能找到的一切都是使用 PDFMiner 的旧语法。为了清楚起见,我继续编辑了我的问题。我想我让它变得比它需要的更混乱。对此感到抱歉。
【参考方案1】:
这是一个使用当前版本的 PDFMiner(2016 年 9 月)从 PDF 文件中提取文本的工作示例
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO
def convert_pdf_to_txt(path):
rsrcmgr = PDFResourceManager()
retstr = StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
fp = open(path, 'rb')
interpreter = PDFPageInterpreter(rsrcmgr, device)
password = ""
maxpages = 0
caching = True
pagenos=set()
for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
interpreter.process_page(page)
text = retstr.getvalue()
fp.close()
device.close()
retstr.close()
return text
PDFMiner 的结构最近发生了变化,因此这应该适用于从 PDF 文件中提取文本。
编辑:截至 2018 年 6 月 7 日仍在工作。在 Python 版本 3.x 中验证
编辑:该解决方案适用于 2019 年 10 月 3 日的 Python 3.7。我使用了 2018 年 11 月发布的 Python 库 pdfminer.six
。
【讨论】:
工作正常,但是,如何处理例如名称中的空格?假设我有一个包含 4 列的 pdf,其中我在一个列中有名字和姓氏,现在它被解析为一行中的名字和一行中的姓氏,这是一个示例 docdro.id/rRyef3x 当前使用此代码出现导入错误:ImportError: No module named 'pdfminer.pdfpage' 感谢它在 python v2.7.12 和 ubuntu 16.04 上工作,但最好加载编码为 utf-8 的 pdf 文档,因为我的示例 pdf 有一些编码问题,所以在编码后试试这个使用 utf-8 即可解决问题...import sys reload(sys) sys.setdefaultencoding('utf-8')
@DuckPuncher,它现在还在工作吗?我必须将 file(path, 'rb')
更改为 `open(path, 'rb') 才能让我的工作。
仍然为 Python3.7 用户工作。安装 pdfminer.six==20181108 包。迄今为止我的案例的最佳解决方案,我比较了许多解决方案。【参考方案2】:
来自 DuckPuncher 的出色回答,对于 Python3,请确保安装 pdfminer2 并执行以下操作:
import io
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
def convert_pdf_to_txt(path):
rsrcmgr = PDFResourceManager()
retstr = io.StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
fp = open(path, 'rb')
interpreter = PDFPageInterpreter(rsrcmgr, device)
password = ""
maxpages = 0
caching = True
pagenos = set()
for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages,
password=password,
caching=caching,
check_extractable=True):
interpreter.process_page(page)
fp.close()
device.close()
text = retstr.getvalue()
retstr.close()
return text
【讨论】:
它对我不起作用:ModuleNotFoundError: No module named 'pdfminer.pdfpage' 我正在使用 python 3.6 @Atti,以防万一,请确保您已安装 pdfminer2,因为还有另一个软件包 pdfminer(我讨厌这个)。在进行 pip3 freeze 时,它适用于 pdfminer2==20151206 版本。 感谢我终于成功了,我从 conda forge 安装了 pdfminer.six 对于 Python 3,推荐使用 pdfminer.six 包 - github.com/pdfminer/pdfminer.six 这仍然是最新的。我收到了同样的ImportError:
消息【参考方案3】:
此代码使用 pdfminer for python 3 (pdfminer-20191125) 测试
from pdfminer.layout import LAParams
from pdfminer.converter import PDFPageAggregator
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.layout import LTTextBoxHorizontal
def parsedocument(document):
# convert all horizontal text into a lines list (one entry per line)
# document is a file stream
lines = []
rsrcmgr = PDFResourceManager()
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)
for page in PDFPage.get_pages(document):
interpreter.process_page(page)
layout = device.get_result()
for element in layout:
if isinstance(element, LTTextBoxHorizontal):
lines.extend(element.get_text().splitlines())
return lines
【讨论】:
我有可以使用 Nitro Pro 工具转换的 PDF 文件。但是,当我尝试使用此处发布的代码转换相同的 PDF 时,我得到的输出表明存在权限错误。这是输出: ('来自 SAGE 社会科学合集。保留所有权利。\n\n\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c \x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c') 文件流是什么意思? @Vincent 以 open(file,'rb') 作为流:[...] 您是否设法将此文件作为表格/熊猫理想地获得? groupe-psa.com/en/publication/monthly-world-sales-march-2020【参考方案4】:完全披露,我是pdfminer.six 的维护者之一。它是用于 python 3 的 pdfminer 的社区维护版本。
现在,它有多个 api 用于从 PDF 中提取文本,具体取决于您的需要。在幕后,所有这些 api 都使用相同的逻辑来解析和分析布局。
(所有示例都假定您的 PDF 文件名为 example.pdf)
命令行
如果您只想提取文本一次,可以使用命令行工具 pdf2txt.py:
$ pdf2txt.py example.pdf
高级 API
如果你想用 Python 提取文本(属性),你可以使用高级 api。如果您想以编程方式从 PDF 中提取信息,这种方法是首选解决方案。
from pdfminer.high_level import extract_text
# Extract text from a pdf.
text = extract_text('example.pdf')
# Extract iterable of LTPage objects.
pages = extract_pages('example.pdf')
可组合的 api
还有一个可组合的 api,它在处理生成的对象时提供了很大的灵活性。例如,它允许您创建自己的布局算法。其他答案中建议使用此方法,但仅当您需要自定义某些组件时,我才会推荐此方法。
from io import StringIO
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser
output_string = StringIO()
with open('example.pdf', 'rb') as in_file:
parser = PDFParser(in_file)
doc = PDFDocument(parser)
rsrcmgr = PDFResourceManager()
device = TextConverter(rsrcmgr, output_string, laparams=LAParams())
interpreter = PDFPageInterpreter(rsrcmgr, device)
for page in PDFPage.create_pages(doc):
interpreter.process_page(page)
print(output_string.getvalue())
类似的问答here。我会尽量让它们保持同步。
【讨论】:
【参考方案5】:这在 2020 年 5 月使用 Python3 中的 PDFminer 6 工作。
安装包
$ pip install pdfminer.six
导入包
from pdfminer.high_level import extract_text
使用保存在磁盘上的 PDF
text = extract_text('report.pdf')
或者:
with open('report.pdf','rb') as f:
text = extract_text(f)
使用内存中已有的 PDF
如果 PDF 已在内存中,例如,如果使用 requests 库从 Web 检索,则可以使用 io
库将其转换为流:
import io
response = requests.get(url)
text = extract_text(io.BytesIO(response.content))
与 PyPDF2 相比的性能和可靠性
PDFminer.six 比 PyPDF2(某些类型的 PDF 失败)更可靠,尤其是 PDF 版本 1.7
但是,使用 PDFminer.six 提取文本的速度比 PyPDF2 慢 6 倍。
我在 15" MBP (2018) 上使用 timeit
定时提取文本,仅使用 10 页 PDF 定时提取功能(无文件打开等),并得到以下结果:
PDFminer.six: 2.88 sec
PyPDF2: 0.45 sec
pdfminer.six 占用空间也很大,需要 pycryptodome,它需要安装 GCC 和其他东西,将 Alpine Linux 上的最小安装 docker 映像从 80 MB 提升到 350 MB。 PyPDF2 没有明显的存储影响。
【讨论】:
以上是关于PDF解析模块-PDFMiner开发手册[翻译]的主要内容,如果未能解决你的问题,请参考以下文章
Python:解析PDF文本及表格——pdfminertabulapdfplumber 的用法及对比