- 文档
- 教程
【免费下载链接】Python-100-Days
Python - 100天从新手到大师
本指南基于 Python-100-Days 项目 Day21-30/27.Python操作PDF文件.md 展开,系统讲解如何用
PyPDF2从 PDF 中提取文本、旋转与叠加页面、加密文档、批量添加水印,以及如何用reportlab从零创建 PDF。读完本文,你将掌握 Python 处理 PDF 的两大核心链路——"读"(解析既有文档)与"写"(生成新文档),并能直接将其迁移到报表导出、合同批量处理等真实业务场景。
PDF 是 Portable Document Format(便携式文档格式)的缩写,通常使用.pdf作为扩展名。在日常开发工作中,最容易遇到的两个任务就是从 PDF 中读取文本内容,以及用已有的内容生成 PDF 文档。前者对应文档解析与数据提取,后者对应报表生成与文档输出。
从 PDF 中提取文本:PyPDF2 入门
在 Python 中,可以使用三方库PyPDF2来读取 PDF 文件,安装命令如下:
pip install PyPDF2需要说明的是,PyPDF2无法从 PDF 文档中提取图像、图表或其他媒体对象,它只负责提取文本,并将结果作为 Python 字符串返回。因此它非常适合做正文内容的批量抓取,但不适合做版面级的信息还原。
读取并打印全部文本的代码如下:
import PyPDF2 reader = PyPDF2.PdfReader('test.pdf') for page in reader.pages: print(page.extract_text())核心 API 要点:
PyPDF2.PdfReader(文件路径):创建 PDF 读取器对象,对应旧版 API 中的PdfFileReader;reader.pages:一个可迭代的页面集合,每个元素是一个页面对象(PageObject);page.extract_text():提取当前页的文本,返回字符串。
提取失败的场景与替代方案
PyPDF2并不是什么样的 PDF 文档都能成功提取文字,尤其是扫描版 PDF(本质是图片)和中文内容排版复杂的文档,提取效果可能很差甚至返回空字符串,这个问题目前并没有特别通用的解决方法。针对这类场景,可以采用下面的替代方案:
- 使用三方的命令行工具
pdfminer.six,它对文本布局和编码的处理更为精细,安装与用法如下:
pip install pdfminer.six pdf2text.py test.pdf对于扫描版 PDF,可先用 OCR(光学字符识别)库将其转为可检索文本,再交给正则或自然语言处理流程使用(本项目 Day61-65 之后的爬虫与数据采集课程中也有类似的处理思路)。
若只是验证提取效果,仓库内已有一份真实的加密 PDF 样本 公开课/第05次公开课-算法入门系列1-周而复始/code/Python_Tricks_encrypted.pdf,可用来测试读取与解密流程;若文件带密码,读取前需调用
reader.decrypt('密码')完成解锁。
旋转和叠加页面:PageObject 的变换能力
通过创建PdfReader对象读取 PDF 文档后,其页面对象支持多种版面变换操作。以"将偶数页顺时针旋转 90 度、奇数页逆时针旋转 90 度"为例:
reader = PyPDF2.PdfReader('XGBoost.pdf') writer = PyPDF2.PdfWriter() for no, page in enumerate(reader.pages): if no % 2 == 0: new_page = page.rotate(-90) else: new_page = page.rotate(90) writer.add_page(new_page) with open('temp.pdf', 'wb') as file_obj: writer.write(file_obj)这里用到了三类对象与方法,需要辨析清楚:
PdfReader:负责解析输入文档,reader.pages提供页面序列;PageObject.rotate(角度):返回旋转后的新页面对象,角度为顺时针方向的角度值(负数表示逆时针),该方法对应旧版 API 中的rotateClockwise与rotateCounterClockwise两个方法,新版本统一为带符号的角度参数;PdfWriter:负责汇集页面并写出新文档,add_page()把页面加入输出队列,write(文件对象)将结果落盘。
enumerate(reader.pages)用于同时取得页序号no与页面对象本身,适合做"奇偶页差异化处理"这类按页号分支的逻辑。整个流程遵循"读取 → 变换 → 写出"三段式结构,这是 PyPDF2 处理任务的通用骨架。
加密 PDF 文件:给文档设置统一口令
使用PyPDF2中的PdfWriter对象可以为 PDF 文档加密。如果业务上需要给一系列 PDF 文档设置统一的访问口令(例如批量分发内部资料时统一加密码),用 Python 程序批量处理会非常方便:
import PyPDF2 reader = PyPDF2.PdfReader('XGBoost.pdf') writer = PyPDF2.PdfWriter() for page in reader.pages: writer.add_page(page) writer.encrypt('foobared') with open('temp.pdf', 'wb') as file_obj: writer.write(file_obj)要点说明:
writer.encrypt('foobared')在写出前对全部页面施加用户口令,打开加密后的temp.pdf时需要输入密码foobared;- 加密发生在
write()之前,先完成页面收集与加密设置,再统一落盘; - 批量场景下,只需把"读取文件路径、密码"做成参数循环即可,例如遍历一个目录下的所有 PDF 并应用同一密码。
从安全角度补充一点:encrypt设置的是打开文档所需的用户口令,属于访问控制层防护,适合防止他人随意打开,但不应视为高强度内容加密手段。
批量添加水印:mergePage 的页面叠加
PageObject还有一个名为merge_page的方法(对应旧版mergePage),可以将两个 PDF 页面叠加到同一画布上。借助它,很容易实现给 PDF 批量添加水印的功能。
具体思路是:先准备一个提供水印页面的 PDF 文件(例如用 Word 或reportlab生成一张只含公司名称、半透明文字的单页 PDF),读取其中第 1 页作为水印模板,然后遍历目标文档的每一页,将水印页与原始页合并,最后写出新文件:
reader1 = PyPDF2.PdfReader('XGBoost.pdf') reader2 = PyPDF2.PdfReader('watermark.pdf') writer = PyPDF2.PdfWriter() watermark_page = reader2.pages[0] for page in reader1.pages: page.merge_page(watermark_page) writer.add_page(page) with open('temp.pdf', 'wb') as file_obj: writer.write(file_obj)这段代码的关键在于:
reader2.pages[0]取出水印页作为叠加模板,水印 PDF 中建议只放置水印内容,方便复用;page.merge_page(watermark_page)会把水印页内容绘制到当前页之上,两个页面的内容同时保留;- 水印页通常制作为透明背景 + 浅色文字,这样叠加后不会遮挡正文阅读。
如果想更精细,还可以让奇数页和偶数页使用不同的水印(例如偶数页加"机密"、奇数页加"草稿"),做法与上一节的旋转示例一致——用enumerate(reader1.pages)按页号选择不同水印页再合并即可,留给大家自行实践。
创建 PDF 文件:reportlab 从零绘制文档
创建全新的 PDF 文档需要三方库reportlab的支持,安装方式如下:
pip install reportlabreportlab提供了底层canvas绘图 API,可以把页面当成一张画布,自由绘制图片、文本并控制字体与颜色。下面是一个完整示例,展示了 A4 页面上的绘图、注册中文字体、写字与保存四个核心步骤:
from reportlab.lib.pagesizes import A4 from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfgen import canvas pdf_canvas = canvas.Canvas('resources/demo.pdf', pagesize=A4) width, height = A4 # 绘图 image = canvas.ImageReader('resources/guido.jpg') pdf_canvas.drawImage(image, 20, height - 395, 250, 375) # 显示当前页 pdf_canvas.showPage() # 注册字体文件 pdfmetrics.registerFont(TTFont('Font1', 'resources/fonts/Vera.ttf')) pdfmetrics.registerFont(TTFont('Font2', 'resources/fonts/青呱石头体.ttf')) # 写字 pdf_canvas.setFont('Font2', 40) pdf_canvas.setFillColorRGB(0.9, 0.5, 0.3, 1) pdf_canvas.drawString(width // 2 - 120, height // 2, '你好,世界!') pdf_canvas.setFont('Font1', 40) pdf_canvas.setFillColorRGB(0, 1, 0, 0.5) pdf_canvas.rotate(18) pdf_canvas.drawString(250, 250, 'hello, world!') # 保存 pdf_canvas.save()逐段拆解这段代码背后的 API 语义:
canvas.Canvas('resources/demo.pdf', pagesize=A4):创建画布并指定输出文件与页面尺寸,A4来自reportlab.lib.pagesizes,解包后的width, height即页面的宽高点数;canvas.ImageReader('resources/guido.jpg'):加载图片源,drawImage(image, x, y, w, h)以(x, y)为左下角、宽w高h绘制图片;height - 395表示从页面顶部向下偏移定位;pdf_canvas.showPage():结束当前页并开始新的一页,绘图与写字是两页,先图后字;pdfmetrics.registerFont(TTFont('Font1', '字体文件路径')):注册 TrueType 字体,第一个参数是字体别名,后续setFont('Font2', 40)用它引用;注意中文字体(示例中的青呱石头体)用于输出中文,西文字体(Vera.ttf)用于输出英文;setFillColorRGB(r, g, b, a):设置文字填充色,支持第四个透明度参数(如0.9, 0.5, 0.3, 1为不透明橙色、0, 1, 0, 0.5为半透明绿色);drawString(x, y, 文本):以基线位置写入文本;rotate(18)会旋转后续绘图坐标系,因此第二行英文是旋转 18 度后写出的;pdf_canvas.save():保存画布并落盘,所有内容在此之前都要绘制完毕。
如果手头没有示例字体文件,也可以使用仓库内已有的中文字体 Day66-80/code/res/SimHei.ttf 和图片 Day66-80/code/res/guido.jpg 替换路径,验证字体注册与图片绘制逻辑——字体注册方式与字体文件名无关,只要路径指向真实的 TTF 文件即可。
实战延伸:用 reportlab 导出 PDF 报表
reportlab的能力不止于本地生成文件,还可以与 Web 框架结合,把 PDF 作为 HTTP 响应直接输出给浏览器。在项目 Day46-60/50.制作报表.md 的"导出 PDF 报表"一节中,展示了如何在 Django 视图中用reportlab生成 PDF 并指定application/pdf的 MIME 类型:
def export_pdf(request: HttpRequest) -> HttpResponse: buffer = io.BytesIO() pdf = canvas.Canvas(buffer) pdf.setFont("Helvetica", 80) pdf.setFillColorRGB(0.2, 0.5, 0.3) pdf.drawString(100, 550, 'hello, world!') pdf.showPage() pdf.save() resp = HttpResponse(buffer.getvalue(), content_type='application/pdf') resp['content-disposition'] = 'inline; filename="demo.pdf"' return resp与本地写文件相比,这里的关键差异是:canvas.Canvas(buffer)把输出目标从文件路径换成io.BytesIO内存缓冲,save()后通过buffer.getvalue()取出二进制内容,交给HttpResponse并设置content_type='application/pdf';content-disposition: inline表示浏览器内联预览而不是强制下载。这正是 README.md 中"使用reportlab生成 PDF 报表"一节的落地用法,也说明本文介绍的canvas绘图 API 可以直接复用到后端报表系统。
总结
围绕 PDF 的"读"与"写"两个方向,本文覆盖了PyPDF2与reportlab两条工具链的完整用法:
| 任务 | 库 | 核心对象/方法 |
|---|---|---|
| 提取文本 | PyPDF2 / pdfminer.six | PdfReader、page.extract_text() |
| 旋转页面 | PyPDF2 | PageObject.rotate() |
| 加密文档 | PyPDF2 | PdfWriter.encrypt() |
| 叠加页面/水印 | PyPDF2 | PageObject.merge_page() |
| 创建 PDF | reportlab | canvas.Canvas、drawImage、drawString |
在掌握上述基础后,合并多个 PDF、批量加水印、统一加密码、导出 PDF 报表等工作都可以用几十行 Python 代码实现。更复杂的中文排版与表格布局需求,可以进一步研究reportlab的platypus布局引擎,或结合 Day46-60/50.制作报表.md 中的报表导出实践,将 PDF 生成能力集成到真实项目中。
- 文档
- 教程
【免费下载链接】Python-100-Days
Python - 100天从新手到大师
相关推荐
Python 解析 HTML 页面实战:XPath 与 CSS 选择器全解(Python-100-Days 第 62 天)
Python 解析 HTML 页面实战:XPath 与 CSS 选择器全解(Python 100 Days 第 62 天) 在前面的课程中,我们已经掌握了用 r
文档教程QQ空间说说一键导出:GetQzonehistory 把历史说说、评论和配图备份成本地 Excel
QQ空间说说一键导出:GetQzonehistory 把历史说说、评论和配图备份成本地 Excel QQ空间没有官方导出入口,老说说只能逐条截图。GetQzon
网页爬虫数据分析Python-100-Days 第 25 讲:openpyxl 读写 Excel 文件完整实战指南
Python 100 Days 第 25 讲:openpyxl 读写 Excel 文件完整实战指南 openpyxl 是 Python 生态中最流行的 xlsx
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考