PDF 处理合并、拆分、加水印一条龙本文基于 Python 3.9涉及库pypdf、pdfplumber、reportlab。阅读时间约 10 分钟。安装依赖pip install pypdf pdfplumber reportlab财务部的不可能任务周四下午财务部的小李抱着一摞文件愁眉苦脸地找到阿明。“阿明江湖救急”“咋了”“老板要我把这 100 份合同扫描件合并成一个 PDF还要在每页加上’内部资料’的水印。明天上午要。”阿明看着那 100 个 PDF 文件倒吸一口凉气“你手动弄”我试过了下载了三个免费软件要么限制页数要么导出带软件自己的水印要么要充会员……小李快哭了。阿明想起老张的话“Python 免费、无限量、还没广告。”“给我我试试。”第一步认识pypdf——PDF 的瑞士军刀阿明抱着电脑找到老张。老张扫了一眼需求“合并、加水印pypdf搞定。”“以前叫PyPDF2现在升级叫pypdf了API 更简洁。”pipinstallpypdf最简示例读取 PDF 信息frompypdfimportPdfReader readerPdfReader(合同_001.pdf)# PDF 基本信息print(f页数:{len(reader.pages)})print(f标题:{reader.metadata.title})print(f作者:{reader.metadata.author})# 读取第一页的文本textreader.pages[0].extract_text()print(f第一页内容:\n{text[:200]}...)# 只打印前 200 字“PdfReader就是’打开 PDF 看看里面有什么’。”第二步实战——合并多个 PDF“100 个合同合并成一个核心逻辑打开所有 PDF → 把每一页按顺序塞进一个新 PDF → 保存。”frompypdfimportPdfReader,PdfWriterfrompathlibimportPath# 合同文件夹contract_folderPath(./合同扫描件)output_file合并合同_2024年.pdf# 创建 PDF 写入器writerPdfWriter()# 获取所有 PDF 文件按文件名排序pdf_filessorted(contract_folder.glob(*.pdf))print(f 发现{len(pdf_files)}个 PDF 文件开始合并...)forpdf_pathinpdf_files:readerPdfReader(str(pdf_path))# 把这个 PDF 的所有页面添加到写入器forpageinreader.pages:writer.add_page(page)print(f ✅ 已合并:{pdf_path.name}({len(reader.pages)}页))# 保存合并后的文件withopen(output_file,wb)asf:writer.write(f)print(f\n 合并完成输出文件:{output_file})print(f 总页数:{len(writer.pages)}页)阿明运行完看着生成的合并合同_2024年.pdf惊呆了“这就……完了”“完了。100 个文件代码跑完不超过 3 秒。”“PdfWriter就像个’容器’你把各个 PDF 的页面一个个’倒’进去最后’打包’保存。”第三步加水印——给每页盖个章“合并完了还要加水印。”“水印分两种文字水印和图片水印。咱们先做文字水印。”生成水印 PDF“首先用reportlab生成一个只有水印的 PDF”fromreportlab.pdfgenimportcanvasfromreportlab.lib.pagesizesimportA4# 创建水印 PDFwatermark_path水印.pdfccanvas.Canvas(watermark_path,pagesizeA4)# 设置水印样式c.setFont(Helvetica-Bold,60)c.setFillColorRGB(0.8,0.8,0.8,alpha0.3)# 浅灰色半透明# 在页面中央画文字旋转 45 度c.saveState()c.translate(A4[0]/2,A4[1]/2)c.rotate(45)c.drawCentredString(0,0,内部资料)c.restoreState()c.save()print(f✅ 水印模板已生成:{watermark_path})“reportlab是个 PDF 生成库可以画图、写字、做表格。这里咱们只用它生成一个带’内部资料’字样的单页 PDF。”把水印盖到每一页“然后把水印’盖’到合并后的 PDF 每一页上”frompypdfimportPdfReader,PdfWriter# 读取原 PDF 和水印readerPdfReader(合并合同_2024年.pdf)watermark_readerPdfReader(水印.pdf)watermark_pagewatermark_reader.pages[0]writerPdfWriter()print(f 共{len(reader.pages)}页开始加水印...)fori,pageinenumerate(reader.pages):# 把水印页合并到当前页水印在下原页面在上page.merge_page(watermark_page)writer.add_page(page)if(i1)%100:print(f 已处理{i1}页...)# 保存output合并合同_带水印.pdfwithopen(output,wb)asf:writer.write(f)print(f\n 水印添加完成输出:{output})“merge_page就是’把两页叠在一起’。水印页在下原页面在上就实现了’盖章’效果。”阿明打开生成的 PDF每一页都有淡淡的内部资料水印满意地点头。第四步进阶——按页码拆分 PDF“阿明合并会了那拆分呢”“比如这个 500 页的合并合同老板只要看前 50 页你怎么拆出来”frompypdfimportPdfReader,PdfWriter readerPdfReader(合并合同_带水印.pdf)# 拆分提取第 1-50 页注意索引从 0 开始writerPdfWriter()foriinrange(0,50):# 第 1 页到第 50 页writer.add_page(reader.pages[i])withopen(合同_前50页.pdf,wb)asf:writer.write(f)print(✅ 已提取前 50 页)# 拆分提取第 51-100 页writerPdfWriter()foriinrange(50,100):writer.add_page(reader.pages[i])withopen(合同_51-100页.pdf,wb)asf:writer.write(f)print(✅ 已提取 51-100 页)“reader.pages[i]就是取第 i 页然后写到新的 PDF 里。”“你还可以按’每个原始文件拆一页’、‘每 10 页拆一个文件’规则自己定。”第五步pdfplumber——精确提取文字和表格“阿明如果 PDF 里是扫描件图片上面的代码能提取文字吗”“不能。扫描件本质是图片需要 OCR光学字符识别。”“但如果是文字型 PDF比如 Word 导出的pdfplumber可以精确提取文字和表格。”pipinstallpdfplumber提取文字importpdfplumberwithpdfplumber.open(合同_001.pdf)aspdf:fori,pageinenumerate(pdf.pages):textpage.extract_text()print(f 第{i1}页 )print(text[:500])# 打印前 500 字print()提取表格超实用“很多报表 PDF 里有表格手动复制粘贴格式会乱。pdfplumber可以直接提取成结构化数据”importpdfplumberimportpandasaspdwithpdfplumber.open(财务报表.pdf)aspdf:# 提取第一页的表格tablespdf.pages[0].extract_tables()iftables:# 第一个表格转成 DataFramedfpd.DataFrame(tables[0][1:],columnstables[0][0])print(df)# 保存为 Exceldf.to_excel(财务报表_提取.xlsx,indexFalse)print(✅ 表格已提取并保存为 Excel)“extract_tables()返回一个列表每个元素是一个表格二维数组。第一行通常是表头后面是数据。”阿明瞪大眼睛“从 PDF 里抠表格以前我手动复制要半小时还老对不齐……”代码 5 行跑完 2 秒。老张笑。踩坑提醒PDF 操作的坑坑 1加密 PDF“有些 PDF 加了密码PdfReader直接读会报错。”frompypdfimportPdfReader readerPdfReader(加密文件.pdf)ifreader.is_encrypted:reader.decrypt(密码)# 解密坑 2中文水印乱码“reportlab默认字体不支持中文写中文会乱码或报错。”“解决办法指定中文字体。”fromreportlab.pdfbaseimportpdfmetricsfromreportlab.pdfbase.ttfontsimportTTFont# 注册中文字体需要系统里有这个字体文件pdfmetrics.registerFont(TTFont(SimSun,simsun.ttc))ccanvas.Canvas(水印.pdf)c.setFont(SimSun,60)# 使用宋体c.drawString(100,500,内部资料)c.save()“Windows 自带simsun.ttc宋体Mac 可以用/System/Library/Fonts/PingFang.ttc。”坑 3合并后文件超大“100 个扫描件合并后可能有几百 MB邮件发不出去。”“解决办法压缩 PDF。可以用pypdf的压缩功能或者用在线工具。”frompypdfimportPdfReader,PdfWriter readerPdfReader(大文件.pdf)writerPdfWriter()forpageinreader.pages:# 压缩图片page.compress_content_streams()# 压缩内容流writer.add_page(page)withopen(压缩后.pdf,wb)asf:writer.write(f)坑 4旋转页面“有些扫描件是横着拍的PDF 页面方向不对。”# 把第 1 页旋转 90 度pagereader.pages[0]page.rotate(90)writer.add_page(page)一句话总结小李来取文件时阿明已经把合并合同_带水印.pdf发到了她邮箱。100 个文件合并 水印你怎么做到的小李震惊。阿明故作深沉“Python。”老张在旁边补刀“PDF 操作就像拼乐高——合并是把几盒乐高拼成一个大模型拆分是把大模型按步骤拆回去水印就是给每个零件盖上’这是我的’印章。Python 免费、无限量、还没广告。”扩展思考PDF 自动化的场景还有很多发票归档每月把电子发票按月份合并统一加水印存档报告生成从数据库取数据自动生成 PDF 报告用reportlab合同比对提取两份合同的文字用 Python 对比差异批量盖章给几百份文件每页盖电子章PDF 转图片把 PDF 每页转成图片方便预览“核心逻辑都一样读取 → 处理 → 输出。”下集预告下一篇阿明要帮市场部抓取竞品价格——每天打开 10 个网页手动复制价格累到眼花。老张教他requestsBeautifulSoup让 Python 每天去看网页变了就告诉你。记住PDF 操作的核心是’页面’——合并是攒页面拆分是挑页面水印是叠页面。把页面玩明白了PDF 就玩明白了。你工作中遇到过什么 PDF 相关的折磨合并拆分提取表格还是别的欢迎在评论区吐槽。