首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >#WorkBuddy# 30 份 PDF 拆分 / 合并 / 加水印:1.86 秒跑完一条批处理流水线

#WorkBuddy# 30 份 PDF 拆分 / 合并 / 加水印:1.86 秒跑完一条批处理流水线

原创
作者头像
用户12784192
发布于 2026-10-06 09:36:34
发布于 2026-10-06 09:36:34
680
举报

朋友丢来一个文件夹,30 份 PDF,要求三件事:① 每一页都拆成独立的单页 PDF;② 把每份的首页抽出来,按原顺序合成一本"封面合集";③ 全部页面统一盖上 CONFIDENTIAL 水印,且不能挡住正文。

如果手工做,光是拆分就要点 62 次"另存为"——30 份文件一共 62 页。更麻烦的是,只要文档有增删,前面所有操作全部报废,得从头再来一遍。

这篇文章记录我用 WorkBuddy 把这件事批量化的全过程。

先说清楚数据来源:为了避免涉及任何真实文档,我用脚本现场合成了 30 份样例 PDF(共 62 页),下面每一个数字都是真实跑出来的,不是估算。

一、手工做到底卡在哪

把任务拆开看,其实每一步都"不难",但都"很烦":

  • 拆分:每一页另存为独立文件,62 页就是 62 次重复操作,而且文件名还得自己想规则。
  • 合并:要从 30 份文件里各取第一页,顺序不能乱,页码还不能错。
  • 水印:要逐页叠加,而且必须保证正文清晰可读——水印盖住内容就白干了。
  • 可重入:一旦源文件有增删,上面三步全部推倒重来。

只要还是"手工点鼠标",这四件事就没有一件能一次性做对。

二、根因:PDF 本质是可以"拆开重组"的

很多人以为 PDF 是一张张"图片拼起来的",改不了。其实 PDF 的结构是:一个容器,里面装着若干"页面对象",每个页面对象再引用自己的资源(字体、图像等)。

理解这一点,上面三个需求就变成同一件事了:

  • 拆分 = 把页面对象单独装进新容器;
  • 合并 = 把多个容器里的页面对象,按指定顺序装进同一个新容器;
  • 加水印 = 把"水印页面"作为一个图层,叠加到每一个页面对象上。

换句话说,全程都是对"页面对象"做搬运,根本不需要重排内容。这也是它快的原因。

三、真实运行数据

我把整条流水线跑了一遍,处理前后的关键指标如下(全部实测):

  • 源文件:30 份,共 62 页;
  • 拆分产出:62 个单页 PDF,耗时 0.23 s;
  • 合并产出:封面合集 30 页 + 全量合集 62 页,耗时 0.12 s;
  • 水印:62 页全部盖章,耗时 0.05 s,体积只增加 0.02 MB;
  • 端到端总耗时:1.86 秒。

注意"总页数 62 → 62"这一行:内容零丢失。这是批量处理最该盯住的指标——快没有意义,准才有意义。

四、核心代码(可直接复制)

真正干活的部分不到 20 行。这里只保留 1 个代码块,方便你直接抄:

代码语言:python
复制
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 的提示词模板

不用自己写代码,也可以直接把需求讲清楚让 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 删除。

目录
  • 一、手工做到底卡在哪
  • 二、根因:PDF 本质是可以"拆开重组"的
  • 三、真实运行数据
  • 四、核心代码(可直接复制)
  • 五、水印这一步的真实效果
  • 六、三个真实踩过的坑
  • 七、把这件事交给 WorkBuddy 的提示词模板
  • 八、写在最后
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档