☰
Python 操作 PDF 文件实战:文本提取、页面旋转、加密与水印(Python-100-Days 第 27 天)
2026/10/1 9:36:12 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】Python-100-Days

Python - 100天从新手到大师

项目地址:https://gitcode.com/GitHub_Trending/py/Python-100-Days
点击查看免费下载

本指南基于 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(本质是图片)和中文内容排版复杂的文档,提取效果可能很差甚至返回空字符串,这个问题目前并没有特别通用的解决方法。针对这类场景,可以采用下面的替代方案:

  1. 使用三方的命令行工具pdfminer.six,它对文本布局和编码的处理更为精细,安装与用法如下:
pip install pdfminer.six pdf2text.py test.pdf
  1. 对于扫描版 PDF,可先用 OCR(光学字符识别)库将其转为可检索文本,再交给正则或自然语言处理流程使用(本项目 Day61-65 之后的爬虫与数据采集课程中也有类似的处理思路)。

  2. 若只是验证提取效果,仓库内已有一份真实的加密 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 reportlab

reportlab提供了底层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.sixPdfReader、page.extract_text()
旋转页面PyPDF2PageObject.rotate()
加密文档PyPDF2PdfWriter.encrypt()
叠加页面/水印PyPDF2PageObject.merge_page()
创建 PDFreportlabcanvas.Canvas、drawImage、drawString

在掌握上述基础后,合并多个 PDF、批量加水印、统一加密码、导出 PDF 报表等工作都可以用几十行 Python 代码实现。更复杂的中文排版与表格布局需求,可以进一步研究reportlab的platypus布局引擎,或结合 Day46-60/50.制作报表.md 中的报表导出实践,将 PDF 生成能力集成到真实项目中。

  • 文档
  • 教程

【免费下载链接】Python-100-Days

Python - 100天从新手到大师

项目地址:https://gitcode.com/GitHub_Trending/py/Python-100-Days
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询