朋友丢来一个文件夹,30 份 PDF,要求三件事:① 每一页都拆成独立的单页 PDF;② 把每份的首页抽出来,按原顺序合成一本"封面合集";③ 全部页面统一盖上 CONFIDENTIAL 水印,且不能挡住正文。
如果手工做,光是拆分就要点 62 次"另存为"——30 份文件一共 62 页。更麻烦的是,只要文档有增删,前面所有操作全部报废,得从头再来一遍。
这篇文章记录我用 WorkBuddy 把这件事批量化的全过程。
先说清楚数据来源:为了避免涉及任何真实文档,我用脚本现场合成了 30 份样例 PDF(共 62 页),下面每一个数字都是真实跑出来的,不是估算。

把任务拆开看,其实每一步都"不难",但都"很烦":
只要还是"手工点鼠标",这四件事就没有一件能一次性做对。
很多人以为 PDF 是一张张"图片拼起来的",改不了。其实 PDF 的结构是:一个容器,里面装着若干"页面对象",每个页面对象再引用自己的资源(字体、图像等)。
理解这一点,上面三个需求就变成同一件事了:
换句话说,全程都是对"页面对象"做搬运,根本不需要重排内容。这也是它快的原因。
我把整条流水线跑了一遍,处理前后的关键指标如下(全部实测):

注意"总页数 62 → 62"这一行:内容零丢失。这是批量处理最该盯住的指标——快没有意义,准才有意义。
真正干活的部分不到 20 行。这里只保留 1 个代码块,方便你直接抄:
import os
from pypdf import PdfReader, PdfWriter
SRC, SPLIT, OUT = "./src", "./split", "./out"
# 1) 拆分:每个源 PDF 的每一页 -> 独立单页文件
n = 0
for sp in sorted(os.listdir(SRC)):
r = PdfReader(os.path.join(SRC, sp))
for k, page in enumerate(r.pages):
w = PdfWriter(); w.add_page(page)
w.write(open(f"{SPLIT}/{sp[:-4]}_p{k+1}.pdf", "wb")); n += 1
# 2) 合并:全量合集(要按规则筛选就把 add_page 换个条件即可)
full = PdfWriter()
for sp in sorted(os.listdir(SRC)):
for p in PdfReader(os.path.join(SRC, sp)).pages:
full.add_page(p)
full.write(open(f"{OUT}/full_collection.pdf", "wb"))
# 3) 水印:把"只有文字、无底色"的透明页叠加到每一页
wm = PdfReader(f"{OUT}/_watermark.pdf").pages[0]
w2 = PdfWriter()
for p in PdfReader(f"{OUT}/full_collection.pdf").pages:
p.merge_page(wm) # 透明叠加,正文不会被盖住
w2.add_page(p)
w2.write(open(f"{OUT}/full_watermarked.pdf", "wb"))关键就一个 API:page.merge_page(wm)——它把水印页作为图层叠在目标页上。
上面代码里的 _watermark.pdf 是一个"只有文字、没有底色"的透明页面。对比一下同一页加水印前后的效果:

可以看到,水印是斜向、半透明的,正文依然清晰可读——这正是"透明叠加"的意义。如果你拿一张带白色底色的图片当水印页,叠上去会把整页正文盖住,这是最常见的翻车点。
坑 1:手写水印 PDF 时,**%PDF** 里的百分号与格式化占位符冲突。
我本来想用 "...%d..." % length 拼 PDF 字节流,结果直接抛 ValueError: unsupported format character 'P'。原因就是 %PDF-1.4 里的 %P 被当成了格式符。
解法:不要用
%格式化拼 PDF 字节,改成字节串拼接;或者把百分号全部转义成%%。
坑 2:水印页只要带底色,就会把正文盖住。
PDF 叠加是"整页图层覆盖",不会自动抠透明。所以水印页必须只有文字、没有背景填充,否则等于拿一张白纸糊在正文上。
解法:水印页面只画文字(
BT ... Tj ET),完全不画背景矩形。
坑 3:中文水印用标准字体渲染不出来,还会编码报错。
我用 Helvetica 这种 PDF 内置标准字体画水印,结果既没有中文字形,又抛 UnicodeEncodeError: 'latin-1' codec。
解法:内置标准字体只支持 ASCII,水印文字用
CONFIDENTIAL这类英文;要中文水印就得嵌入 CJK 字体。
不用自己写代码,也可以直接把需求讲清楚让 WorkBuddy 生成脚本。下面这段可以直接抄:
我有一个文件夹,里面有若干 PDF。请写一个 Python 脚本,用 pypdf 依次完成三件事: ① 把每个 PDF 的每一页拆成独立的单页 PDF,输出到 ./split,命名规则为 原名_p页码.pdf; ② 把每个源文件的第一页按文件名顺序合并成一本 ./out/cover_collection.pdf,再把所有页合并成 ./out/full_collection.pdf; ③ 生成一个只有文字、无底色的透明水印页(文字用斜向的 CONFIDENTIAL),用 page.merge_page() 叠加到全量合集的每一页,输出 ./out/full_watermarked.pdf。 要求:只依赖 pypdf 和标准库;打印每一步的耗时、产出文件数和总页数;处理前后页数必须一致。
把它交给 WorkBuddy 跑一遍,你得到的是一套可重入、可复跑的脚本——源文件更新了,重新跑一次就行,不用再点 62 次鼠标。
批处理的真正价值,不在于"快",而在于可重复。
手工做的 62 次点击,只要有一次手滑,错的就是整批文件,而且你很难发现错在哪一页。而流水线的好处是:每个数字都能被核对——页数的增减、体积的变化、耗时的量级,任何一个异常都会立刻暴露。
对我这种"宁可多花 10 分钟写脚本,也不愿点 62 次鼠标"的人来说,这就是最省心的解法。
作者所处行业与岗位:信息技术服务业 · 数据与效率工具工程师。
本文所有 PDF 均为脚本合成的测试数据,不含任何真实业务信息;数字均为本机实测,可复现。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。