美文网首页
使用Python从PDF导出数据

使用Python从PDF导出数据

作者: 星月落 | 来源:发表于2019-08-14 20:43 被阅读0次

导言

很多时候,您需要从PDF中提取数据并使用Python以不同的格式将其导出。不幸的是,没有很多Python软件包可以很好地完成提取部分。在这篇文章中,我们将介绍可用于提取文本的各种不同包。我们还将学习如何从PDF中提取一些图像。虽然在Python中没有针对这些任务的完整解决方案,但您应该能够使用此处的信息开始。一旦我们提取了我们想要的数据,我们还将研究如何获取数据并以不同的格式导出数据。

让我们开始学习如何提取文本!

使用PDFMiner提取文本

可能最知名的是名为PDFMiner的软件包。自Python 2.4以来,PDFMiner包就已存在。其主要目的是从PDF中提取文本。实际上,PDFMiner可以告诉您页面上文本的确切位置以及有关字体的信息。对于Python 2.4-2.7,您可以参考以下网站以获取有关PDFMiner的更多信息:

GitHub - https://github.com/euske/pdfminer

PyPI - https://pypi.python.org/pypi/pdfminer/

网页 - https://euske.github.io/pdfminer/

PDFMiner与Python 3不兼容。幸运的是,有一个名为PDFMiner.six的PDFMiner分支完全相同。你可以在这里找到它:https://github.com/pdfminer/pdfminer.six

安装PDFMiner的说明充其量已过时。您实际上可以使用pip来安装它:

python -m pip install pdfminer

如果你想安装PDFMiner for Python 3(这应该是你应该做的),那么你必须像这样安装:

python -m pip install pdfminer.six

关于PDFMiner的文档充其量是相当差的。您很可能需要使用Google和Stack Overflow来有效地了解如何在本文所涵盖的内容之外有效地使用PDFMiner。

提取所有文本

有时您会想要提取PDF中的所有文本。PDFMiner包提供了几种不同的方法,您可以使用它们来执行此操作。我们将首先介绍一些编程方法。让我们尝试阅读美国国税局W9表格中的所有文本。你可以在这里获得一份副本:https://www.irs.gov/pub/irs-pdf/fw9.pdf

一旦您正确保存了PDF,我们就可以查看代码:

importio

frompdfminer.converterimportTextConverter

frompdfminer.pdfinterpimportPDFPageInterpreter

frompdfminer.pdfinterpimportPDFResourceManager

frompdfminer.pdfpageimportPDFPage

defextract_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)

withopen(pdf_path,'rb')asfh:

forpageinPDFPage.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()

iftext:

returntext

if__name__ =='__main__':

print(extract_text_from_pdf('w9.pdf'))

当您直接使用PDFMiner包时,它往往有点冗长。在这里,我们从PDFMiner的各个部分导入各种各样的部分。由于没有任何这些类的文档,也没有文档字符串,我不会解释它们的深入内容。如果你真的很好奇,可以自己深入挖掘源代码。但是,我认为我们可以跟随代码。

我们要做的第一件事是创建一个资源管理器实例。然后我们通过Python的io 模块创建一个类文件对象  。如果您使用的是Python 2,那么您将需要使用该StringIO 模块。我们的下一步是创建一个转换器。在这种情况下,我们选择TextConverter,但你也可以使用一个  HTMLConverter 或一个XMLConverter 你想要的。最后,我们创建一个PDF解释器对象,它将获取我们的资源管理器和转换器对象并提取文本。

最后一步是打开PDF并循环浏览每个页面。最后,我们抓取所有文本,关闭各种处理程序,然后打印出文本  stdout。

按页面提取文本

坦率地说,从多页文档中获取所有文本并不是那么有用。通常,您需要在文档的较小子集上进行操作。因此,让我们重写代码,以便逐页提取文本。这将允许我们一次检查一个页面的文本:

# miner_text_generator.py

importio

frompdfminer.converterimportTextConverter

frompdfminer.pdfinterpimportPDFPageInterpreter

frompdfminer.pdfinterpimportPDFResourceManager

frompdfminer.pdfpageimportPDFPage

defextract_text_by_page(pdf_path):

withopen(pdf_path,'rb')asfh:

forpageinPDFPage.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()

yieldtext

# close open handles

converter.close()

fake_file_handle.close()

defextract_text(pdf_path):

forpageinextract_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.pydumppdf.py为您进行导出。根据pdf2txt.py的源代码,它可用于将PDF导出为纯文本,HTML,XML或“标记”。

通过pdf2txt.py导出文本

PDFMiner附带的pdf2txt.py命令行工具将从PDF文件中提取文本并默认将其打印出来stdout 。它不会识别基于文本的图像,因为PDFMiner不支持光学字符识别(OCR)。让我们尝试使用它的最简单的方法,它只是将路径传递给PDF文件。我们将使用w9.pdf。打开终端并导航到已保存PDF的位置或修改下面的命令以指向该文件:

pdf2txt.py w9.pdf

如果你运行它,它将打印出所有文本  stdout。您还可以使pdf2txt.py将文本写入文本,HTML,XML或“标记的PDF”.XML格式将提供有关PDF的大部分信息,因为它包含文档中每个字母的位置以及作为字体信息。不建议使用HTML,因为生成的标记pdf2txt往往很难看。以下是如何获得不同的格式输出:

pdf2txt.py -o w9.html w9.pdf

pdf2txt.py -o w9.xml w9.pdf

第一个命令将创建一个HTML文档,而第二个命令将创建一个XML文档。 

最终的结果看起来有点偏,但也不算太糟糕。它输出的XML非常冗长,所以我不能在这里重现它。但是,这里有一个片段,可以让您了解它的外观:

F

o

r

m

W

-

9

用Slate提取文本

蒂姆·麦克纳马拉不喜欢使用PDFMiner的钝和难度,所以他写了一个名为slate的包装器,这样可以更容易地从PDF中提取文本。不幸的是,它似乎与Python 3兼容。如果你想尝试一下,你可能需要easy_install 安装  distribute 包,如下所示:

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.py

importslate

defextract_text_from_pdf(pdf_path):

withopen(pdf_path)asfh:

document = slate.PDF(fh, password='', just_text=1)

forpageindocument:

print(page)

if__name__ =='__main__':

extract_text_from_pdf('w9.pdf')

如您所见,要使平板解析PDF,您只需导入平板然后创建其PDF类的实例。PDF类实际上是Python 内置列表的子类,因此它只返回一个列表/可迭代的文本页面。您还会注意到,如果PDF设置了密码,我们可以传入密码参数。无论如何,一旦解析了文档,我们就会在每个页面上打印出文本。

我真的很喜欢使用石板更容易。不幸的是,几乎没有与此软件包相关的文档。查看源代码后,似乎所有这些包都支持文本提取。

导出数据

现在我们有一些文本可供使用,我们将花一些时间学习如何以各种不同的格式导出数据。具体来说,我们将学习如何通过以下方式导出文本:

XML

JSON

CSV

让我们开始吧!

导出到XML

可扩展标记语言(XML)格式是最着名的输出和输入格式之一。它在互联网上广泛用于许多不同的事物。正如我们在本文中已经看到的,PDFMiner还支持XML作为其输出之一。

但是,让我们创建自己的XML创建工具。这是一个简单的例子:

# xml_exporter.py

importos

importxml.etree.ElementTreeasxml

fromminer_text_generatorimportextract_text_by_page

fromxml.domimportminidom

defexport_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

forpageinextract_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='  ')

withopen(xml_path,'w')asfh:

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库,minidomElementTree。我们还导入了我们用于一次抓取一页文本的PDFMiner生成器脚本。在这个例子中,我们创建了顶级元素,它是PDF的文件名。然后我们在它下面添加一个Pages元素。下一步是我们循环,我们从PDF中提取每个页面并保存我们想要的信息。在这里,您可以添加一个特殊的解析器,您可以将页面拆分为句子或单词,并分析出更多有趣的信息。例如,您可能只想要具有特定名称或日期/时间戳的句子。您可以使用Python的正则表达式来查找这些类型的东西,或者只检查句子中是否存在子字符串。

对于此示例,我们只从每个页面中提取前100个字符并将它们保存到XML子元素中。从技术上讲,下一部分代码可以简化为只写出XML。但是,ElementTree对XML没有任何作用,使其易于阅读。它最终看起来像缩小的JavaScript,因为它只是一个巨大的文本块。因此,我们不是将该文本块写入磁盘,而是使用minidom在写出XML之前用空格“美化”XML。结果最终看起来像这样:

Form    W-9(Rev. November2017)Department of the Treasury  Internal Revenue Service RequestforTaxp

Form W-9(Rev.11-2017)Page2By signing the filled-out form, you:1.Certify that the TIN you are g

Form W-9(Rev.11-2017)Page3Criminal penaltyforfalsifying information. Willfully falsifying cert

Form W-9(Rev.11-2017)Page4The following chart shows types of payments that may be exemptfromba

Form W-9(Rev.11-2017)Page51.Interest, dividend,andbarter exchange accounts opened before1984

Form W-9(Rev.11-2017)Page6The IRS doesnotinitiate contactswithtaxpayers via emails. Also, th

这是非常干净的XML,也很容易阅读。对于奖励积分,您可以在PyPDF2部分中学习所学内容,并使用它从PDF中提取元数据并将其添加到XML中。

导出为JSON

JavaScript Object Notation或JSON是一种易于读写的轻量级数据交换格式。Pythonjson 在其标准库中包含一个模块,允许您以编程方式读取和写入JSON。让我们从上一节中学到的内容,并使用它来创建一个输出JSON而不是XML的导出器脚本:

# json_exporter.py

importjson

importos

fromminer_text_generatorimportextract_text_by_page

defexport_as_json(pdf_path, json_path):

filename = os.path.splitext(os.path.basename(pdf_path))[0]

data = {'Filename': filename}

data['Pages'] = []

counter =1

forpageinextract_text_by_page(pdf_path):

text = page[0:100]

page = {'Page_{}'.format(counter): text}

data['Pages'].append(page)

counter +=1

withopen(json_path,'w')asfh:

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的好处是Microsoft Excel和LibreOffice会自动在一个漂亮的电子表格中打开它们。如果您想查看原始值,还可以在文本编辑器中打开CSV文件。

Python有一个内置csv 模块,可用于读取和写入CSV文件。我们将在此处使用它从我们从PDF中提取的文本中创建CSV。我们来看看一些代码:

# csv_exporter.py

importcsv

importos

fromminer_text_generatorimportextract_text_by_page

defexport_as_csv(pdf_path, csv_path):

filename = os.path.splitext(os.path.basename(pdf_path))[0]

counter =1

withopen(csv_path,'w')ascsv_file:

writer = csv.writer(csv_file)

forpageinextract_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,Taxp

Form,W-9,(Rev.,11-2017)Page,2,By,signing,the,filled-out,"form,",you:,1.,Certify,that,the,TIN,you,are,g

Form,W-9,(Rev.,11-2017)Page,3,Criminal,penalty,for,falsifying,information.,Willfully,falsifying,cert

Form,W-9,(Rev.,11-2017)Page,4,The,following,chart,shows,types,of,payments,that,may,be,exempt,from,ba

Form,W-9,(Rev.,11-2017)Page,5,1.,"Interest,","dividend,",and,barter,exchange,accounts,opened,before,1984

Form,W-9,(Rev.,11-2017)Page,6,The,IRS,does,not,initiate,contacts,with,taxpayers,via,emails.,"Also,",th

我认为这个比JSON或XML示例更难阅读,但它并不太糟糕。现在让我们继续看看我们如何从PDF中提取图像。

从PDF中提取图像

不幸的是,没有Python包实际上从PDF中提取图像。我发现最接近的是一个名为minecart的项目,声称能够做到这一点,但只适用于Python 2.7。我无法使用我的样本PDF。有一篇关于Ned Batchelder 博客的文章,讲述了他如何能够从PDF中提取JPG。他的代码如下:

# Extract jpg's from pdf's. Quick and dirty.

importsys

pdf = file(sys.argv[1],"rb").read()

startmark ="ÿØ"

startfix =0

endmark ="ÿÙ"

endfix =2

i =0

njpg =0

whileTrue:

istream = pdf.find("stream", i)

ifistream <0:

break

istart = pdf.find(startmark, istream, istream+20)

ifistart <0:

i = istream+20

continue

iend = pdf.find("endstream", istart)

ifiend <0:

raiseException("Didn't find end of stream!")

iend = pdf.find(endmark, iend-20)

ifiend <0:

raiseException("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.py

importos

importsubprocess

defimage_exporter(pdf_path, output_dir):

ifnotos.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 ,虽然我无法找到任何进行图像提取的包的示例。

结语

我们在这篇文章中介绍了很多不同的信息。我们了解了几个不同的包,我们可以用它们从PDF中提取文本,如PDFMiner或Slate。我们还学习了如何使用Python的内置库将文本导出为XML,JSON和CSV。最后,我们研究了从PDF导出图像的难题。虽然Python目前没有任何适合此任务的好库,但您可以使用其他工具(如Poppler的  pdfimage 实用程序)解决此问题。

源网络,版权归原创者所有。如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。


更多技术,欢迎关注下方公众号

相关文章

网友评论

      本文标题:使用Python从PDF导出数据

      本文链接:https://www.haomeiwen.com/subject/jmznjctx.html