Python 合并 PDF 文件(批量处理方法)
作者:互联网
2026-04-09
周一早上九点,你的邮箱里躺着十几封邮件,每封都带了一个PDF附件——上周会议的签到表、各小组的报告、财务的报销凭证。领导在微信群里发了一条消息:“把这些文件合并成一个,发到公司群里。”
你打开第一个PDF,又打开第二个,鼠标拖来拖去,发现Adobe Acrobat弹出一个窗口:“您的试用期已结束。”同事推荐了在线合并工具,你上传完所有文件,等待进度条走到100%,页面弹出五个字:“请付费解锁。”
这不是你的错。PDF合并这件事看起来简单,真上手才发现处处是坑。在线工具要么限制页数,要么加水印,要么悄悄把你的文件传到不明服务器。桌面软件不是收费就是臃肿得像个航空母舰。
你需要的其实很简单:一个能批量处理、不用花钱、跑起来就完事的工具。Python刚好能做到,而且代码少到你不用是程序员也能看懂。
先把你需要的工具装好
Python合并PDF这件事,全靠一个叫PyPDF2的库。它专门用来处理PDF,能读、能写、能合并、能拆分。
安装就一行命令:
pip install PyPDF2
如果你用的是Python 3,可能需要安装PyPDF2的升级版PyPDF4,用法几乎一样。这里用PyPDF2演示,兼容性最好。
装完之后,打开你的代码编辑器,新建一个Python文件,就叫merge_pdf.py。
最简单的合并:把所有PDF放在一起
假设你的桌面上有一个文件夹叫“待合并”,里面放了三个PDF文件:报告1.pdf、报告2.pdf、报告3.pdf。你想把它们按顺序拼成一个总报告。
代码长这样:
import PyPDF2
import os
# 创建一个空的PDF写入器
merger = PyPDF2.PdfMerger()
# 定义文件夹路径
folder_path = r"C:Users你的用户名Desktop待合并"
# 获取文件夹里所有PDF文件,并按文件名排序
pdf_files = [f for f in os.listdir(folder_path) if f.endswith('.pdf')]
pdf_files.sort()
# 逐个添加到合并器
for pdf in pdf_files:
full_path = os.path.join(folder_path, pdf)
merger.append(full_path)
# 写出合并后的文件
output_path = os.path.join(folder_path, "合并结果.pdf")
merger.write(output_path)
merger.close()
print(f"合并完成,文件保存在:{output_path}")
跑完这段代码,你会发现“待合并”文件夹里多了一个“合并结果.pdf”,里面按顺序包含了所有文件的内容。
这段代码的逻辑很简单:PyPDF2.PdfMerger()创建了一个空容器,相当于一张白纸。append()方法把每个PDF一页一页地贴上去,就像在打印机上叠放纸张。最后write()把结果存下来。
文件顺序乱了怎么办
上面的代码用了sort()来排序,但这只能保证按字母顺序排列。如果文件名是“报告1”、“报告10”、“报告2”,sort()会把“报告10”排在“报告2”前面,因为字符串比较是一个字符一个字符来的。
解决办法有两种。
第一种,给文件命名的时候用数字编号,比如001_报告、002_报告。这样排序就是按数字顺序,不会乱。
第二种,自己指定顺序:
# 手动指定顺序
pdf_files = ["签到表.pdf", "小组报告.pdf", "财务凭证.pdf"]
for pdf in pdf_files:
full_path = os.path.join(folder_path, pdf)
if os.path.exists(full_path):
merger.append(full_path)
else:
print(f"警告:找不到文件 {pdf}")
加一个判断,文件不存在的时候跳过并给出提示,防止程序崩溃。
只合并某些页面
有时候你不需要合并整个PDF,只需要其中的几页。比如一个20页的报告,你只要第3到第7页。
PyPDF2允许指定页码范围:
# 只合并第3到第7页(页码从0开始)
merger.append("报告.pdf", pages=(2, 7))
这里的pages参数接收一个元组,第一个数字是起始页(从0开始),第二个数字是结束页(不包含这一页)。所以(2, 7)表示第3页到第7页,一共5页。
如果你想单独挑几页,可以用另一种方式:
# 先读取文件
reader = PyPDF2.PdfReader("报告.pdf")
# 只取第1页、第3页、第5页
pages_to_take = [0, 2, 4]
for page_num in pages_to_take:
merger.append(reader.pages[page_num])
这种方式灵活度更高,你想怎么组合都行。
处理子文件夹里的PDF
真实的场景往往更复杂。你的文件可能分散在不同的子文件夹里:财务组一个文件夹、技术组一个文件夹、销售组一个文件夹。你想把这些文件夹里的所有PDF都合并到一起。
这时候需要用os.walk来遍历文件夹树:
import PyPDF2
import os
merger = PyPDF2.PdfMerger()
root_folder = r"C:Users你的用户名Desktop各部门报告"
# 遍历所有子文件夹
for folder_path, subfolders, files in os.walk(root_folder):
for file in files:
if file.endswith('.pdf'):
full_path = os.path.join(folder_path, file)
merger.append(full_path)
print(f"已添加:{full_path}")
merger.write(os.path.join(root_folder, "全部合并.pdf"))
merger.close()
这段代码会从根文件夹开始,一层一层往下找,把所有子文件夹里的PDF都翻出来合并。注意一个问题:这样合并的顺序是按os.walk的遍历顺序来的,不是按文件夹名字排序。如果你需要控制顺序,可以在添加之前先收集所有文件路径,排序之后再添加。
加上书签和目录
合并后的PDF如果页数太多,翻起来很痛苦。加上书签会友好很多。
你可以把每个文件的文件名作为书签插入:
merger = PyPDF2.PdfMerger()
for pdf in pdf_files:
# 记录当前总页数,作为书签的起始位置
merger.append(pdf)
# 获取文件名(不含扩展名)作为书签名称
bookmark_name = os.path.splitext(pdf)[0]
# 在最后添加的书签位置插入书签
# 这里需要先获取当前总页数,PyPDF2的add_bookmark方法需要知道页码
PyPDF2添加书签的API稍微有点绕。更简单的方式是换一个库——pypdf(PyPDF2的现代替代品),它的书签功能更直观。
安装pypdf:
pip install pypdf
然后用pypdf合并并添加书签:
from pypdf import PdfWriter, PdfReader
writer = PdfWriter()
for pdf in pdf_files:
reader = PdfReader(pdf)
# 记录添加之前的页数
start_page = len(writer.pages)
# 添加所有页面
for page in reader.pages:
writer.add_page(page)
# 添加书签,指向这个文件的第一页
bookmark_name = os.path.splitext(pdf)[0]
writer.add_outline_item(bookmark_name, start_page)
with open("带书签的合并文件.pdf", "wb") as f:
writer.write(f)
这样生成的PDF,左侧书签栏里会列出每个原文件的文件名,点击就能跳转到对应位置。
处理加密的PDF
有些PDF设置了打开密码,直接合并会报错。如果你知道密码,可以在读取时解密:
reader = PyPDF2.PdfReader("加密文件.pdf")
if reader.is_encrypted:
reader.decrypt("你的密码")
然后把reader的页面添加到merger里。
如果密码不知道,那基本无解。PDF的加密算法是工业级的,暴力破解不现实。你需要先找文件提供方要密码,或者用专门工具移除密码——但移除密码也需要先输入密码。
大文件合并时的内存问题
合并几十个PDF、几百页文件,PyPDF2跑起来很快。但如果合并上千页的大文件,或者一次合并上百个PDF,可能会遇到内存不足。
PyPDF2是把所有内容加载到内存里再写入的。解决方案是用pypdf的增量写入模式,或者换用PDFtk这个命令行工具——它在底层是用C++实现的,处理大文件比Python高效得多。
如果你坚持用Python,可以这样优化:分批次合并,先合并成几个中间文件,再把中间文件合并成最终结果。
# 先每10个文件合并成一个临时文件
batch_size = 10
temp_files = []
for i in range(0, len(pdf_files), batch_size):
batch = pdf_files[i:i+batch_size]
temp_writer = PyPDF2.PdfMerger()
for pdf in batch:
temp_writer.append(os.path.join(folder_path, pdf))
temp_name = f"temp_{i}.pdf"
temp_writer.write(temp_name)
temp_writer.close()
temp_files.append(temp_name)
# 再合并所有临时文件
final_merger = PyPDF2.PdfMerger()
for temp in temp_files:
final_merger.append(temp)
final_merger.write("最终合并.pdf")
final_merger.close()
# 清理临时文件
import os
for temp in temp_files:
os.remove(temp)
这种分段合并的方式,内存占用会小很多。
给代码加一个图形界面
代码写好了,但每次运行都要改文件夹路径,同事想用又不会Python。这时候可以加一个简单的图形界面,用tkinter(Python自带的GUI库)实现。
import tkinter as tk
from tkinter import filedialog, messagebox
import PyPDF2
import os
def merge_pdfs():
# 让用户选择文件夹
folder = filedialog.askdirectory()
if not folder:
return
# 获取所有PDF文件
pdf_files = [f for f in os.listdir(folder) if f.lower().endswith('.pdf')]
if not pdf_files:
messagebox.showwarning("提示", "该文件夹中没有PDF文件")
return
pdf_files.sort()
# 合并
merger = PyPDF2.PdfMerger()
for pdf in pdf_files:
full_path = os.path.join(folder, pdf)
merger.append(full_path)
output_path = os.path.join(folder, "合并结果.pdf")
merger.write(output_path)
merger.close()
messagebox.showinfo("完成", f"合并完成!n文件保存在:{output_path}")
# 创建窗口
root = tk.Tk()
root.title("PDF合并工具")
root.geometry("300x150")
btn = tk.Button(root, text="选择文件夹并合并PDF", command=merge_pdfs, height=3, width=25)
btn.pack(expand=True)
root.mainloop()
保存为pdf_merger_gui.py,双击运行,会弹出一个窗口,点按钮选择文件夹,剩下的交给程序。你的同事双击就能用,不需要安装Python环境吗?还是需要的——不过你可以用pyinstaller打包成一个exe文件,发给谁都能用。
处理扫描件和图片型PDF
有一种PDF比较特殊:里面不是文字,而是扫描的图片。这类PDF合并的时候,上面所有方法都适用,因为PyPDF2处理的是页面对象,不管里面是文字还是图片。
但合并之后可能会遇到一个问题——文件特别大。图片型PDF本来就大,合并之后更大。如果需要压缩,可以用另外的库,比如pypdf的压缩功能,或者用img2pdf重新生成。
简单压缩的方法:
from pypdf import PdfWriter, PdfReader
reader = PdfReader("大文件.pdf")
writer = PdfWriter()
for page in reader.pages:
# 压缩页面内容
page.compress_content_streams()
writer.add_page(page)
with open("压缩后.pdf", "wb") as f:
writer.write(f)
这个压缩力度有限,但聊胜于无。真正想大幅压缩图片型PDF,需要用OCR或专门的PDF压缩工具。
遇到报错怎么办
合并过程中最常见的报错是“PdfReadError: EOF marker not found”。意思是PDF文件可能损坏或不完整。解决办法是在append之前先验证文件是否能正常读取:
def is_valid_pdf(filepath):
try:
with open(filepath, 'rb') as f:
reader = PyPDF2.PdfReader(f)
# 尝试获取页数,如果出错说明文件有问题
_ = len(reader.pages)
return True
except:
return False
# 只添加有效的PDF
for pdf in pdf_files:
full_path = os.path.join(folder_path, pdf)
if is_valid_pdf(full_path):
merger.append(full_path)
else:
print(f"跳过无效文件:{pdf}")
另一个常见报错是“Permission denied”,表示文件被其他程序打开(比如你在Adobe Acrobat里正看着这个文件)。关掉文件再运行一次就行。
把合并过程写成日志
如果你要合并的文件很多,想记录哪些成功了、哪些失败了,可以加一个日志功能:
import logging
logging.basicConfig(filename='merge_log.txt', level=logging.INFO,
format='%(asctime)s - %(message)s')
for pdf in pdf_files:
try:
full_path = os.path.join(folder_path, pdf)
merger.append(full_path)
logging.info(f"成功添加:{pdf}")
except Exception as e:
logging.error(f"添加失败:{pdf},错误信息:{str(e)}")
跑完之后打开merge_log.txt,一目了然。
回到那个周一的早晨
现在你手头有了一段能用的Python代码。你双击运行,三秒钟之后,“合并结果.pdf”出现在文件夹里。你把它发到群里,领导回了一个大拇指。
更重要的是,下次再有同事遇到同样的问题,你不用再解释“你试试那个在线工具、注意不要点广告、合并完记得检查顺序”——直接把代码扔过去,或者打包成exe发给他。
PDF合并这件事,Python帮你做了一次性投入、无限次复用的自动化。那些浪费在下载软件、比较付费方案、担心文件泄露上的时间,都可以省下来了。
相关推荐
专题
+ 收藏
+ 收藏
+ 收藏
+ 收藏
+ 收藏
+ 收藏
最新数据
相关文章
阿里云大模型服务平台百炼新人免费额度如何申请?申请与使用免费额度教程及常见问题解答
办公 AI 工具 OpenClaw 部署 Windows 系统一站式教程
Qwen3.6 正式发布!阿里云百炼同步开启“AI大模型节省计划”超值优惠
【新手零难度操作 】OpenClaw 2.6.4 安装误区规避与快速使用指南(包含最新版安装包)
OpenClaw 2.6.4 可视化部署 打造个人 AI 数字员工(包含最新版安装包)
【小白友好!】OpenClaw 2.6.4 本地 AI 智能体快速搭建教程(内有安装包)
零基础部署 OpenClaw v2.6.2,Windows 系统完整教程
【适合新手的】零基础部署 OpenClaw 自动化工具教程
开发者们的第一台自主进化的“爱马仕”来了
极简部署 OpenClaw 2.6.2 本地 AI 智能体快速启用(含最新版安装包)
AI精选
