夜雨聆风学习资料网

ARTICLE · 1006561

三份PDF合并成一个,我用30行脚本几秒搞定

三份PDF合并成一个,我用30行脚本几秒搞定

月末把三个部门的PDF合同扫描件合成一整本,是我每个月都要做的活。

以前我用在线网站。把文件拖上去,转圈,下载。步骤不难,但每个文件都要过一遍外国服务器。有回客户文件里夹着报价单,我盯着"已上传"三个字想了十分钟——这东西到底传哪儿去了?没人能回答。

后来我换了全套本地的写法。30行脚本,合并、拆分、抽页一个脚本全包,文件不出我电脑。今天把这个脚本掰开讲清楚,你照着抄就能用。

先说结论

PDF合并、拆分、抽页这套操作,别用在线网站。本地用 Python 一个库就够:pypdf。

装它只要一行:

pip3 install pypdf

装完写脚本就能跑。不联网、不上传、不排队。

场景一:把几个PDF合成一个

这是最常用的。我拿三个测试文件演示一下完整效果。

from pypdf import PdfReader, PdfWriter  files = ["引1.pdf", "引2.pdf", "引3.pdf"]  # 要合并的文件,按这个顺序 out = "合并.pdf"  w = PdfWriter() for name in files:     r = PdfReader(name)     for p in r.pages:         w.add_page(p)  with open(out, "wb") as f:     w.write(f)  print("合并完成,共", len(PdfReader(out).pages), "页")

跑完我的机器上是这么输出的:

合并完成,共 3 页

三个文件合成一本,3页一个不少。

注意一个坑:合并的顺序,就是列表里文件的顺序。上面那份我一开始没排序,引3排到了引2前面,因为文件名按字典序排。真要按章节来,就把文件名在列表里写成想要的顺序,或者用 sorted() 加自己定的规则排好再进列表。

场景二:把一整本PDF拆回单页

这招常在要"只发某一页"的时候用。比如整本标书要拆出技术负责人那一页盖章再传。

from pypdf import PdfReader, PdfWriter  r = PdfReader("合并.pdf") for i, page in enumerate(r.pages, 1):     w = PdfWriter()     w.add_page(page)     with open(f"第{i:02d}页.pdf"  , "wb") as f:  # 输出形如 第01页.pdf、第02页.pdf         w.write(f)

跑完目录里会多出一堆单页文件:

第01页.pdf  1 页 第02页.pdf  1 页 第03页.pdf  1 页

i:02d的意思是页码补成两位数——01、02,这样按文件名排序时不会出现"第10页排在第2页前面"这种乱序。

场景三:只要其中某几页

比如一本20页的PDF,我只要第1页和第3页做个附件。

from pypdf import PdfReader, PdfWriter  r = PdfReader("合并.pdf") w = PdfWriter() for i in [1, 3]:            # 注意这儿的1、3是"第1页"的意思     w.add_page(r.pages[i - 1])   # 页码从0数起,所以要减1 with open("抽取_第1和3页.pdf", "wb") as f:     w.write(f)

我实测输出的文件是2页,正好只留了要的两页。

全部三个场景拼一个脚本

上面是拆开讲,实际用的时候我拼成一个大脚本,一个命令全搞定:

import glob, os from pypdf import PdfReader, PdfWriter  # 1) 合并 files = sorted(glob.glob("*.pdf")) w = PdfWriter() for name in files:     r = PdfReader(name)     for p in r.pages:         w.add_page(p) with open("合并.pdf", "wb") as f:     w.write(f) print("合并完成", len(PdfReader("合并.pdf").pages), "页")  # 2) 拆单页 for i, page in enumerate(PdfReader("合并.pdf").pages, 1):     w = PdfWriter()     w.add_page(page)     with open(f"分_{i:02d}.pdf", "wb") as f:         w.write(f)  # 3) 抽页 w = PdfWriter() for i in [1, 3]:     w.add_page(PdfReader("合并.pdf").pages[i - 1]) with open("抽_1和3.pdf", "wb") as f:     w.write(f)

三个步骤共用一个合并.pdf中间文件,跑完桌面就齐了。

我踩过的三个坑

坑一:装错库。 网上教程有让装PyPDF2的,和pypdf是两拨维护者,一个库名拼法不对就报No module named。直接装pypdf,新版本就是它。这俩现在其实合并了,但认准pypdf最保险。

坑二:页码从0开始。 人眼看到的"第1页",在代码里是pages[0]。抽页时忘记减1,抽出来的永远错位一页。我把这点写进注释里防自己忘。

坑三:扫出来的"假PDF"。 有些扫描件其实是图片贴的,提取不了文字——这正常,pypdf只管页面处理,不管OCR。真要读里面的文字,得走OCR那套,那是另一篇文章的事,这里不展开。

文件一个没出我电脑

整套跑下来,所有PDF都在本地磁盘打转,没有一个字节离开电脑。对经常碰合同、报价、标书的人来说,这个"数据不出门"本身就是最大的好处。

你手上要是也有这种"每周都要合一次"的PDF活儿,直接把脚本第一块的files列表换成你的文件名就能用。跑一次,以后这半小时就省了。

— 硅基聊斋 · charlotty —

互动话题:你还用在线网站处理PDF吗?评论区聊聊。

相关学习资料