- 内容提要
- 作者简介
- 技术评审者简介
- 致谢
- 译者序 会编程的人不一样
- 前言
- 本书的读者对象
- 编码规范
- 什么是编程
- 本书简介
- 下载和安装 Python
- 启动 IDLE
- 如何寻求帮助
- 聪明地提出编程问题
- 小结
- 第一部分 Python 编程基础
- 第1章 Python 基础
- 第2章 控制流
- 第3章 函数
- 第4章 列表
- 第5章 字典和结构化数据
- 第6章 字符串操作
- 第二部分 自动化任务
- 第7章 模式匹配与正则表达式
- 第8章 读写文件
- 第9章 组织文件
- 第10章 调试
- 第11章 从 Web 抓取信息
- 第12章 处理 Excel 电子表格
- 第13章 处理 PDF 和 Word 文档
- 第14章 处理 CSV 文件和 JSON 数据
- 第15章 保持时间、计划任务和启动程序
- 第16章 发送电子邮件和短信
- 第17章 操作图像
- 第18章 用 GUI 自动化控制键盘和鼠标
- 附录A 安装第三方模块
- 附录B 运行程序
- 附录C 习题答案
13.1 PDF 文档
PDF表示Portable Document Format,使用.pdf文件扩展名。虽然PDF支持许多功能,但本章将专注于最常做的两件事:从PDF读取文本内容和从已有的文档生成新的PDF。
用于处理PDF的模块是PyPDF2。要安装它,就从命令行运行pip install PyPDF2。这个模块名称是区分大小写的,所以要确保y是小写,其他字母都是大写(请查看附录A,了解安装第三方模块的所有细节)。如果该模块安装正确,在交互式环境中运行import PyPDF2,应该不会显示任何错误。
13.1.1 从PDF提取文本
PyPDF2没有办法从PDF文档中提取图像、图表或其他媒体,但它可以提取文本,并将文本返回为Python字符串。为了开始学习PyPDF2的工作原理,我们将它用于一个示例PDF,如图13-1所示。
图13-1 PDF页面,我们将从中提取文本
有问题的PDF格式
虽然PDF文件对文本布局非常好,让人们很容易打印并阅读,但软件要将它们解析为纯文本却并不容易。因此,PyPDF2从PDF提取文本时可能会出错,甚至根本不能打开某些PDF。遗憾的是,你对此没有什么办法,PyPDF2可能就是不能处理某些PDF文件。话虽这样说,我至今没有发现不能用PyPDF2打开的PDF文件。
从http://nostarch.com/automatestuff/下载这个PDF文件,并在交互式环境中输入以下代码:
>>> import PyPDF2 >>> pdfFileObj = open('meetingminutes.pdf', 'rb') >>> pdfReader = PyPDF2.PdfFileReader(pdfFileObj) ❶ >>> pdfReader.numPages 19 ❷ >>> pageObj = pdfReader.getPage(0) ❸ >>> pageObj.extractText() 'OOFFFFIICCIIAALL BBOOAARRDD MMIINNUUTTEESS Meeting of March 7, 2015 \n The Board of Elementary and Secondary Education shall provide leadership and create policies for education that expand opportunities for children, empower families and communities, and advance Louisiana in an increasingly competitive global market. BOARD of ELEMENTARY and SECONDARY EDUCATION '
首先,导入PyPDF2模块。然后以读二进制模式打开meetingminutes.pdf,并将它保存在pdfFileObj中。为了取得表示这个PDF的PdfFileReader对象,调用PyPDF2. PdfFileReader()并向它传入pdfFileObj。将这个PdfFileReader对象保存在pdfReader中。
该文档的总页数保存在PdfFileReader对象的numPages属性中❶。示例PDF文档有19页,但我们只提取第一页的文本。
要从一页中提取文本,需要通过PdfFileReader对象取得一个Page对象,它表示PDF中的一页。可以调用PdfFileReader对象的getPage()方法❷,向它传入感兴趣的页码(在我们的例子中是0),从而取得Page对象。
PyPDF2在取得页面时使用从0开始的下标:第一页是0页,第二页是1页,以此类推。事情总是这样,即使文档中页面的页码不同。例如,假定你的PDF是从一个较长的报告中抽取出3页,它的页码分别是42、43和44,要取得这个文档的第一页,需要调用pdfReader.getPage(0),而不是getPage(42)或getPage(1)。
在取得Page对象后,调用它的extractText()方法,返回该页文本的字符串❸。文本提取并不完美:该PDF中的文本Charles E.“Chas”Roemer, President,在函数返回的字符串中消失了,而且空格有时候也会没有。但是,这种近似的PDF文本内容,可能对你的程序来说已经足够了。
13.1.2 解密PDF
某些PDF文档有加密功能,以防止别人阅读,只有在打开文档时提供口令才能阅读。在交互式环境中输入以下代码,处理下载的PDF,它已经用口令rosebud加密:
>>> import PyPDF2 >>> pdfReader = PyPDF2.PdfFileReader(open('encrypted.pdf', 'rb')) ❶ >>> pdfReader.isEncrypted True >>> pdfReader.getPage(0) ❷ Traceback (most recent call last): File "< pyshell#173>", line 1, in < module> pdfReader.getPage() --snip-- File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 1173, in getObject raise utils.PdfReadError("file has not been decrypted") PyPDF2.utils.PdfReadError: file has not been decrypted ❸ >>> pdfReader.decrypt('rosebud') 1 >>> pageObj = pdfReader.getPage(0)
所有PdfFileReader对象都有一个isEncrypted属性,如果PDF是加密的,它就是True,如果不是,它就是False❶。在文件用正确的口令解密之前,尝试调用函数来读取文件,将会导致错误❷。
要读取加密的PDF,就调用decrypt()函数,传入口令字符串❸。在用正确的口令调用decrypt()后,你会看到调用getPage()不再导致错误。如果提供了错误的口令,decrypt()函数将返回0,并且getPage()会继续失败。请注意,decrypt()方法只解密了PdfFileReader对象,而不是实际的PDF文件。在程序中止后,硬盘上的文件仍然是加密的。程序下次运行时,仍然需要再次调用decrypt()。
13.1.3 创建PDF
在PyPDF2中,与PdfFileReader对象相对的是PdfFileWriter对象,它可以创建一个新的PDF文件。但PyPDF2不能将任意文本写入PDF,就像Python可以写入纯文本文件那样。PyPDF2写入PDF的能力,仅限于从其他PDF中拷贝页面、旋转页面、重叠页面和加密文件。
模块不允许直接编辑PDF。必须创建一个新的PDF,然后从已有的文档拷贝内容。本节的例子将遵循这种一般方式:
1.打开一个或多个已有的PDF(源PDF),得到PdfFileReader对象。
2.创建一个新的PdfFileWriter对象。
3.将页面从PdfFileReader对象拷贝到PdfFileWriter对象中。
4.最后,利用PdfFileWriter对象写入输出的PDF。
创建一个PdfFileWriter对象,只是在Python中创建了一个代表PDF文档的值,这并没有创建实际的PDF文件,要实际生成文件,必须调用PdfFileWriter对象的write()方法。
write()方法接受一个普通的File对象,它以写二进制的模式打开。你可以用两个参数调用Python的open()函数,得到这样的File对象:一个是要打开的PDF文件名字符串,一个是'wb',表明文件应该以写二进制的模式打开。
如果这听起来有些令人困惑,不用担心,在接下来的代码示例中,你会看到这种工作方式。
13.1.4 拷贝页面
可以利用PyPDF2,从一个PDF文档拷贝页面到另一个PDF文档。这让你能够组合多个PDF文件,去除不想要的页面,或调整页面的次序。
从http://nostarch.com/automatestuff/下载meetingminutes.pdf和meetingminutes2.pdf,放在当前工作目录中。在交互式环境中输入以下代码:
>>> import PyPDF2 >>> pdf1File = open('meetingminutes.pdf', 'rb') >>> pdf2File = open('meetingminutes2.pdf', 'rb') ❶ >>> pdf1Reader = PyPDF2.PdfFileReader(pdf1File) ❷ >>> pdf2Reader = PyPDF2.PdfFileReader(pdf2File) ❸ >>> pdfWriter = PyPDF2.PdfFileWriter() >>> for pageNum in range(pdf1Reader.numPages): ❹ pageObj = pdf1Reader.getPage(pageNum) ❺ pdfWriter.addPage(pageObj) >>> for pageNum in range(pdf2Reader.numPages): ❹ pageObj = pdf2Reader.getPage(pageNum) ❺ pdfWriter.addPage(pageObj) ❻ >>> pdfOutputFile = open('combinedminutes.pdf', 'wb') >>> pdfWriter.write(pdfOutputFile) >>> pdfOutputFile.close() >>> pdf1File.close() >>> pdf2File.close()
以读二进制的模式打开两个PDF文件,将得到的两个File对象保存在pdf1File和pdf2File中。调用PyPDF2.PdfFileReader(),传入pdf1File,得到一个表示meetingminutes.pdf的PdfFileReader对象❶。再次调用PyPDF2.PdfFileReader(),传入pdf2File,得到一个表示meetingminutes2.pdf的PdfFileReader对象❷。然后创建一个新的PdfFileWriter对象,它表示一个空白的PDF文档❸。
接下来,从两个源PDF拷贝所有的页面,将它们添加到PdfFileWriter对象。在PdfFileReader对象上调用getPage(),取得Page对象❹。然后将这个Page对象传递给PdfFileWriter的addPage()方法❺。这些步骤先是针对pdf1Reader进行,然后再针对pdf2Reader进行。在拷贝页面完成后,向PdfFileWriter的write()方法传入一个File对象,写入一个新的PDF文档,名为combinedminutes.pdf❻。
注意
PyPDF2不能在PdfFileWriter对象中间插入页面,addPage()方法只能够在末尾添加页面。
现在你创建了一个新的PDF文件,将来自meetingminutes.pdf和meetingmin utes2.pdf的页面组合在一个文档中。要记住,传递给PyPDF2.PdfFileReader()的File对象,需要以读二进制的方式打开。即使用'rb'作为open()的第二个参数。类似的,传入PyPDF2.PdfFileWriter()的File对象需要以写二进制的模式打开,即使用'wb'。
13.1.5 旋转页面
利用rotateClockwise()和rotateCounterClockwise()方法,PDF文档的页面也可以旋转90度的整数倍。向这些方法传入整数90、180或270就可以了。在交互式环境中输入以下代码,同时将meetingminutes.pdf放在当前工作目录中:
>>> import PyPDF2 >>> minutesFile = open('meetingminutes.pdf', 'rb') >>> pdfReader = PyPDF2.PdfFileReader(minutesFile) ❶ >>> page = pdfReader.getPage(0) ❷ >>> page.rotateClockwise(90) {'/Contents': [IndirectObject(961, 0), IndirectObject(962, 0), --snip-- } >>> pdfWriter = PyPDF2.PdfFileWriter() >>> pdfWriter.addPage(page) ❸>>> resultPdfFile = open('rotatedPage.pdf', 'wb') >>> pdfWriter.write(resultPdfFile) >>> resultPdfFile.close() >>> minutesFile.close()
这里,我们使用getPage(0)来选择PDF的第一页❶,然后对该页调用rotateClockwise(90)❷。我们将旋转过的页面写入一个新的PDF文档,并保存为rotatedPage.pdf❸。
得到的PDF文件有一个页面,顺时针旋转了90度,如图13-2所示。rotateClockwise()和rotateCounterClockwise()的返回值包含许多信息,你可以忽略。
图13-2 rotatedPage.pdf文件,页面顺时针旋转了90度
13.1.6 叠加页面
PyPDF2也可以将一页的内容叠加到另一页上,这可以用来在页面上添加公司标志、时间戳或水印。利用Python,很容易为多个文件添加水印,并且只针对程序指定的页面添加。
从http://nostarch.com/automatestuff/下载watermark.pdf,将它和meetingminutes.pdf一起放在当前工作目录中。然后在交互式环境中输入以下代码:
>>> import PyPDF2 >>> minutesFile = open('meetingminutes.pdf', 'rb') ❶ >>> pdfReader = PyPDF2.PdfFileReader(minutesFile) ❷ >>> minutesFirstPage = pdfReader.getPage(0) ❸>>> pdfWatermarkReader = PyPDF2.PdfFileReader(open('watermark.pdf', 'rb')) ❹ >>> minutesFirstPage.mergePage(pdfWatermarkReader.getPage(0)) ❺ >>> pdfWriter = PyPDF2.PdfFileWriter() ❻ >>> pdfWriter.addPage(minutesFirstPage) ❼ >>> for pageNum in range(1, pdfReader.numPages): pageObj = pdfReader.getPage(pageNum) pdfWriter.addPage(pageObj) >>> resultPdfFile = open('watermarkedCover.pdf', 'wb') >>> pdfWriter.write(resultPdfFile) >>> minutesFile.close() >>> resultPdfFile.close()
这里我们生成了meetingminutes.pdf的PdfFileReader对象❶。调用getPage(0),取得第一页的Page对象,并将它保存在minutesFirstPage中❷。然后生成了watermark.pdf的PdfFileReader对象❸,并在minutesFirstPage上调用mergePage()❹。传递给mergePage()的参数,是watermark.pdf第一页的Page对象。
既然我们已经在minutesFirstPage上调用了mergePage(),minutesFirstPage就代表加了水印的第一页。我们创建一个PdfFileWriter对象❺,并加入加了水印的第一页❻。然后循环遍历meetingminutes.pdf的剩余页面,将它们添加到PdfFileWriter对象中❼。最后,我们打开一个新的PDF文件watermarkedCover.pdf,并将PdfFileWriter的内容写入该文件。
图 13-3 展示了结果。新的 PDF 文件 watermarkedCover.pdf,包含meetingminutes.pdf的全部内容,并在第一页加了水印。
图13-3 最初的PDF(左边)、水印PDF(中间)以及合并的PDF(右边)
13.1.7 加密PDF
PdfFileWriter对象也可以为PDF文档进行加密。在交互式环境中输入以下代码:
>>> import PyPDF2 >>> pdfFile = open('meetingminutes.pdf', 'rb') >>> pdfReader = PyPDF2.PdfFileReader(pdfFile) >>> pdfWriter = PyPDF2.PdfFileWriter() >>> for pageNum in range(pdfReader.numPages): pdfWriter.addPage(pdfReader.getPage(pageNum)) ❶ >>> pdfWriter.encrypt('swordfish') >>> resultPdf = open('encryptedminutes.pdf', 'wb') >>> pdfWriter.write(resultPdf) >>> resultPdf.close()
在调用write()方法保存文件之前,调用encrypt()方法,传入口令字符串❶。PDF可以有一个用户口令(允许查看这个PDF)和一个拥有者口令(允许设置打印、注释、提取文本和其他功能的许可)。用户口令和拥有者口令分别是encrypt()的第一个和第二个参数。如果只传入一个字符串给encrypt(),它将作为两个口令。
在这个例子中,我们将meetingminutes.pdf的页面拷贝到PdfFileWriter对象。用口令swordfish加密了PdfFileWriter,打开了一个名为encryptedminutes.pdf的新PDF,将PdfFileWriter的内容写入新PDF。任何人要查看encryptedminutes.pdf,都必须输入这个口令。在确保文件的拷贝被正确加密后,你可能会删除原来的未加密的文件。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论