- 本书赞誉
- 前言
- 目标读者
- 不适合阅读本书的读者
- 本书结构
- 什么是数据处理
- 遇到困难怎么办
- 排版约定
- 使用代码示例
- 致谢
- 第 1 章 Python 简介
- 第 2 章 Python 基础
- 第 3 章 供机器读取的数据
- 第 4 章 处理 Excel 文件
- 第 5 章 处理 PDF 文件 以及用 Python 解决问题
- 第 6 章 数据获取与存储
- 第 7 章 数据清洗:研究、匹配与格式化
- 第 8 章 数据清洗:标准化和脚本化
- 第 9 章 数据探索和分析
- 第 10 章 展示数据
- 第 11 章 网页抓取:获取并存储网络数据
- 第 12 章 高级网页抓取:屏幕抓取器与爬虫
- 第 13 章 应用编程接口
- 第 14 章 自动化和规模化
- 第 15 章 结论
- 附录 A 编程语言对比
- 附录 B 初学者的 Python 学习资源
- 附录 C 学习命令行
- 附录 D 高级 Python 设置
- 附录 E Python 陷阱
- 附录 F IPython 指南
- 附录 G 使用亚马逊网络服务
- 关于作者
- 关于封面
5.2 解析 PDF 的编程方法
处理 PDF 要比 Excel 文件更加困难,因为每一个 PDF 文件的格式都不可预知。(如果你有一系列 PDF 文件,那么就可以对文件进行解析了,因为这些文档的格式很可能是一致的。)
PDF 工具处理文档的方法有很多种,其中一种方法是将 PDF 转换成文本。在我们写作本书的同时,Danielle Cervantes 在 NICAR 上做了一个关于 PDF 工具的演讲,NICAR 是一个针对记者的 listserv1。这个演讲汇总了下列 PDF 解析工具:
1listserv 是一个用来管理电子邮件列表的软件。——译者注
· ABBYY's Transformer
· Able2ExtractPro
· Acrobat Professional
· Adobe Reader
· Apache Tika
· Cogniview's PDF to Excel
· CometDocs
· Docsplit
· Nitro Pro
· PDF XChange Viewer
· pdfminer
· pdftk
· pdftotext
· Poppler
· Tabula
· Tesseract
· xPDF
· Zamzar
除了上面这些工具,你还可以用许多编程语言来解析 PDF,其中包括 Python。
仅因为你知道类似 Python 这样的工具,并不意味着它总是解决问题的最佳工具。考虑到可用的工具有很多种,你可能会发现用另一个工具来完成部分任务(比如数据提取)更加容易。保持开放的心态,事先对各种可用的工具进行调研。
在 4.1 节中提到过,PyPI 网站是查找 Python 包的好地方。如果搜索“PDF”(https://pypi.python.org/pypi?:action=search&term=pdf&submit=search),你会得到一堆结果,类似图 5-1 给出的那些。
图 5-1:PyPI 网站上的 PDF 包
浏览这些 Python 包,了解一下每个库的详细信息,但分辨不出哪一个库是解析 PDF 的最佳选择。如果你尝试更多的搜索,比如“parse pdf”(解析 pdf),会出现更多的库供你选择,但还是没有明显的最佳选择(搜索结果见 https://pypi.python.org/pypi?:action=search&term=parse+pdf&submit=search)。所以我们用搜索引擎查看一下大家都在用什么库来解析 PDF。
在搜索库或者答案时,注意观察你找到资料的发布日期。帖子或问题的年代越久远,它过时且不再适用的可能性就越大。先试着将搜索范围限定在过去的两年内,然后仅在需要时再扩大搜索的时间范围。
在阅读了许多教程、文档、博客文章和几篇有用的文章(例如这一篇:http://www.binpress.com/tutorial/manipulating-pdfs-with-python/167)之后,我们决定使用 slate 库(https://pypi.python.org/pypi/slate)。
slate 能够满足我们的需求,但并非总是如此。放弃并从头开始也是可以的。如果有很多库可供选择的话,选择你认为最合适的那一个,即使有人告诉你它不是“最好的”工具。究竟哪一个工具最好,大家见仁见智。在你学习编程的过程中,“最好的”工具就是你凭直觉选择的那一个。
5.2.1 利用slate库打开并读取PDF
我们决定用 slate 库来解决这个问题,下面我们来安装这个库。在命令行中运行:
pip install slate
现在你已经安装了 slate,你可以创建一个名为 parse_pdf.py 的脚本,将下面的代码保存其中。一定要确保脚本文件和 PDF 文件位于同一文件夹下,或者修改代码中的文件路径。这段代码会打印出文件的前两行内容:
import slate ➊ pdf = 'EN-FINAL Table 9.pdf' ➋ with open(pdf) as f: ➌ doc = slate.PDF(f) ➍ for page in doc[:2]: ➎ print page
❶ 导入 slate 库。
❷ 创建字符串变量,用于保存文件路径,一定要确保空格和大小写都正确。
❸ 将文件名字符串传入 Python 的 open 函数,这样 Python 就可以打开该文件。Python 将打开的文件保存为变量 f。
❹ 将打开的文件 f 传递给 slate.PDF(f),slate 可以将 PDF 文件解析成可用的格式。
❺ 遍历文档 doc 的前两页并输出,这样我们可以知道程序运行正常。
通常来说,pip 会安装所有必需的依赖库;但需要包管理器列出这些依赖库。在使用 slate 库或其他库时,如果你得到 ImportError,你应该仔细阅读下一行错误信息,看一下哪个库没有安装。运行代码时如果得到如下错误信息:ImportError: No module named pdfminer.pdfparser,说明安装 slate 时没有正确安装 pdfminer,即使它是必需的库。你需要运行 pip install --upgrade --ignoreinstalled slate==0.3 pdfminer==20110515 来安装 pdfminer(参见 slate 仓库的这个 issue,https://github.com/timClicks/slate/issues/5)。
运行脚本,然后将输出内容与 PDF 中的内容对比一下。
下面是第一页的内容:
TABLE 9Afghanistan 10 11 10 15 40 37 - - - - 90 74 75 74Albania 12 14 9 0 10 99 - - - 36 30 75 78 71Algeria 5 y 6 y 4 y 0 2 99 - - - - 68 88 89 87Andorra - - - - - 100 v - - - - - - - -Angola 24 x 22 x 25 x - - 36 x - - - - - - - -Antigua and Barbuda - - - - - - - - - - - - - -Argentina 7 y 8 y 5 y - - 99 y - - - - - - - -Armenia 4 5 3 0 7 100 - - - 20 9 70 72 67Australia - - - - - 100 v - - - - - - - -Austria - - - - - 100 v - - - - - - - -Azerbaijan 7 y 8 y 5 y 1 12 94 - - - 58 49 75 79 71Bahamas - - - - - - - - - - - - - -Bahrain 5 x 6 x 3 x - - - - - - - - - - -Bangladesh 13 18 8 29 65 31 - - - - 33 y - - -Barbados - - - - - - - - - - - - - -Belarus 1 1 2 0 3 100 y - - - 4 4 65 y 67 y 62 yBelgium - - - - - 100 v - - - - - - - -Belize 6 7 5 3 26 95 - - - - 9 71 71 70Benin 46 47 45 8 34 80 13 2 y 1 14 47 - - -Bhutan 3 3 3 6 26 100 - - - - 68 - - -Bolivia ( Plurinational State of) 26 y 28 y 24 y 3 22 76 y - - - - 16 - - -Bosnia and Herzegovina 5 7 4 0 4 100 - - - 6 5 55 60 50Botswana 9 y 11 y 7 y - - 72 - - - - - - - -Brazil 9 y 11 y 6 y 11 36 93 y - - - - - - - -Brunei Darussalam - - - - - - - - - - - - - -Bulgaria - - - - - 100 v - - - - - - - -Burkina Faso 39 42 36 10 52 77 76 13 9 34 44 83 84 82Burundi 26 26 27 3 20 75 - - - 44 73 - - -Cabo Verde 3 x,y 4 x,y 3 x,y 3 18 91 - - - 16 y 17 - - -Cambodia 36 y 36 y 36 y 2 18 62 - - - 22 y 46 y - - -Cameroon 42 43 40 13 38 61 1 1 y 7 39 47 93 93 93Canada - - - - - 100 v - - - - - - - -Central African Republic 29 27 30 29 68 61 24 1 11 80 y 80 92 92 92Chad 26 25 28 29 68 16 44 18 y 38 - 62 84 85 84Chile 3 x 3 x 2 x - - 100 y - - - - - - - -China - - - - - - - - - - - - - -Colombia 13 y 17 y 9 y 6 23 97 - - - - - - - -Comoros 27 x 26 x 28 x - - 88 x - - - - - - - -Congo 25 24 25 7 33 91 - - - - 76 - - -TABLE 9 CHILD PROTECTIONCountries and areasChild labour (%)+ 2005-2012*Child marriage (%) 2005-2012*Birth registration (%)+ 2005-2012*totalFemale genital mutilation/cutting (%)+ 2002-2012*Justification of wife beating (%) 2005-2012*Violent discipline (%)+ 2005-2012*prevalenceattitudestotalmalefemalemarried by 15married by 18womenagirlsbsupport for the practicecmalefemaletotalmalefemale78 THE STATE OF THE WORLD'S CHILDREN 2014 IN NUMBERS
如果你查看一下 PDF 文件,很容易发现页面中每一行的格式。我们来看一下 page 的数据类型是什么:
for page in doc[:2]: print type(page) ➊
❶ 将代码中的 print page 修改为 print type(page)。
运行代码,输出如下:
<type 'str'> <type 'str'>
这样我们知道 slate 中的 page 是一个长字符串。这一点很有用,因为现在我们就可以使用字符串方法(想复习字符串方法,可以回看第 2 章)。
总的来说,读取这个 PDF 文件并不难。由于这个文件中只包含表格,几乎没有任何文本,slate 可以很好地解析。在某些情况下,表格被包围在文本中,你可能需要跳过一些行才能获取你想要的数据。如果你需要跳过一些行的话,可以采用上一章 Excel 例子中的方法,创建一个每行递增的计数器,用来找到数据所在的位置,然后利用 4.3 节“什么是索引”中的方法将我们需要的数据提取出来。
我们的最终目标是从 PDF 文件中提取出数据,数据格式与 Excel 文件的输出格式相同。想要做到这一点,我们需要分割字符串,找出每一行的内容。其背后的思考过程是寻找规律,找到每一个新行开始的标志。这听起来可能很简单,但可能会非常复杂。
在处理大型字符串时,人们通常会使用正则表达式(RegEx)。如果你不熟悉正则表达式和如何编写正则表达式的话,这个方法有点困难。如果你准备挑战一下自己,想学习 Python 中正则表达式的更多内容,可以查看 7.2.6 节。对于我们的目标而言,我们将采用一种更简单的方法来提取数据。
5.2.2 将PDF转换成文本
首先我们希望将 PDF 转换成文本,然后再对文本进行解析。如果文件很大或数量很多,这种方法更好。(使用 slate 库的话,每次运行脚本都要对 PDF 进行解析。如果文件很大或很多的话,这种方法既浪费时间又浪费内存。)
我们要用 pdfminer 将 PDF 转换成文本。首先安装这个库:
pip install pdfminer
安装好 pdfminer,你就可以使用一个叫作 pdf2txt.py 的命令,将 PDF 文件转换成文本。现在我们来试一下。运行下面的命令,将 PDF 转换成同一文件夹下的文本文件,这样所有的数据都在同一文件夹下:
pdf2txt.py -o en-final-table9.txt EN-FINAL\ Table\ 9.pdf
第一个参数(-o en-final-table9.txt)是我们想要创建的文本文件。第二个参数(EN-FINAL\ TABLE\ 9.pdf)是我们的 PDF 文件。确保大小写和文件名中的空格正确。空格前面需要加一个反斜线(\)。这叫转义(escaping)。转义是告诉计算机,空格是输入内容的一部分。
利用 Tab 自动补全
给你介绍一位终端里的新朋友:Tab 键。对于上面命令的第二个参数,你可以先输入 EN,然后按两下 Tab 键。如果只有一种备选的话,计算机会自动补全文件名。如果有多种备选,计算机会发出警告音,并返回一系列备选。在输入又长又奇怪的文件夹或文件名时,这种方法极为有用。
试试这个。切换到 home 目录(在基于 Unix 的系统是 cd ~/,在 Windows 上是 cd %cd%)。现在,假设你想进入 Documents 目录。试着输入 cd D,然后按两下 Tab 键。发生了什么?home 目录里还有哪些文件或文件夹以字母 D 开头?(可能是 Downloads 文件夹?)
现在试一下 cd Doc,然后按两下 Tab 键。你应该会看到自动补全成 Documents 文件夹。
运行完这个命令之后,我们创建了这个 PDF 文件的文本格式文件,叫作 en-final-table9.txt。
我们将新文件读入 Python。创建一个新脚本,与前面的脚本保存在同一文件夹下。脚本名叫作 parse_pdf_text.py,或者你认为合适的其他名字。在脚本中写入下列代码:
pdf_txt = 'en-final-table9.txt' openfile = open(pdf_txt, 'r') for line in openfile: print line
我们可以逐行读取文本,然后打印出每一行,将表格内容以文本格式呈现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论