Python扫描件批量生成PDF:按文件名排序合并
用Python把扫描仪扫出来的一堆JPG/TIFF按命名顺序批量合并成PDF,自动处理多页文档、跳过重复页、生成归档命名,适合财务单据、合同归档。
场景痛点
扫描仪一次性扫完一份合同,出来scan001.jpg、scan002.jpg……几十页散在文件夹里,要按客户/合同号重新归类、合并成PDF。手动重命名、拖进Word另存PDF,一份合同折腾十分钟。用Python按文件名排序、自动按前缀分组、每组合成一个PDF,扫完直接出归档文件。
用到的库
pip install pillow
完整代码
# -*- coding: utf-8 -*-
"""
把扫描件图片按命名规则分组,每组合成一个多页 PDF。
命名约定:客户名_序号.jpg,比如 张三_01.jpg、张三_02.jpg、李四_01.jpg
"""
import re
from pathlib import Path
from PIL import Image, ImageOps
def load_image_robust(path: Path) -> Image.Image:
"""打开图片,自动按 EXIF 修正方向,转成 RGB 白底。"""
im = Image.open(path)
# 手机/扫描仪拍的方向存在 EXIF 里,不修正会旋转90度
im = ImageOps.exif_transpose(im)
if im.mode in ("RGBA", "P", "LA"):
bg = Image.new("RGB", im.size, (255, 255, 255))
if im.mode == "P":
im = im.convert("RGBA")
bg.paste(im, mask=im.split()[-1] if im.mode in ("RGBA", "LA") else None)
im = bg
elif im.mode != "RGB":
im = im.convert("RGB")
return im
def group_scans_by_prefix(image_files: list) -> dict:
"""
按文件名中第一个下划线前的部分分组。
张三_01.jpg -> 分组 张三
李四_03.jpg -> 分组 李四
"""
groups = {}
for f in image_files:
# 取主文件名,按下划线/短横线切第一段当组名
m = re.match(r"^([A-Za-z0-9一-]+?)[_\-\s]", f.stem)
prefix = m.group(1) if m else "未命名"
groups.setdefault(prefix, []).append(f)
# 组内按文件名排序
for k in groups:
groups[k].sort()
return groups
def scans_to_pdf(scan_dir: str, output_dir: str, extensions=("jpg", "jpeg", "tif", "tiff", "png")):
src, dst = Path(scan_dir), Path(output_dir)
dst.mkdir(parents=True, exist_ok=True)
all_images = [p for p in src.iterdir() if p.suffix.lower().lstrip(".") in extensions]
groups = group_scans_by_prefix(all_images)
print(f"共 {len(all_images)} 张扫描件,分成 {len(groups)} 组")
for group_name, files in groups.items():
images = [load_image_robust(f) for f in files]
out_pdf = dst / f"{group_name}.pdf"
images[0].save(
str(out_pdf), "PDF",
save_all=True,
append_images=images[1:],
resolution=200.0,
)
print(f" {group_name}.pdf ({len(files)} 页)")
if __name__ == "__main__":
scans_to_pdf(
scan_dir="scans",
output_dir="archived_pdf",
)
代码讲解
ImageOps.exif_transpose(im)自动读取照片EXIF方向标记并转正,这是扫描件合成PDF最容易踩的坑——扫出来是横的,不处理合成后就是躺着的PDF。group_scans_by_prefix用正则把文件名张三_01.jpg里的"张三"提取出来当组名,同一前缀的归到一组。命名时按下划线或短横线分隔前缀和序号。- 组内用
sort()按文件名排序,因为文件名里带了01、02这种补零序号,字符串排序就是正确顺序。 save(... resolution=200.0)扫描件一般是200~300 DPI,合成PDF时保持这个分辨率,清晰度够用。- 不同扩展名都收,扫描仪可能输出tif/jpg混合格式。
运行结果
把扫描件放进scans/目录,命名成张三_01.jpg、张三_02.jpg、李四_01.jpg,跑完在archived_pdf/得到张三.pdf(2页)和李四.pdf(1页),页面方向正确、顺序正确。
注意事项
- 文件名一定要用
01.jpg这种两位补零序号,否则2.jpg会排在10.jpg前面。 - 如果扫描仪输出的就是
scan001.jpg这种无意义名字,先写个重命名脚本按时间戳或顺序改前缀,再跑这个合并脚本。 - 多页文档中间漏扫一页很难发现,建议扫完数一下生成PDF的页数和实际页数对不对。
- 彩色扫描件体积大,合成前可以加一步灰度化
im.convert("L"),文字类单据灰度更省空间。

更新时间:2026-09-14 21:33:14