批量 PDF 加密 / 解密:加打开密码 + 限权限,80 行 pypdf 脚本

2026-10-05 · 喵窝资源铺

两个高频场景:① 一批 PDF 发给客户前要加打开密码(最好顺带禁止复制修改);② 拿到一批已知密码的加密 PDF要批量解密成明文(比如拿到自家公司的归档件)。Adobe Acrobat 收费、WPS 要一个个另存。这篇给一个 pypdf 的批量脚本:encrypt 加密码+限权限,decrypt 批量去密;坏文件不崩,全部记进报告。文中代码实跑验证,真实输出贴在下面。

它做的四件事

需求脚本的处理
加密+限权限encrypt 目录 输出 --user w123 --owner adm456:打开要密码,且只允许打印、禁止修改/复制/批注
仅限权限只给 --owner 不给 --user:打开不要密码,但别人动不了内容(发公开件常用)
批量解密decrypt 目录 输出 --password w123:输出去密后的明文副本,输出的 PDF is_encrypted=False
烂文件不崩损坏 PDF / 密码错的文件自动跳过并写进报告

实跑抓到的坑:pypdf 6.x 的 encrypt 签名变了

坑1:网上老教程写 permissions_flags(复数),pypdf 6.x 直接 TypeError。实测 inspect.signature(PdfWriter.encrypt),现在的签名是 encrypt(self, user_password, owner_password=None, ..., permissions_flag=..., algorithm=None)——参数是单数 permissions_flag,而且 user_password 变成了必填位置参数。只想限权限不加密打开的,要传空串 user_password=""。搜到的旧代码一个个抄都报 unexpected keyword argument 'permissions_flags',这个坑版本相关,认准 pypdf --version 再抄。
坑2:验证加密别只看文件打不打开,要用 decrypt() 的返回值。PdfReader.decrypt(密码) 返回 0 是密码错误、1 是成功。实测错误密码返回 0,正确密码返回 1 且 len(r.pages) 正常。另外「只限权限、打开不要密码」的 PDF,reader.is_encrypted 也是 True,解密脚本传空密码即可解开——这类文件在企业归档里非常常见。

脚本全文(pdf_encrypt.py)

# -*- coding: utf-8 -*-
"""批量 PDF 加密/解密:整目录处理,坏文件不崩。
用法:
  python3 pdf_encrypt.py encrypt <输入目录> <输出目录> --user w123 --owner adm456
  python3 pdf_encrypt.py encrypt <输入目录> <输出目录> --owner adm456
  python3 pdf_encrypt.py decrypt <输入目录> <输出目录> --password w123
  python3 pdf_encrypt.py decrypt <输入目录> <输出目录>
只依赖 pypdf(6.x 验证通过)。
"""
import os, sys
from pypdf import PdfReader, PdfWriter
from pypdf.constants import UserAccessPermissions as UAP

def walk_pdfs(d):
    for root, _, files in os.walk(d):
        for f in sorted(files):
            if f.lower().endswith(".pdf"):
                yield os.path.join(root, f)

def out_path(outdir, in_root, p):
    dst = os.path.join(outdir, os.path.relpath(p, in_root))
    os.makedirs(os.path.dirname(dst) or ".", exist_ok=True)
    return dst

def parse_opts(args):
    opts = {}
    for a in args:
        if a.startswith("--"):
            k, _, v = a.partition("=")
            if v == "":
                i = args.index(a)
                if i + 1 < len(args) and not args[i+1].startswith("--"):
                    v = args[i+1]
            opts[k] = v
    return opts

def main():
    args = [x for x in sys.argv[1:]]
    if len(args) < 3 or args[0] not in ("encrypt", "decrypt"):
        print(__doc__); sys.exit(2)
    mode, indir, outdir = args[0], args[1], args[2]
    opts = parse_opts(args[3:])
    user_pw = opts.get("--user")
    owner_pw = opts.get("--owner")
    password = opts.get("--password")
    report = []
    for p in walk_pdfs(indir):
        name = os.path.basename(p)
        try:
            r = PdfReader(p)
            if mode == "encrypt":
                w = PdfWriter(clone_from=r)
                if user_pw or owner_pw:
                    perm = UAP.PRINT
                    w.encrypt(
                        user_password=user_pw or "",
                        owner_password=owner_pw or user_pw or "",
                        permissions_flag=perm,
                    )
            else:
                if r.is_encrypted:
                    ok = r.decrypt(password or "")
                    if ok == 0:
                        raise ValueError("密码错误或未提供,解不开")
                w = PdfWriter(clone_from=r)
            dst = out_path(outdir, indir, p)
            with open(dst, "wb") as f:
                w.write(f)
            report.append((name, "OK", dst))
        except Exception as e:
            report.append((name, f"[跳过] {e}", ""))
    for name, st, dst in report:
        print(f"{st}\t{name}" + (f"\t{dst}" if dst else ""))
    bad = sum(1 for _, s, _ in report if s != "OK")
    print(f"\n处理 {len(report)} 个,成功 {len(report)-bad},跳过 {bad}")

if __name__ == "__main__":
    main()

实测输出

造了 4 个测试文件:2 页的报告、1 页的便签、截断损坏的假 PDF、还有一个「只限权限、打开不要密码」的 owner_locked.pdf(模拟企业归档常见形态)。

加密 encrypt testdata_enc out_enc --user w123 --owner adm456:

EOF marker not found
[跳过] Stream has ended unexpectedly	broken.pdf
OK	memo1.pdf	out_enc/memo1.pdf
OK	owner_locked.pdf	out_enc/owner_locked.pdf
OK	report2.pdf	out_enc/report2.pdf

处理 4 个,成功 3,跳过 1

复核加密结果:is_encrypted: True;错误密码 decrypt('wrong') 返回 0;正确密码 decrypt('w123') 返回 1、2 页正常读出。

解密 decrypt out_enc out_dec --password w123:

OK	memo1.pdf	out_dec/memo1.pdf
OK	owner_locked.pdf	out_dec/owner_locked.pdf
OK	report2.pdf	out_dec/report2.pdf

处理 3 个,成功 3,跳过 0

解密产物复核:is_encrypted: False,页数正常——拿到的就是干净明文副本。对没给密码的目录跑 decrypt,也能处理「只限权限」那类文件(传空密码解开)。

不想自己跑脚本?喵窝资源铺提供人工代跑+交付服务(miaowoo.900890.xyz):把你的 PDF 打包发来,说明要加密还是解密、密码规则,交付加密后文件或明文副本。由 AI 完成脚本与初验、人工复核后交付,9.9 元起。虚拟物品,交付后不退款,介意勿拍。

← 返回喵窝资源铺 · 相关:批量拆分 PDF · 相关:批量 PDF 去空白页+加水印 · 相关:批量合并 PDF