多层嵌套 zip 批量提取文件
用途
把「外层 zip → 内层 zip → 目标文件」这类多层压缩包一次解开,并把指定类型文件(默认 PDF)汇总到一个输出目录。最常见场景是 12306 / 铁路 12306 下载的电子发票:
网上购票系统-电子发票通知.zip <- 第 1 层(同一批常分 (1)(2)...(6) 多份下载)
26329116809000069372.zip <- 第 2 层
26329116809000069372.pdf <- 目标文件
26329116809000069372.ofd
同样适用于任何「zip 套 zip」的批量资料归档。
何时使用
- 用户给一批 zip,解开后里面还是 zip,需要继续解压并取出某类文件。
- 文件名是纯数字(12306 为
263...),或含中文(已做乱码修复)。 - 下载批次重复(
.zip与(1)~(6).zip),需按文件名自动去重。
如何执行
运行 scripts/extract_from_zips.py(仅用标准库 zipfile,无需安装依赖):
# 处理某个目录(默认扫描该目录下所有 .zip 作为外层)
python3 extract_from_zips.py /path/to/folder
# 提取多种类型、指定输出目录、保留中间解压产物
python3 extract_from_zips.py /path/to/folder -e pdf,jpg,png -o ./out --keep
# 处理当前目录
python3 extract_from_zips.py
执行行为:
1. 收集目标目录下所有 .zip 作为外层。
2. 用队列递归解压每一层 zip(不限层数,直到不再有 zip);解压时对中文 / 日文等文件名乱码自动回退 gbk / cp437 解码。
3. 收集所有目标扩展名文件,按文件名去重(同一文件多次下载只保留一份)。
4. 复制到输出目录(默认 <目标目录>/input),用完清理临时目录(除非 --keep)。
关键注意事项
- 下载批次会重复:
.zip与(1)~(6).zip多份里常有同一张票,必须按文件名去重。 - 不要污染原目录:在系统临时目录解压,原始 zip 与用户已手动解压的目录一律不动。
- 不要删除用户文件:只做「提取复制」,不删除、不移动原始压缩包。
- 中文名乱码:Windows 打的 zip 文件名常是 GBK 编码,解压时需回退解码(脚本已处理)。
- 校验:提取后确认每个文件头是
%PDF-(PDF)或对应魔数;数量应与内层 zip 数一致。
验证
cd input && ls -1 | wc -l && for f in *.pdf; do head -c 5 "$f" | grep -q '%PDF-' || echo "损坏: $f"; done
火车票发票:按乘车日期命名
若要把 12306 电子发票按「乘车日期」重命名为 MMDD(同日多张用 -2、-3,如 0809、0809-2),用专用脚本 scripts/extract_train_invoices.py:
# 先清空 input 再按乘车日期重写命名
python3 extract_train_invoices.py /path/to/火车票 --clear
# 仅追加写入(不清空原有文件)
python3 extract_train_invoices.py /path/to/火车票
- 乘车日期取自 PDF 文本中「
YYYY年MM月DD日后紧跟HH:MM开」的日期,自动排除「开票日期:」这一开票日。 - 默认保留 input 原有文件;加
--clear先清空再写入。 - 日期解析失败时回退用原票号命名,不会丢失文件。
打印优化:每 4 张拼成一张 A4
发票 PDF 提取完成后,常需拼版打印。用 scripts/merge_invoices_grid.py 把 input 中发票每 4 张拼成一张 A4(2×2 网格),矢量嵌入、保持比例居中、不拉伸变形,输出到 output/:
# 默认把 <input_dir> 每 4 张拼一张,输出到 <input_dir>/../output
python3 merge_invoices_grid.py /path/to/火车票/input
# 显式指定输入输出、改每页张数(如 6 张 / 2×3)
python3 merge_invoices_grid.py /path/to/火车票/input /path/to/火车票/output --per 4
- 按文件名(乘车日期)排序,拼页顺序连贯;末尾不足 4 张也照常拼一张(留空)。
- 输出命名
NN_首日期-尾日期.pdf(如01_0513-0801.pdf),便于核对。 - 依赖 PyMuPDF:
pip install pymupdf(仅此脚本需要,提取脚本只用标准库)。
> 若只需要简单顺序合并成一个大 PDF(不做拼版),用 pdfunite input/*.pdf output/all.pdf 即可。