
1. 办公场景里那些让人头大的 PDF 处理需求先说清楚这篇要解决什么用 Cursor 配合 pypdf写一个能在本地跑起来的 PDF 合并、拆分、AES-256 加密三合一脚本。适合谁每天要处理多份合同、研报、对账单的财务、法务、运营同学以及想用 AI 辅助写实用小工具的开发者。我平时接触的办公场景里PDF 处理几乎是最容易被低估的重复劳动。比如财务月底要把十几个账户的流水对账单按顺序拼成一本归档册法务要把一份 80 页的框架合同按单页拆出来分发给不同部门会签审计报告在发给外部机构之前得先加个打开密码防止中途被无关人员看到。这些事单看每一件都不难但架不住天天做、批量做。更麻烦的是很多人第一反应是去搜在线 PDF 工具。上传、处理、下载看起来三秒钟搞定但你把公司流水、客户名单、合同条款全传到了别人的服务器上。一旦涉及金融数据或者商业机密这就是实打实的合规风险。所以本地化处理不是洁癖是刚需。技术选型上Python 生态里能碰 PDF 的库不少。PyMuPDF 渲染快、提取强但依赖 C 编译的二进制pdfplumber 擅长抽表格文本可它压根不支持写入和加密。真正适合合并 拆分 加密这条链路的是 pypdf——它是经典 PyPDF2 的重构升级版纯 Python 实现接口干净而且原生支持 AES-256 加密只要额外装一个 cryptography 就能跑。轻量容器、无编译环境也能部署这点对运维很友好。这篇我会带着你用 Cursor 把这个工具箱从零写出来包含依赖配置、可复制的完整代码、命令行参数设计以及加密后怎么验证、批量怎么测。全程本地运行不碰任何在线服务。2. 用 Cursor 搭好 pypdf 环境与项目骨架在动手写代码之前先把环境和项目结构理清楚。这一步用 Cursor 来做会省很多事因为它的 AI 补全和终端集成能帮你快速定位依赖问题。首先是 Python 版本。建议 3.10 及以上pypdf 3.x 对类型注解和异步支持都更完善。我实测在 3.10.12 和 3.11.5 上都跑得很稳。如果你机器上还是 3.8某些 cryptography 的新版本会装不上建议先升级。依赖只有两个核心库。pypdf 负责 PDF 的读写、页面操作和加密接口cryptography 是 pypdf 调用 AES-256 时的底层密码学套件不装它writer.encrypt(algorithmAES-256)会直接抛 DependencyError。版本上我锁的是 pypdf3.15.0 和 cryptography41.0.3这两个组合经过验证加密兼容性最好。包管理我推荐用 uv它比 pip 快很多而且能自动管理虚拟环境。在 Cursor 里打开终端执行uv init pdf-toolkit cd pdf-toolkit uv add pypdf3.15.0 cryptography41.0.3如果你习惯 pip等价命令是python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install pypdf3.15.0 cryptography41.0.3项目结构建议这样组织方便后续扩展成命令行工具pdf-toolkit/ ├── pdf_toolkit.py # 主脚本 ├── pyproject.toml # uv 生成的依赖声明 └── samples/ # 放待处理的 PDF在 Cursor 里新建pdf_toolkit.py后你可以直接让 Cursor 的 AI 帮你生成函数骨架。我的做法是先写注释描述每个函数要干什么然后让 Cursor 补全实现再自己检查加密参数和异常处理。这样比纯手写快又不会失控。有一点要提醒Cursor 生成的代码默认可能用 PyPDF2 的旧导入方式from PyPDF2 import PdfReader。pypdf 3.x 已经改成from pypdf import PdfReader如果你混用会报 ModuleNotFoundError。这个坑我在第一次迁移时踩过记得统一。环境搭好后先跑一句python -c import pypdf; print(pypdf.__version__)确认版本是 3.15.0再往下走。3. 可复制的合并、拆分与 AES-256 加密配置这一节是核心我把完整脚本拆成几个函数讲每个都能单独复制使用。先给一份可直接运行的完整代码再逐段解释关键参数。# -*- coding: utf-8 -*- 文件名: pdf_toolkit.py 描述: 本地 PDF 合并、拆分与 AES-256 加密工具箱 import os import argparse from pypdf import PdfReader, PdfWriter def merge_pdfs(pdf_list, output_path): 按顺序合并多个 PDF 为一个文件 writer PdfWriter() for file_path in pdf_list: if not os.path.exists(file_path): print(f[跳过] 文件不存在: {file_path}) continue reader PdfReader(file_path) for page in reader.pages: writer.add_page(page) with open(output_path, wb) as f: writer.write(f) print(f[合并完成] 输出: {output_path}) def split_pdf(input_path, output_dir): 将多页 PDF 按页拆分为独立文件 os.makedirs(output_dir, exist_okTrue) reader PdfReader(input_path) for idx, page in enumerate(reader.pages): writer PdfWriter() writer.add_page(page) out os.path.join(output_dir, fpage_{idx 1}.pdf) with open(out, wb) as f: writer.write(f) print(f[拆分完成] 共 {len(reader.pages)} 页 - {output_dir}) def encrypt_pdf(input_path, output_path, user_pwd, owner_pwd): 使用 AES-256 对 PDF 加密 reader PdfReader(input_path) writer PdfWriter() for page in reader.pages: writer.add_page(page) writer.encrypt( user_passworduser_pwd, owner_passwordowner_pwd, algorithmAES-256, ) with open(output_path, wb) as f: writer.write(f) print(f[加密完成] 输出: {output_path}) def main(): parser argparse.ArgumentParser(descriptionPDF 合并/拆分/加密工具) sub parser.add_subparsers(destcmd) p_merge sub.add_parser(merge) p_merge.add_argument(inputs, nargs) p_merge.add_argument(-o, --output, requiredTrue) p_split sub.add_parser(split) p_split.add_argument(input) p_split.add_argument(-d, --dir, default./splitted) p_enc sub.add_parser(encrypt) p_enc.add_argument(input) p_enc.add_argument(-o, --output, requiredTrue) p_enc.add_argument(--user-pwd, requiredTrue) p_enc.add_argument(--owner-pwd, requiredTrue) args parser.parse_args() if args.cmd merge: merge_pdfs(args.inputs, args.output) elif args.cmd split: split_pdf(args.input, args.dir) elif args.cmd encrypt: encrypt_pdf(args.input, args.output, args.user_pwd, args.owner_pwd) else: parser.print_help() if __name__ __main__: main()关键参数说明。writer.encrypt()里algorithmAES-256是重点pypdf 3.x 支持 RC4-40、RC4-128、AES-128、AES-256 几种企业合规场景直接选 AES-256。user_password是打开文件时输入的密码owner_password是控制打印、编辑、提取权限的密码两者可以不同也可以相同。如果你更习惯用配置文件管理参数可以加一个config.toml[encrypt] algorithm AES-256 user_password UserPass123 owner_password OwnerAdmin456 [paths] output_dir ./output然后在脚本里用tomllibPython 3.11读取。这样批量处理时改配置就行不用动代码。命令行用法示例# 合并 python pdf_toolkit.py merge a.pdf b.pdf c.pdf -o combined.pdf # 拆分 python pdf_toolkit.py split combined.pdf -d ./pages # 加密 python pdf_toolkit.py encrypt combined.pdf -o secured.pdf \ --user-pwd UserPass123 --owner-pwd OwnerAdmin456这套参数设计的好处是每个动作独立方便你在 Cursor 里单独调试某个函数也方便后续接进自动化流水线。4. 验证请求与加密结果检查代码写完得验证它真的能用。我分三步走先跑合并再跑拆分最后验证加密文件是否真的锁住了。第一步准备测试文件。你可以用任意 PDF也可以让脚本自己生成。为了快速验证我写了个生成模拟 PDF 的小函数def generate_test_pdfs(): template b%PDF-1.4\n1 0 obj\n /Type /Catalog /Pages 2 0 R \nendobj\n2 0 obj\n /Type /Pages /Kids [3 0 R] /Count 1 \nendobj\n3 0 obj\n /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents 4 0 R \nendobj\n4 0 obj\n /Length 45 \nstream\nBT /F1 12 Tf 72 712 Td (Test Page %d) Tj ET\nendstream\nendobj\nxref\n0 5\n0000000000 65535 f\n0000000009 00000 n\n0000000062 00000 n\n0000000117 00000 n\n0000000213 00000 n\ntrailer\n /Size 5 /Root 1 0 R \n%%EOF for i in range(1, 4): with open(ftest_source_{i}.pdf, wb) as f: f.write(template % i)第二步执行合并和拆分观察日志python pdf_toolkit.py merge test_source_1.pdf test_source_2.pdf test_source_3.pdf -o combined.pdf python pdf_toolkit.py split combined.pdf -d ./pages预期输出里会显示合并后的页数和拆分出的文件数。如果合并后页数不对多半是某个源文件读取失败被跳过了检查路径。第三步加密并验证。执行python pdf_toolkit.py encrypt combined.pdf -o secured.pdf \ --user-pwd UserPass123 --owner-pwd OwnerAdmin456然后做两个验证动作。一是用 PDF 阅读器双击secured.pdf应该弹出密码框输入错误密码被拒绝输入UserPass123才能打开。二是用 Adobe Acrobat Pro 打开检查编辑文本提取页面打印这些选项是否变灰——如果变灰说明 owner 密码生效了。你还可以用 pypdf 自己验证加密状态from pypdf import PdfReader reader PdfReader(secured.pdf) print(是否加密:, reader.is_encrypted) reader.decrypt(UserPass123) print(解密后页数:, len(reader.pages))如果is_encrypted返回 True且解密后能读到页数说明加密链路完整。批量测试时可以写个循环遍历目录下所有 PDF逐个加密并记录结果这样能快速发现某个文件格式异常导致的失败。5. 本篇常见报错排查实际跑的时候报错基本集中在几个地方。我把最常见的列出来对照着查。报错一DependencyError: pypdf requires cryptography to encrypt/decrypt原因很明确你调了writer.encrypt()但环境里没装 cryptography。解决uv add cryptography41.0.3 # 或 pip install cryptography41.0.3装完不用重启脚本直接再跑。这个错我在新容器里第一次部署时遇到过因为 pypdf 把 cryptography 列为可选依赖不会自动装。报错二ModuleNotFoundError: No module named PyPDF2这是新旧库混用。pypdf 3.x 的导入是from pypdf import PdfReader不是from PyPDF2 import ...。如果你从旧项目复制代码记得全局替换。Cursor 有时也会按旧习惯补全检查一下 import 行。报错三PdfReadError: EOF marker not found源 PDF 损坏或者不是合法 PDF。常见于下载中断的文件或者被其他工具改坏的文件。用PdfReader打开前先判断文件头是不是%PDF-不是就跳过。报错四加密后文件打不开提示密码错误检查你是不是把 user 和 owner 密码搞反了。打开文件用的是 user_password不是 owner_password。另外注意密码里的特殊字符命令行传参时如果含$、!要加引号。报错五合并后页面顺序错乱merge_pdfs是按传入列表顺序追加的如果你用glob拿文件顺序可能是随机的。建议显式排序import glob files sorted(glob.glob(samples/*.pdf))报错六local proxy failed或网络相关错误这个通常出现在你试图用某些在线 API 时。本方案全程本地运行不涉及网络请求如果你遇到这类报错说明代码里混进了别的调用检查一下。排查思路就一条先看报错类型是依赖问题、导入问题还是文件问题对症下药。大部分情况重装依赖或改导入就能解决。6. 把工具接进你的日常工作流脚本能跑通只是第一步真正提效是把它接进日常流程。我分享几个实际用法。批量加密整个目录可以写个 shell 循环for f in ./reports/*.pdf; do python pdf_toolkit.py encrypt $f -o ./secured/$(basename $f) \ --user-pwd $READ_PWD --owner-pwd $ADMIN_PWD done密码从环境变量读不要硬编码在脚本里。生产环境建议用 KMS 或者密钥管理服务动态生成别把明文密码写进代码仓库。如果你经常要处理固定几类文档可以在 Cursor 里把常用命令存成任务或者写个Makefilemerge: python pdf_toolkit.py merge $(IN) -o $(OUT) encrypt: python pdf_toolkit.py encrypt $(IN) -o $(OUT) \ --user-pwd $(USER_PWD) --owner-pwd $(OWNER_PWD)这样一条make encrypt INreport.pdf OUTsecured.pdf就搞定。还有一点经验合并不同来源的 PDF 时页面尺寸经常不一致A4 和 Letter 混在一起会导致排版错乱。可以在合并前统一 mediaboxfrom pypdf import PageObject for page in reader.pages: page.mediabox.upper_right (595, 842) # 统一为 A4 writer.add_page(page)最后如果你想让这套工具更智能比如自动识别文档类型、按规则命名输出文件可以结合 Cursor 的 AI 能力写一些辅助函数。但核心的合并、拆分、加密逻辑pypdf 已经足够稳不需要引入更重的依赖。工具的价值在于用起来顺手。先跑通再按自己的场景微调慢慢就变成你办公流里离不开的一环了。