Skip to content

PDF文档

PDF 表示 Portable Document Format,使用.pdf文件扩展名。虽然 PDF 支持许多功能,但本章将专注于最常做的两件事:从 PDF 读取文本内容和从已有的文档生成新的PDF。

用于处理 PDF 的模块是 PyPDF2 。要安装它,就从命令行运行 pip install PyPDF2 。这个模块名称是区分大小写的,所以要确保y是小写,其他字母都是大写。如果该模块安装正确,在交互式环境中运行import PyPDF2,应该不会显示任何错误。

PyPDF2 默认只能处理英文,如果想让 PyPDF2 能够处理中文,则需要小小改动一下源码(见附录)。

shell
pip install PyPDF2
pip install pdfplumber

下载测试文件

点我下载

创建 PDF

在 PyPDF2 中,与 PdfReader 对象相对的是 PdfWriter 对象,它可以创建一个新的 PDF 文件。但 PyPDF2 不能将任意文本写入 PDF ,就像 Python 可以写入纯文本文件那样。PyPDF2 写入 PDF 的能力,仅限于从其他 PDF 中拷贝页面、旋转页面、重叠页面和加密文件。

模块不允许直接编辑 PDF。必须创建一个新的 PDF,然后从已有的文档拷贝内容。本节的例子将遵循这种一般方式:

1.打开一个或多个已有的 PDF(源PDF),得到 PdfReader 对象。

2.创建一个新的 PdfWriter 对象。

3.将页面从 PdfFileReader 对象拷贝到 PdfFileWriter 对象中。

4.最后,利用 PdfWrite r 对象写入输出的 PDF。

创建一个 PdfWriter 对象,只是在 Python 中创建了一个代表 PDF 文档的值,这并没有创建实际的 PDF 文件,要实际生成文件,必须调用 PdfWriter 对象的 write() 方法。

write() 方法接受一个普通的 File 对象,它以写二进制的模式打开。你可以用两个参数调用 Python 的 open() 函数,得到这样的 File 对象:一个是要打开的 PDF 文件名字符串,一个是 'wb' ,表明文件应该以写二进制的模式打开。

如果这听起来有些令人困惑,不用担心,在接下来的代码示例中,你会看到这种工作方式。

拷贝页面

可以利用 PyPDF2 ,从一个 PDF 文档拷贝页面到另一个 PDF 文档。这让你能够组合多个 PDF 文件,去除不想要的页面,或调整页面的次序。

python
from PyPDF2 import PdfReader, PdfWriter

"""分割前二十页"""
# 读取 pdf 文件
pdf_reader = PdfReader('金发科技2019年年报.pdf')
# 获取所有的总页数
for page in range(len(pdf_reader.pages)):
    # 创建一个 pdf 写入对象
    pdf_writer = PdfWriter()
    # 往写入对象中添加一页pdf内容
    pdf_writer.add_page(pdf_reader.pages[page])
    # 将 pdf 写入对象保存到本地
    with open(f'金发科技2019年年报 {page}.pdf', 'wb') as out:
        pdf_writer.write(out)
    # 写入前二十页就可以了
    if page > 20:
        break

提取多页写入一个文件

python
from PyPDF2 import PdfReader, PdfWriter

"""一次分割多页数据"""
pdf_reader = PdfReader('金发科技2019年年报.pdf')
pdf_writer = PdfWriter()
# 循环添加二十页数据
for page in range(20):
    pdf_writer.add_page(pdf_reader.pages[page])

with open('金发科技2019年年报20页.pdf', 'wb') as out:
    pdf_writer.write(out)

从 PDF 提取文本

PyPDF2 没有办法从 PDF 文档中提取图像、图表或其他媒体,但它可以提取文本,并将文本返回为 Python 字符串。为了开始学习 PyPDF2 的工作原理,我们将它用于一个示例 PDF

有问题的PDF格式

虽然PDF文件对文本布局非常好,让人们很容易打印并阅读,但软件要将它们解析为纯文本却并不容易。因此,PyPDF2从PDF提取文本时可能会出错,甚至根本不能打开某些PDF。遗憾的是,你对此没有什么办法,PyPDF2可能就是不能处理某些PDF文件。话虽这样说,我至今没有发现不能用PyPDF2打开的PDF文件。

python
import pdfplumber
import openpyxl

"""
    中文内容读取需要修改部分源码内容
"""
pdf_filename = '金发科技2019年年报20页.pdf'
with pdfplumber.open(pdf_filename) as pdf:
    """提取文字"""
    # 读取第一页数据
    first_page = pdf.pages[0]
    print(first_page.extract_text())
    for page in pdf.pages:
        print(page.extract_text())

    """提取表格"""
    # 第五页
    page_5 = pdf.pages[5 - 1]
    print(page_5.extract_table())

    # 第六页
    page_6 = pdf.pages[6 - 1]
    print(page_6.extract_tables())

    """
    1.打开文件金发科技2019年年报20页.pdf
    2.提取第5页的表格
    3.保存到 第五页表格.xlsx 文件
    """
    table = page_5.extract_table()
    workbook = openpyxl.Workbook()
    sheet = workbook.active
    for row in table:
        print(row)
        sheet.append(row)
    workbook.save('第五页表格.xlsx')

添加水印

PyPDF2 也可以将一页的内容叠加到另一页上,这可以用来在页面上添加公司标志、时间戳或水印。利用Python,很容易为多个文件添加水印,并且只针对程序指定的页面添加。

python
from PyPDF2 import PdfWriter, PdfReader
import copy

# 加载水印文件
watermark_paf = PdfReader('logo.pdf', strict=False)
watermark_page = watermark_paf.pages[len(watermark_paf.pages) - 1]

pdf_reader = PdfReader("金发科技2019年年报20页.pdf")
pdf_writer = PdfWriter()
"""
对每一页都进行合并水印的操作 ,注意.mergePage() 方法合成的页面顺序
下面的内容.mergePage(出现在上面的内容)
"""

for page in range(len(pdf_reader.pages)):
    original_page = pdf_reader.pages[page]
    # 复制一页水印
    new_page = copy.copy(watermark_page)
    # 合并水印
    new_page.merge_page(original_page)
    pdf_writer.add_page(new_page)

with open('金发科技2019年年报20页 加水印.pdf', 'wb') as out:
    pdf_writer.write(out)

加密 PDF

PdfFileWriter 对象也可以为 PDF 文档进行加密。

在调用 write() 方法保存文件之前,调用 encrypt() 方法,传入口令字符串。PDF 可以有一个用户口令(允许查看这个PDF)和一个拥有者口令(允许设置打印、注释、提取文本和其他功能的许可)。用户口令和拥有者口令分别是 encrypt() 的第一个和第二个参数。如果只传入一个字符串给 encrypt(),它将作为两个口令。

python
from PyPDF2 import PdfWriter, PdfReader

pdf_reader = PdfReader("Netease Q2 2019 Earnings Release-Final.pdf")
pdf_writer = PdfWriter()

for page in range(pdf_reader.getNumPages()):
    pdf_writer.addPage(pdf_reader.getPage(page))

"""在保存之前加密
pdf_writer.encrypt(密码)
"""
pdf_writer.encrypt('123456')
with open('encrypt.pdf', 'wb') as out:
    pdf_writer.write(out)

print('写入完毕')

解密 PDF

某些PDF文档有加密功能,以防止别人阅读,只有在打开文档时提供口令才能阅读。在交互式环境中输入以下代码,处理下载的PDF,它已经用口令rosebud加密:

python
"""读取时解密
pdf_reader.decrypt(密码)
"""
decrypt_reader = PdfReader('encrypt.pdf')
print('开始解密')
decrypt_reader.decrypt('123456')
print(decrypt_reader.getNumPages())
for page in range(decrypt_reader.getNumPages()):
    print(decrypt_reader.pages)

所有 PdfReader 对象都有一个 isEncrypted 属性,如果 PDF 是加密的,它就是 True,如果不是,它就是 False。在文件用正确的口令解密之前,尝试调用函数来读取文件,将会导致错误。

要读取加密的 PDF,就调用 decrypt() 函数,传入口令字符串。在用正确的口令调用 decrypt() 后,你会看到调用 get_page() 不再导致错误。如果提供了错误的口令,decrypt() 函数将返回0,并且 get_page() 会继续失败。请注意,decrypt() 方法只解密了 PdfReader 对象,而不是实际的 PDF 文件。在程序中止后,硬盘上的文件仍然是加密的。程序下次运行时,仍然需要再次调用 decrypt() 。