Python 合并 PDF 文件(批量处理方法)

简介: 周一邮件堆成山?PDF合并总被收费、限页、传服务器?用Python+PyPDF2,5行代码批量合并PDF,免费、离线、无水印!支持自定义顺序、选页、加书签、处理加密/扫描件,还能打包成双击即用的exe——告别工具焦虑,三秒搞定。

周一早上九点,你的邮箱里躺着十几封邮件,每封都带了一个PDF附件——上周会议的签到表、各小组的报告、财务的报销凭证。领导在微信群里发了一条消息:“把这些文件合并成一个,发到公司群里。”

你打开第一个PDF,又打开第二个,鼠标拖来拖去,发现Adobe Acrobat弹出一个窗口:“您的试用期已结束。”同事推荐了在线合并工具,你上传完所有文件,等待进度条走到100%,页面弹出五个字:“请付费解锁。”
代理 IP 使用小技巧 让你的数据抓取效率翻倍 (27).png

这不是你的错。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))
tempname = 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帮你做了一次性投入、无限次复用的自动化。那些浪费在下载软件、比较付费方案、担心文件泄露上的时间,都可以省下来了。

目录
相关文章
|
14天前
|
存储 人工智能 项目管理
Todo 时代结束了:当 AI 开始自己管项目,人类管理者该管什么?
AI 不再只是执行你的指令,它开始管理自己的项目了。
112 2
|
27天前
|
人工智能 安全 测试技术
AI智能体的测试流程
AI智能体测试重在验证“受控随机性”与“逻辑链完整性”,区别于传统确定性测试。涵盖单元(提示鲁棒性、工具调用、RAG)、推理链、性能成本、黄金集回归、安全红队及UAT/A/B六大维度,确保智能体可靠、安全、高效落地。(239字)
|
21天前
|
人工智能 自然语言处理 供应链
2026年的企业级Agent解决方案,打破数据孤岛实现跨部门智能协同
2026年,瓴羊Agent One作为阿里云企业级智能体平台,以“智能中枢”角色打破数据孤岛:统一语义层、跨系统编排任务、主动协同推送。联动Dataphin、Quick Audience、Quick BI小Q与Quick Service,实现全域智能协同,赋能企业高效决策与敏捷运营。(239字)
|
30天前
|
机器学习/深度学习 数据采集 算法
基于烟花算法(FWA)及三次样条的机器人路径规划,50个场景任意选择附Matlab代码
✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 🍎 往期回顾关注个人主页: Matlab科研工作室 🍊个人信条:格物致知,完整Matlab代码及仿真咨询内容私信。 🔥  内容介绍 本文筛选50个覆盖工业、服务、室外、特殊环境的典型场景,均适配“烟花算法(FWA)全局路径搜索+三次样条局部平滑”的融合方案,可直接用于算法仿真、实验验证或工程应用。所有场景均考虑障碍物分布、运动约束等核心因素,充分发挥FWA全局寻优、抗局部最优的优势,以及三次样条路径连续可导、运动平稳的特点,适配不同类型机器人(AGV、工业机械臂、无人机、服
|
16天前
|
人工智能 机器人 API
OpenClaw阿里云无影云电脑一键部署实战:快速搭建、飞书无缝集成与常见问题解答
OpenClaw(原Clawdbot)作为轻量化、可扩展的AI智能体框架,凭借灵活的模型接入、多渠道消息分发与自动化任务能力,成为个人与团队搭建专属AI助手的优选。2026年,阿里云无影云电脑提供OpenClaw官方预置镜像,无需手动配置复杂环境,新手可通过桌面化操作快速完成云端部署。将OpenClaw接入飞书,可让AI助手直接在企业协作、团队沟通场景中响应指令、处理任务,实现从云端部署到实际应用的闭环。
509 11
|
1月前
|
Arthas 人工智能 Java
我们做了比你更懂 Java 的 AI-Agent -- Arthas Agent
Arthas Agent 是基于阿里开源Java诊断工具Arthas的AI智能助手,支持自然语言提问,自动匹配排障技能、生成安全可控命令、循证推进并输出结构化报告,大幅降低线上问题定位门槛。
912 64
我们做了比你更懂 Java 的 AI-Agent -- Arthas Agent
|
9天前
|
机器学习/深度学习 搜索推荐 数据处理
PAI-Rec推荐开发平台:企业级智能推荐解决方案,驱动业务全域增长
PAI-Rec是阿里云一站式推荐系统平台,集成多路召回、多目标精排(如DBMTL)、GPU加速推理与灵活迭代能力,已助力电商、直播、音视频等多行业提升点击率、转化率与ROI,实现高效、低成本、可自主演进的智能推荐。
127 16
|
1月前
|
安全 Go API
Go 1.26 go fix 实战:一键现代化你的Go代码
2026年Go 1.26重磅升级`go fix`:从静态补丁工具跃升为智能重构引擎!支持全项目扫描、自动适配`errors.AsType`/`io.ReadAll`等新特性,提升性能与类型安全。本文带你三步上手、避坑实战,轻松实现代码现代化。(239字)
270 9
|
18天前
|
弹性计算 人工智能 Linux
OpenClaw是什么?OpenClaw入门详解、阿里云ECS/本地部署及免费大模型配置教程
OpenClaw(社区俗称Clawdbot,龙虾)是一款基于MIT开源协议开发的自托管AI智能体执行网关,核心价值在于打破传统AI“只会说不会做”的局限,连接大语言模型与设备系统、工具应用,将自然语言指令转化为可落地的实操动作,实现从需求下达、任务拆解到执行反馈的全流程闭环。与单纯输出文字的对话式AI不同,OpenClaw可直接操控终端、管理文件、自动化浏览器操作,兼顾数据隐私与执行效率,无需专业技术背景,零基础用户也能快速部署使用。
1295 5
|
1月前
|
人工智能 数据库 开发者
OpenClaw「虾搞」数据库首场活动明日杭州开搞!
3月14日,OpenClaw“虾搞”数据库首场线下活动将在杭州阿里西溪园区举行。原定70人的技术沙龙,4天报名超600人,场地紧急扩容3倍!聚焦AI Agent与数据库融合,致敬开发者敢试错、勇探索的“虾搞”精神。全程直播,多地巡演即将启动。
326 3

热门文章

最新文章

下一篇
开通oss服务