pytho处理pdf

原书地址

关注微信公众号(瓠悠笑软件部落),一起学习,一起摸鱼
huyouxiao.com

简介

PDF 和 Word 文档都是二进制文件,但比普通的文本文件要复杂一些,它们除了保存文本之外,还要保存字体,颜色,布局等信息。如果你想让你的程序能够读取或者写内容到 PDF 文件或者 Word 文档中,除了将文件名传给 open() 函数外,你还需要做更多的事情。
  幸运的是,Python 有很多现成的 modules. 可以让你轻松的处理 PDFs 和 Word 文档。本文将要介绍两个模块: PyPDF2 和 Python-Docx

PDF 文档

PDF 是 Protable Document Format 的缩写,使用 .pdf 作为文件的扩展名。虽然PDF支持许多功能,但本章将重点介绍这两个功能。你将经常与他们做的事情:阅读PDF文本内容和从现有文档中制作新的PDF文件。
  
  您将用于处理PDF的模块是PyPDF2。 要安装它,请运行从命令行安装PyPDF2: pip install PyPDF2。 该模块名称是大小写敏感的,所以要确保 y 是小写的,其他一切都是大写的。(有关安装第三方模块的详细信息,请查看附录A如果模块安装正确,则运行导入PyPDF2交互式shell不应显示任何错误。

有问题的PDF格式
虽然PDF文件非常适合以人们容易的方式布置文本打印和阅读,软件解析成纯文本并不简单。
因此,从PDF和文本中提取文本时,PyPDF2可能会出错,甚至可能根本无法打开一些PDF。 这件事你无能为力。不幸的是, PyPDF2可能根本无法使用某些特定PDF文件。但反过来想,PyPDF2可以打开大多数PDF文件,到目前为止我还没有找到任何PDF文件不能用PyPDF2打开。

从 PDF 中提取文本

PyPDF2无法从PDF文档中提取图像,图表或其他媒体,但它可以提取文本并将其作为Python字符串返回。 在开始学习PyPDF2的工作原理,我们将在示例中使用图13-1中显示的PDF。
图13-1

#! /usr/bin/python3
import PyPDF2
pdfFileObj = open('meetingminutes.pdf', 'rb')
pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
print(str(pdfReader.numPages))
pageObj = pdfReader.getPage(0)
print(pageObj.extractText())

# 输出内容
19
OOFFFFIICCIIAALL  BBOOAARRDD  MMIINNUUTTEESS   Meeting of 
March 7
, 2014
        
     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 glob
al market.
 BOARD 
 of ELEMENTARY
 and 
 SECONDARY
 EDUCATION

首先,导入PyPDF2模块。 然后以二进制读取模式(‘rb’) 打开 meetingminutes.pdf 并将其存储在pdfFileObj 中。 获取一个 PdfFileReader 对象表示这个PDF,调用PyPDF2.PdfFileReader()并传递给它pdfFileObj。 将 PdfFileReader 存储在 pdfReader 对象中。
文档中的总页数存储在PdfFileReader对象的属性numPages中。 示例PDF有19页,但是让我们只从第一页提取文本。
要从页面中提取文本,您需要获取一个Page对象,该对象代表从PdfFileReader对象中查找PDF的单页。 你可以得到一个页面通过在PdfFileReader对象上调用 getPage() 方法并传递您感兴趣的页面的页码 - 在我们的例子中: 0。
PyPDF2使用从零开始的索引来获取页面:第一页是第0页,第二个是第1页,依此类推。 即使是页面,情况也是如此在文件中编号不同。 例如,说你的PDF是来自较长报告的三页摘录,其页面编号为42,43,和44.要获得本文档的第一页,您可以调用pdfReader.getPage(0),而不是getPage(42) 或getPage(1)
获得Page对象后,调用其extractText()方法返回一个页面的文本字符串。 文本提取并不完美:文本Charles E.“Chas”来自PDF的总裁Roemer缺席了返回的字符串extractText(),间距有时会关闭。 不过,这个近似值PDF文本内容可能对您的程序来说足够好。

解密PDFs

某些PDF文档具有可以防止它们的加密功能正在阅读,直到打开文档的人提供密码。使用您下载的PDF在交互式shell中输入以下内容,已使用密码rosebud加密:

>>> import PyPDF2
>>> pdfReader = PyPDF2.PdfFileReader(open('encrypted.pdf', 'rb'))
>>> pdfReader.isEncrypted
True
>>> pdfReader.getPage(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.5/dist-packages/PyPDF2/pdf.py", line 1176, in getPage
    self._flatten()
  File "/usr/local/lib/python3.5/dist-packages/PyPDF2/pdf.py", line 1505, in _flatten
    catalog = self.trailer["/Root"].getObject()
  File "/usr/local/lib/python3.5/dist-packages/PyPDF2/generic.py", line 516, in __getitem__
    return dict.__getitem__(self, key).getObject()
  File "/usr/local/lib/python3.5/dist-packages/PyPDF2/generic.py", line 178, in getObject
    return self.pdf.getObject(self).getObject()
  File "/usr/local/lib/python3.5/dist-packages/PyPDF2/pdf.py", line 1617, in getObject
    raise utils.PdfReadError("file has not been decrypted")
PyPDF2.utils.PdfReadError: file has not been decrypted
>>> pdfReader = PyPDF2.PdfFileReader(open('encrypted.pdf', 'rb'))
>>> pdfReader.decrypt('rosebud')
1
>>> pageObj = pdfReader.getPage(0)
>>> print(pageObj.extractText())
OOFFFFIICCIIAALL  BBOOAARRDD  MMIINNUUTTEESS   Meeting of 
March 7
, 2014
        
     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 glob
al market.
 BOARD 
 of ELEMENTARY
 and 
 SECONDARY
 EDUCATION
>>> 

所有PdfFileReader对象都有一个isEncrypted属性,如果是,则为True PDF是加密的,如果不是你,则为False。 任何试图调用函数的尝试在使用正确的密码解密之前读取文件导致错误。
要读取加密的PDF,请调用decrypt() 函数并传递 password 作为字符串。 用正确的密码调用decrypt() 后,你就可以了看到调用getPage() 不再导致错误。 如果用错误的 password,decrypt() 函数将返回0 并且 getPage() 将继续失败。请注意,decrypt() 方法仅解密PdfFileReader对象实际的PDF文件。 程序终止后,硬盘上的文件驱动器仍然加密。 你的程序将不得不再次调用decrypt() 下次运行。

创建PDF文件

PyPDF2与PdfFileReader对象的对应关系是PdfFileWriter对象可以创建新的PDF文件。 但是PyPDF2无法像Python可以处理纯文本文件一样将任意文本写入PDF。 相反,PyPDF2的PDF写作能力能力仅限于:从其他PDF,旋转页面,复制页面,铺设页面,加密文件。
PyPDF2不允许您直接编辑PDF。 相反,你必须创建一个新的PDF,然后从现有文档复制内容。本节中的示例将遵循以下一般方法:

  1. 在 PdfFileReader 对象中打开一个或者多个已经存在的 PDFs 文件。
  2. 创建一个新的 PdfFileWriter 对象。
  3. 从PdfFileReader 对象中复制页面,保存到 PdfFileWriter 对象中。
  4. 最后,使用 PdfFileWriter 对象将内容写到 PDF 文件中。

创建PdfFileWriter对象仅创建表示Python中的PDF文档的值。 它不会创建实际的PDF文件。 为此,你必须调用PdfFileWriter的write()方法
write()方法使用写二进制模式(write-binary)打开普通的File对象。 您可以通过调用Python的open()来获取这样的File对象。函数有两个参数:你想要PDF文件名的字符串,和’wb’表示文件应该以写二进制模式打开。如果这听起来有点令人困惑,请不要担心 , 在以下代码示例中你会看到它是如何工作的。

复制页面

您可以使用PyPDF2将页面从一个PDF文档复制到另一个PDF文档。这允许您组合多个PDF文件,剪切不需要的页面,或重新排序页面。

#! /usr/bin/python3
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文件并存储两个结果到 pdf1File 和 pdf2File 的文件对象中。 调用PyPDF2.PdfFileReader() 并传递它 pdf1File 来获取 sessionminutes.pdf 的 PdfFileReader 对象。 再一次调用并传递给它 pdf2File 以获取 meetingminutes2.pdf 的PdfFileReader对象。然后创建一个新的PdfFileWriter对象,它表示一个空白的PDF。

接下来,复制两个源PDF中的所有页面并添加它们到PdfFileWriter对象。 通过调用getPage()来获取PdfFileReader对象的Page对象。 然后将该Page对象传递给PdfFileWriter 的 addPage() 方法。 这些步骤首先针对pdf1Reader 做一次,然后再针对pdf2Reader 做一次。 完成复制页面后,将新的文件名 combinedminutes.pdf 传递给open()方法, 创建新的PDF文件,然后调用pdfWriter的write方法,把 pdfOutoutFile 传进 write() 方法,完成写pdf内容操作,最后关闭所有打开的pdf文件。

注意
PyPDF2无法在PdfFileWriter对象的中间插入页面; addPage()方法只会添加页面到最后。

您现在已经创建了一个新的PDF文件,它将 meetingminutes.pdf 和 meetingminutes2.pdf 的页面合并成单个文档。记住需要在读取二进制模式下打开传递给PyPDF2.PdfFileReader() 的File对象
,将’rb’作为第二个参数传递给open()。类似的,需要使用’wb’写二进制模式打开传递给PyPDF2.PdfFileWriter() 的File对象。

旋转页面

可以使用 rotateClockwise() 和 rotateCounterClockwise() 方法顺时针旋转页面或者逆时针旋转页面。

#! /usr/bin/python3
import PyPDF2 
minutesFile = open('meetingminutes.pdf', 'rb')
pdfReader = PyPDF2.PdfFileReader(minutesFile)
page = pdfReader.getPage(0)
page.rotateClockwise(90)
pdfWriter = PyPDF2.PdfFileWriter()
pdfWriter.addPage(page)
resultPdfFile = open('rotatePage.pdf', 'wb')
pdfWriter.write(resultPdfFile)
resultPdfFile.close()
minutesFile.close()

这里我们使用getPage(0) 来选择PDF 的第一页,然后我们在那个 page 上调用rotateClockwise(90)。我们编写一个新的PDF,并将其保存为rotatingPage.pdf。生成的PDF将有一页,旋转90度. 来自rotateClockwise() 和的返回值 rotateCounterClockwise() 包含许多您可以忽略的信息。

叠加页面(给PDF文件添加水印)

PyPDF2还可以将一个页面的内容覆盖在另一个页面的内容上,用于向页面添加徽标,时间戳或水印。 使用Python可以轻松地将水印添加到多个文件中。

#! /usr/bin/python3
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 变量。然后我们用PdfFileReader读取水印pdf文件,minutesFirstPage 调用 mergePage()。 将watermark.pdf第一页的Page对象覆盖在minutesFirstPage上。

加密PDF文件

PdfFileWriter对象还加密PDF文档:

#! /usr/bin/python3
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('huxing123')
resultPdf = open('encryptedPdf.pdf', 'wb')
pdfWriter.write(resultPdf)
resultPdf.close()

在调用 write() 方法保存到文件之前,调用encrypt() 方法并传递密码字符串。 PDF可以有用户密码
(允许您查看PDF)和所有者密码(允许您设置)打印,评论,提取文本和其他功能的权限)。
用户密码和所有者密码是第一个和第二个参数分别加密。 如果只有一个字符串参数传递给encrypt(),它将用于两个密码。在此示例中,我们将meetingminutes.pdf的页面复制到一个
PdfFileWriter对象。 我们用密码加密了PdfFileWriter,打开了一个名为encryptedminutes.pdf的新PDF,并写了PdfFileWriter的内容到新的PDF。 在任何人都可以查看encryptedPdf.pdf,他们必须输入此密码。 你可能想要确认后删除原始的未加密的meetingminutes.pdf文件副本已正确加密。

猜你喜欢

转载自blog.csdn.net/fudaxing/article/details/88710713