以下是教你如何生成不同格式输出的方法: pdf2txt.py -o w9.html w9.pdf pdf2txt.py -o w9.xml w9.pdf 第一条命令将创建一个HTML文件,而第二条将创建一个XML文件。 最终的结果看上去有点怪,但是它并不太糟糕。XML格式的输出极其冗长,因此我不能将它完整地在这里重现,以下是一小段示例: < 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 font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "40.018,736.334,44.036,744.496" size = "8.162" > o < text font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "44.036,736.334,46.367,744.496" size = "8.162" > r < text font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "46.367,736.334,52.338,744.496" size = "8.162" > m < text font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "52.338,736.334,54.284,744.496" size = "8.162" > < text font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "54.284,736.334,56.230,744.496" size = "8.162" > < text font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "56.230,736.334,58.176,744.496" size = "8.162" > < text font = "JYMPLA+HelveticaNeueLTStd-Roman" bbox = "58.176,736.334,60.122,744.496" size = "8.162" > < text font = "ZWOHBU+HelveticaNeueLTStd-BlkCn" bbox = "60.122,732.312,78.794,761.160" size = "28.848" > W < text font = "ZWOHBU+HelveticaNeueLTStd-BlkCn" bbox = "78.794,732.312,87.626,761.160" size = "28.848" > - < text font = "ZWOHBU+HelveticaNeueLTStd-BlkCn" bbox = "87.626,732.312,100.106,761.160" size = "28.848" > 9 < text > 使用Slate提取文本 Tim McNamara觉得PDFMiner使用起来太过愚蠢和费力,因此他写了一个围绕它的包装器叫做slate,以使它更简单地从PDF中提取文本。不幸的是,它和Python 3不兼容。如果你想试用,你可能需要easy_install以便于安装distribute包,如下: easy_install distribute 我不能使用pip 正确安装这个包。然而一旦安装了它,你将能够使用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 import slate def 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' ) 正如你能看到的,让slate分析一个PDF文件,你只需要引进slate然后创建一个它的PDF类的实例。PDF类其实是Python内置类list的一个子类,所以它仅是返回了一列/可遍历的文本页。如果PDF文件设有密码,你可以传入一个密码参数。不管怎样,一旦文件被分析,我们只要打印出每一页的文本即可。 我非常喜欢slate,它用起来更简单。不幸的是,这个包也几乎没有什么相关文档。在浏览过它的源码之后,它看起来只支持纯文本提取。 导出你的数据 现在我们得到了一些文本,我们会花费一些时间来学习如何导出数据成各种不同的格式。具体来说,我们将学习如何以如下方法导出文本: 让我们开始吧! 导出成XML 可扩展标记语言(XML)格式是最为人所熟知的输入输出格式之一。它被广泛运用于互联网中的许多不同的事物。正如我们已经在本贴中看到的,PDFMiner也支持XML作为它的输出之一。 话虽这么说,让我们创建我们自己的XML生成工具。如下是一个简单的例子: # xml_exporter.py import os import xml.etree.ElementTree as xml from miner_text_generator import extract_text_by_page from xml.dom import minidom def 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)元素。下一步是for循环,在此循环中我们从PDF中提取每一页然后保存想要的信息。此处你可以加入一个特定的分析程序,其中你可以将页分成句子或者单词,从而分析出更有趣的信息。比如,你可能只想得到有某个特定名字或日期/时间戳的句子。你可以运用Python的正则表达式来找出这类东西,或者仅是检查子字符串在句子中的存在。 对于这个例子,我们仅仅是提取了每一页的前100个字符并将其存入一个XML的子元素(SubElement)中。接下来的一段代码可以简化成仅是写出XML文件。然而,ElementTree不会做任何事来使得XML易读。它最后看上去有点像压缩的Java似的一块巨型文本。所以我们在写入文件之前使用minidom 通过空格来“美化”XML,而不是将整块文本写入磁盘。最终看上去像这样: < 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 上面是漂亮干净的XML,同时它也是易读的。锦上添花的是,你可以运用你在PyPDF2章节中所学到的知识从PDF中提取元数据(metadata),然后将其也加入到XML中。 导出成JSON Java对象注释, 或者JSON, 是一种易读易写的轻量级的数据交换格式。Python包含一个json 模块于它的标准库中,从而允许你用编程方式来读写JSON。让我们运用从前一章节学到的内容来创建一个导出器脚本来输出JSON而不是XML: # json_exporter.py import json import os from miner_text_generator import extract_text_by_page def 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文件的输出路径为参数。在Python中JSON基本上就是一个字典,所以我们创建一对简单的顶层的键: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的元数据(metadata)来加强这个例子,如果你乐意的话。请注意输出将会改变,它依赖于你想从每一页或文档中分析出什么样的结果。 现在让我们来快速看一下怎样导出CSV文件。 导出成CSV CSV是 **comma separated values** (逗号分隔值)的缩写。它是一种漂亮的标准格式,并且已经存在了很长时间。CSV的优点就是Microsoft Excel和 LibreOffice都能够自动地以漂亮的电子表格的方式将它们打开。你也可以在一个文本编辑器中打开CSV文件,如果你乐意看到它的原始值的话。 Python有一个内置的csv 模块,你可以用它来读写CSV文件。在这里我们将用它从我们由PDF中提取的文本来创建一个CSV。让我们看一下代码: # csv_exporter.py import csv import os from miner_text_generator import extract_text_by_page def 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 ,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样本。