使用Python中的PDFMiner提取PDF文件中的文本?

119

我正在寻找有关如何使用Python的PDFMiner从PDF文件中提取文本的文档示例。

看起来PDFMiner更新了他们的API,所有相关的例子都包含过时的代码(类和方法已经改变)。我找到的那些使从PDF文件中提取文本任务更容易的库正在使用旧的PDFMiner语法,所以我不确定该怎么做。

目前,我只是查看源代码来看是否能够弄清楚它。


2
请查看http://stackoverflow.com/help/how-to-ask和http://stackoverflow.com/help/mcve,并更新您的答案,使其格式更好,并符合指南。 - Parker
你使用的是哪个版本的Python,2.7.x还是3.x.x?需要注意的是,作者明确说明PDFminer不支持Python 3.x.x。这可能是你遇到import错误的原因。如果是这样,你应该使用pdfminer3k,因为它是该库的Python 3导入版本。 - WGS
@Nanashi,就像我在原始问题中所说的那样,依赖于PDFMiner的库在导入完成之前会出现错误,以及我能找到的任何示例。这不是PDFMiner的问题。这是我在寻找文档或如何使用PDFMiner的示例。我能找到的所有内容都使用旧的PDFMiner语法。我已经编辑了我的问题以使其更清晰。我认为我让它比必要的更加混乱。对此我很抱歉。 - RattleyCooper
如果是这种情况,那么你会感到失望:文档非常稀少。GitHub 下载的离线文档甚至不到 100KB。此外,我认为 Google 用户组也不活跃。如果你愿意冒着文档相当不足的风险,这里是相关链接(http://www.unixuser.org/~euske/python/pdfminer/programming.html)。还有一个推荐的例子在这里(http://denis.papathanasiou.org/2010/08/04/extracting-text-images-from-pdf-files/)。 - WGS
可能是如何将pdfminer用作库的重复问题。 - tripleee
显示剩余4条评论
6个回答

213

这里是使用当前版本的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。我使用了Python库pdfminer.six,该库于2018年11月发布。


3
工作正常,但是,如何处理例如名称中的空格? 假设我有一个包含4列的PDF文件,在其中一列中有名和姓氏,现在它会被解析为一行中的名字和一行中的姓氏。 这是一个示例:http://docdro.id/rRyef3x - Deusdeorum
3
目前在运行这段代码时出现了导入错误:ImportError: No module named 'pdfminer.pdfpage' - Jeffrey Swan
2
谢谢,它可以在Python v2.7.12和Ubuntu 16.04上运行,不过最好使用UTF-8编码加载PDF文档,因为我的样本PDF存在一些编码问题,所以请在使用UTF-8编码后再尝试,这样可以解决问题... import sys reload(sys) sys.setdefaultencoding('utf-8') - sib10
3
@DuckPuncher,现在还有效吗?我不得不将file(path, 'rb')更改为open(path, 'rb')才能让我的工作。 - craned
3
仍然为Python3.7用户工作。安装了pdfminer.six==20181108软件包。对于我的情况,这是迄今为止最好的解决方案,我比较了许多解决方案。 - aze45sq6d
显示剩余10条评论

34

这个在2020年5月使用Python3中的PDFminer six工具包是可行的。

安装该工具包

$ 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 库从网络检索到了 PDF,可以使用 io 库将其转换为流:

import io

response = requests.get(url)
text = extract_text(io.BytesIO(response.content))

与PyPDF2相比的性能和可靠性

相比较PyPDF2(在处理特定类型的PDF时失败),PDFminer.six表现更加可靠,尤其是针对PDF版本1.7。

然而,PDFminer.six进行文本提取的速度比PyPDF2慢了6倍左右。

我在15寸MBP上使用timeit计时,对10页PDF文件进行文本提取,并仅计算提取函数的时间(不包括文件打开等时间),得到以下结果:

PDFminer.six: 2.88 sec
PyPDF2:       0.45 sec

pdfminer.six需要pycryptodome支持,而pycryptodome需要GCC和其他组件的安装,这使得在Alpine Linux上,即使是最小的安装docker镜像也从80 MB增加到了350 MB。PyPDF2没有显著的存储影响。

更新(2022年8月4日):根据Martin Thoma的说法,过去两年中PyPDF2有了很大的改进,因此也可以尝试使用它。这是他的基准测试结果


2
自从这个答案发布以来,PyPDF2已经有了很多改进。特别是文本提取方面得到了很大的改善。在我的基准测试中,PyPDF2的文本提取现在比pdfminer更好。 - Martin Thoma

33

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

2
它对我不起作用:ModuleNotFoundError: No module named 'pdfminer.pdfpage',我正在使用Python 3.6。 - Atti
@Atti,以防万一,请确保您已安装pdfminer2,因为还有另一个包pdfminer(我讨厌这个)。使用pip3 freeze时它适用于pdfminer2 == 20151206版本。 - juan Isaza
7
谢谢,我最终解决了问题,我从conda forge安装了pdfminer.six。 - Atti
10
对于Python 3,pdfminer.six是推荐的包 - https://github.com/pdfminer/pdfminer.six - Mike Driscoll
Python 3.7 报错 - ModuleNotFoundError: No module named 'chardet'。通过 pip install chardet 安装 chardet 可以解决此问题。 - Avnish Tiwary
显示剩余4条评论

29

全面披露,我是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())

类似的问题和答案在这里。我会尽力保持它们同步。


应该从import tools.pdf2模块而不是pdfminer.high_level导入pdf2text。 - undefined

2

这段代码已经经过 Python 3 版本的 PDFminer (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

我有一些PDF文件,可以使用Nitro Pro工具进行转换。但是,当我尝试使用此处发布的代码来转换同一个PDF文件时,输出表明存在权限错误。以下是输出内容:('from the SAGE Social Science Collections. All Rights Reserved.\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\x0c') - b00kgrrl
什么是文件流? - Vincent
@Vincent 使用 open(file,'rb') 打开文件流:[...] - Rodrigo Formighieri
你能将这个文件转换成表格/最好是Pandas格式吗?https://www.groupe-psa.com/en/publication/monthly-world-sales-march-2020/ - Je Je

1

我知道这是一个老问题。 对于任何试图使用pdfminer的人,你应该切换到pdfminer.six,它是当前维护的版本。


或者使用 PyPDF2 :-) - Martin Thoma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接