赞
踩
导言
使用PDFMiner提取文本
python -m pip install pdfminer
python -m pip install pdfminer.six
提取所有文本
import iofrom pdfminer.converter import TextConverterfrom pdfminer.pdfinterp import PDFPageInterpreterfrom pdfminer.pdfinterp import PDFResourceManagerfrom pdfminer.pdfpage import PDFPagedef extract_text_from_pdf(pdf_path): resource_manager = PDFResourceManager() fake_file_handle = io.StringIO() converter = TextConverter(resource_manager, fake_file_handle) page_interpreter = PDFPageInterpreter(resource_manager, converter) with open(pdf_path, 'rb') as fh: for page in PDFPage.get_pages(fh, caching=True, check_extractable=True): page_interpreter.process_page(page) text = fake_file_handle.getvalue() # close open handles converter.close() fake_file_handle.close() if text: return textif __name__ == '__main__': print(extract_text_from_pdf('w9.pdf'))
io
模块创建一个类文件对象 。如果您使用的是Python 2,那么您将需要使用该
StringIO
模块。我们的下一步是创建一个转换器。在这种情况下,我们选择
TextConverter
,但你也可以使用一个
HTMLConverter
或一个
XMLConverter
你想要的。最后,我们创建一个PDF解释器对象,它将获取我们的资源管理器和转换器对象并提取文本。
最后一步是打开PDF并循环浏览每个页面。最后,我们抓取所有文本,关闭各种处理程序,然后打印出文本
stdout
。
按页面提取文本
# miner_text_generator.pyimport iofrom pdfminer.converter import TextConverterfrom pdfminer.pdfinterp import PDFPageInterpreterfrom pdfminer.pdfinterp import PDFResourceManagerfrom pdfminer.pdfpage import PDFPagedef extract_text_by_page(pdf_path): with open(pdf_path, 'rb') as fh: for page in PDFPage.get_pages(fh, caching=True, check_extractable=True): resource_manager = PDFResourceManager() fake_file_handle = io.StringIO() converter = TextConverter(resource_manager, fake_file_handle) page_interpreter = PDFPageInterpreter(resource_manager, converter) page_interpreter.process_page(page) text = fake_file_handle.getvalue() yield text # close open handles converter.close() fake_file_handle.close()def extract_text(pdf_path): for page in extract_text_by_page(pdf_path): print(page) print()if __name__ == '__main__': print(extract_text('w9.pdf'))
extract_text
功能打印出每页的文本。这是我们可以添加一些解析逻辑来解析我们想要的东西的地方。或者我们可以将文本(或HTML或XML)保存为单独的文件以供将来解析。
您会注意到文本可能不符合您的预期。因此,您肯定需要找出解析您感兴趣的文本的最佳方法。
PDFMiner的优点在于您已经可以将PDF“导出”为文本,HTML或XML。
如果您不想自己想出PDFMiner,也可以使用PDFMiner的命令行工具pdf2txt.py和dumppdf.py为您进行导出。根据pdf2txt.py的源代码,它可用于将PDF导出为纯文本,HTML,XML或“标记”。
通过pdf2txt.py导出文本
stdout
。它不会识别基于文本的图像,因为PDFMiner不支持光学字符识别(OCR)。让我们尝试使用它的最简单的方法,它只是将路径传递给PDF文件。我们将使用w9.pdf。打开终端并导航到已保存PDF的位置或修改下面的命令以指向该文件:
pdf2txt.py w9.pdf
pdf2txt.py -o w9.html w9.pdf pdf2txt.py -o w9.xml w9.pdf
<pages><page id="1" bbox="0.000,0.000,611.976,791.968" rotate="0"><textbox id="0" bbox="36.000,732.312,100.106,761.160"><textline bbox="36.000,732.312,100.106,761.160"><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="36.000,736.334,40.018,744.496" size="8.162">F</text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="40.018,736.334,44.036,744.496" size="8.162">o</text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="44.036,736.334,46.367,744.496" size="8.162">r</text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="46.367,736.334,52.338,744.496" size="8.162">m</text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="52.338,736.334,54.284,744.496" size="8.162"> </text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="54.284,736.334,56.230,744.496" size="8.162"> </text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="56.230,736.334,58.176,744.496" size="8.162"> </text><text font="JYMPLA+HelveticaNeueLTStd-Roman" bbox="58.176,736.334,60.122,744.496" size="8.162"> </text><text font="ZWOHBU+HelveticaNeueLTStd-BlkCn" bbox="60.122,732.312,78.794,761.160" size="28.848">W</text><text font="ZWOHBU+HelveticaNeueLTStd-BlkCn" bbox="78.794,732.312,87.626,761.160" size="28.848">-</text><text font="ZWOHBU+HelveticaNeueLTStd-BlkCn" bbox="87.626,732.312,100.106,761.160" size="28.848">9</text><text></text></textline>
用Slate提取文本
easy_install distribute
我无法正确安装该软件包。一旦安装完毕,您就可以使用pip来安装slate了:
python -m pip install slate
请注意,最新版本为0.5.2,pip可能会也可能不会获取该版本。如果没有,那么你可以直接从GitHub安装slate:
python -m pip install git+https://github.com/timClicks/slate
现在我们准备编写一些代码来从PDF中提取文本:# slate_text_extraction.pyimport slatedef extract_text_from_pdf(pdf_path): with open(pdf_path) as fh: document = slate.PDF(fh, password='', just_text=1) for page in document: print(page)if __name__ == '__main__': extract_text_from_pdf('w9.pdf')
如您所见,要使平板解析PDF,您只需导入平板然后创建其
PDF
类的实例。PDF类实际上是Python 内置
列表
的子类,因此它只返回一个列表/可迭代的文本页面。您还会注意到,如果PDF设置了密码,我们可以传入密码参数。无论如何,一旦解析了文档,我们就会在每个页面上打印出文本。
我真的很喜欢使用石板更容易。不幸的是,几乎没有与此软件包相关的文档。查看源代码后,似乎所有这些包都支持文本提取。
导出数据
导出到XML
# xml_exporter.pyimport osimport xml.etree.ElementTree as xmlfrom miner_text_generator import extract_text_by_pagefrom xml.dom import minidomdef export_as_xml(pdf_path, xml_path): filename = os.path.splitext(os.path.basename(pdf_path))[0] root = xml.Element('{filename}'.format(filename=filename)) pages = xml.Element('Pages') root.append(pages) counter = 1 for page in extract_text_by_page(pdf_path): text = xml.SubElement(pages, 'Page_{}'.format(counter)) text.text = page[0:100] counter += 1 tree = xml.ElementTree(root) xml_string = xml.tostring(root, 'utf-8') parsed_string = minidom.parseString(xml_string) pretty_string = parsed_string.toprettyxml(indent=' ') with open(xml_path, 'w') as fh: fh.write(pretty_string) #tree.write(xml_path)if __name__ == '__main__': pdf_path = 'w9.pdf' xml_path = 'w9.xml' export_as_xml(pdf_path, xml_path)
该脚本将使用Python的内置XML库,
minidom
和
ElementTree
。我们还导入了我们用于一次抓取一页文本的PDFMiner生成器脚本。在这个例子中,我们创建了顶级元素,它是PDF的文件名。然后我们在它下面添加一个
Pages
元素。下一步是我们
的
循环,我们从PDF中提取每个页面并保存我们想要的信息。在这里,您可以添加一个特殊的解析器,您可以将页面拆分为句子或单词,并分析出更多有趣的信息。例如,您可能只想要具有特定名称或日期/时间戳的句子。您可以使用Python的正则表达式来查找这些类型的东西,或者只检查句子中是否存在子字符串。
对于此示例,我们只从每个页面中提取前100个字符并将它们保存到XML子元素中。从技术上讲,下一部分代码可以简化为只写出XML。但是,ElementTree对XML没有任何作用,使其易于阅读。它最终看起来像缩小的JavaScript,因为它只是一个巨大的文本块。因此,我们不是将该文本块写入磁盘,而是使用minidom在写出XML之前用空格“美化”XML。结果最终看起来像这样:
<?xml version="1.0" ?><w9> <Pages> <Page_1>Form W-9(Rev. November 2017)Department of the Treasury Internal Revenue Service Request for Taxp</Page_1> <Page_2>Form W-9 (Rev. 11-2017)Page 2 By signing the filled-out form, you: 1. Certify that the TIN you are g</Page_2> <Page_3>Form W-9 (Rev. 11-2017)Page 3 Criminal penalty for falsifying information. Willfully falsifying cert</Page_3> <Page_4>Form W-9 (Rev. 11-2017)Page 4 The following chart shows types of payments that may be exempt from ba</Page_4> <Page_5>Form W-9 (Rev. 11-2017)Page 5 1. Interest, dividend, and barter exchange accounts opened before 1984</Page_5> <Page_6>Form W-9 (Rev. 11-2017)Page 6 The IRS does not initiate contacts with taxpayers via emails. Also, th</Page_6> </Pages></w9>
这是非常干净的XML,也很容易阅读。对于奖励积分,您可以在PyPDF2部分中学习所学内容,并使用它从PDF中提取元数据并将其添加到XML中。
导出为JSON
json
在其标准库中包含一个模块,允许您以编程方式读取和写入JSON。让我们从上一节中学到的内容,并使用它来创建一个输出JSON而不是XML的导出器脚本:
# json_exporter.pyimport jsonimport osfrom miner_text_generator import extract_text_by_pagedef export_as_json(pdf_path, json_path): filename = os.path.splitext(os.path.basename(pdf_path))[0] data = {'Filename': filename} data['Pages'] = [] counter = 1 for page in extract_text_by_page(pdf_path): text = page[0:100] page = {'Page_{}'.format(counter): text} data['Pages'].append(page) counter += 1 with open(json_path, 'w') as fh: json.dump(data, fh)if __name__ == '__main__': pdf_path = 'w9.pdf' json_path = 'w9.json' export_as_json(pdf_path, json_path)
在这里,我们导入了我们需要的各种库,包括我们的PDFMiner模块。然后我们创建一个接受PDF输入路径和JSON输出路径的函数。JSON基本上是Python中的一个字典,因此我们创建了几个简单的顶级键:
Filename
和
Pages
。该
Pages
键映射到一个空列表。接下来,我们遍历PDF的每个页面并提取每个页面的前100个字符。然后我们创建一个字典,其中页码作为键,100个字符作为值,并将其附加到顶级页面列表。最后,我们使用
json
模块的
dump
命令编写文件。
该文件的内容最终看起来像这样:
{'Filename': 'w9', 'Pages': [{'Page_1': 'Form W-9(Rev. November 2017)Department of the Treasury Internal Revenue Service Request for Taxp'}, {'Page_2': 'Form W-9 (Rev. 11-2017)Page 2 By signing the filled-out form, you: 1. Certify that the TIN you are g'}, {'Page_3': 'Form W-9 (Rev. 11-2017)Page 3 Criminal penalty for falsifying information. Willfully falsifying cert'}, {'Page_4': 'Form W-9 (Rev. 11-2017)Page 4 The following chart shows types of payments that may be exempt from ba'}, {'Page_5': 'Form W-9 (Rev. 11-2017)Page 5 1. Interest, dividend, and barter exchange accounts opened before 1984'}, {'Page_6': 'Form W-9 (Rev. 11-2017)Page 6 The IRS does not initiate contacts with taxpayers via emails. Also, th'}]}
再一次,我们有一些很容易阅读的好输出。如果您愿意,也可以使用PDF的元数据来增强此示例。请注意,输出将根据您要解析每个页面或文档的内容而更改。
现在让我们快速浏览一下如何导出到CSV。
导出为CSV
csv
模块,可用于读取和写入CSV文件。我们将在此处使用它从我们从PDF中提取的文本中创建CSV。我们来看看一些代码:
# csv_exporter.pyimport csvimport osfrom miner_text_generator import extract_text_by_pagedef export_as_csv(pdf_path, csv_path): filename = os.path.splitext(os.path.basename(pdf_path))[0] counter = 1 with open(csv_path, 'w') as csv_file: writer = csv.writer(csv_file) for page in extract_text_by_page(pdf_path): text = page[0:100] words = text.split() writer.writerow(words)if __name__ == '__main__': pdf_path = 'w9.pdf' csv_path = 'w9.csv' export_as_csv(pdf_path, csv_path)
在本例中,我们导入Python的
csv
库。否则,导入与前一个示例相同。在我们的函数中,我们使用CSV文件路径创建CSV文件处理程序。然后我们初始化一个CSV编写器对象,该文件处理程序作为其唯一参数。接下来,我们像以前一样循环遍历PDF的页面。这里唯一的区别是我们将前100个字符分成单个单词。这允许我们将一些实际数据添加到CSV中。如果我们不这样做,那么每一行中只有一个元素,那时它实际上不是一个CSV文件。最后,我们将单词列表写入CSV文件。
这是我得到的结果:
Form,W-9(Rev.,November,2017)Department,of,the,Treasury,Internal,Revenue,Service,Request,for,TaxpForm,W-9,(Rev.,11-2017)Page,2,By,signing,the,filled-out,"form,",you:,1.,Certify,that,the,TIN,you,are,gForm,W-9,(Rev.,11-2017)Page,3,Criminal,penalty,for,falsifying,information.,Willfully,falsifying,certForm,W-9,(Rev.,11-2017)Page,4,The,following,chart,shows,types,of,payments,that,may,be,exempt,from,baForm,W-9,(Rev.,11-2017)Page,5,1.,"Interest,","dividend,",and,barter,exchange,accounts,opened,before,1984Form,W-9,(Rev.,11-2017)Page,6,The,IRS,does,not,initiate,contacts,with,taxpayers,via,emails.,"Also,",th
我认为这个比JSON或XML示例更难阅读,但它并不太糟糕。现在让我们继续看看我们如何从PDF中提取图像。
从PDF中提取图像
# Extract jpg's from pdf's. Quick and dirty.import syspdf = file(sys.argv[1], "rb").read()startmark = "ÿØ"startfix = 0endmark = "ÿÙ"endfix = 2i = 0njpg = 0while True: istream = pdf.find("stream", i) if istream < 0: break istart = pdf.find(startmark, istream, istream+20) if istart < 0: i = istream+20 continue iend = pdf.find("endstream", istart) if iend < 0: raise Exception("Didn't find end of stream!") iend = pdf.find(endmark, iend-20) if iend < 0: raise Exception("Didn't find end of JPG!") istart += startfix iend += endfix print("JPG %d from %d to %d" % (njpg, istart, iend)) jpg = pdf[istart:iend] jpgfile = file("jpg%d.jpg" % njpg, "wb") jpgfile.write(jpg) jpgfile.close() njpg += 1 i = iend
这对我正在使用的PDF也不起作用。评论中有些人声称它适用于某些PDF,并且评论中也有一些更新代码的例子。Stack Overflow上有这些代码的变体,其中一些以某种方式使用PyPDF2。这些都不适合我。
我的建议是使用像Poppler这样的工具来提取图像。Poppler有一个名为pdfimages的工具,可以与Python的子进程模块一起使用。以下是如何在没有Python的情况下使用它:
pdfimages -all reportlab-sample.pdf images/prefix-jpg
确保
images
已创建文件夹(或您要创建的任何输出文件夹),因为
pdfimages
它不会为您创建。
让我们编写一个也执行此命令的Python脚本,并确保输出文件夹也存在:
# image_exporter.pyimport osimport subprocessdef image_exporter(pdf_path, output_dir): if not os.path.exists(output_dir): os.makedirs(output_dir) cmd = ['pdfimages', '-all', pdf_path, '{}/prefix'.format(output_dir)] subprocess.call(cmd) print('Images extracted:') print(os.listdir(output_dir))if __name__ == '__main__': pdf_path = 'reportlab-sample.pdf' image_exporter(pdf_path, output_dir='images')
在此示例中,我们导入
subprocess
和
os
模块。如果输出目录不存在,我们尝试创建它。我们使用subprocess的
call
方法来执行
pdfimages
。我们使用
call
因为它会等待
pdfimages
完成运行。您可以使用
Popen
,但这基本上将在后台运行该过程。最后,我们打印出输出目录的列表,以确认图像是否被提取到它。
互联网上还有一些其他文章引用了一个名为Wand的库,你可能也想尝试一下。它是一个ImageMagick包装器。还值得注意的是,有一个名为pypoppler的Python绑定到Poppler ,虽然我无法找到任何进行图像提取的包的示例。
结语
pdfimage
实用程序)解决此问题。
长按订阅更多精彩▼
如有收获,点个在看,诚挚感谢
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。