经常遇到这样的场景:电脑里堆着一批 PDF,可能是扫描版合同、产品手册、论文资料,加起来好几个 GB。想发邮件、传网盘、做归档,第一个卡点就是体积。手动打开 PDF 一份份压缩,遇到几十上百个文件根本不可行;用在线工具,又要排队上传,还担心文件泄露。
本文围绕「批量压缩 PDF 文档」这个主题,整理了三套可以直接落地的方案:Ghostscript 命令行、qpdf 无损优化、Python 脚本自动化,并给出完整批量压缩脚本、常见问题排查思路和工程经验。无论你只是偶尔压几个文件,还是想把压缩流程固化到日常工作中,都能找到可以直接使用的办法。
1. 为什么 PDF 会很大,压缩前需要先知道什么
1.1 PDF 体积从哪里来
要压缩 PDF,首先要理解 PDF 的体积构成。一个 PDF 文件并不是一张“大图片”,而是一个容器,内部通常包含以下几类内容:
- 嵌入图片:扫描件、照片、截图,这是体积最大的来源,常见的是 JPEG、JPEG2000 和 FlateDecode 图片。
- 字体对象:页面使用的中文、英文、数字字体被嵌入到文件中,一套完整字体可能就有十几 MB。
- 内容流(Content Stream):记录文字、图形、坐标绘制指令,一般会经过压缩,但不同工具生成的压缩率差异很大。
- 元数据与书签:XMP 元数据、书签、注释、文档属性等,数量多时也会占用空间。
- 重复对象:同一张图片、同一个字体被多次引用,但某些低质量生成工具会把它们存成多份副本。
其中,图片和字体几乎决定了 PDF 体积的大部分。如果你的 PDF 是扫描件,压缩的核心就是把图片重新采样、降低分辨率;如果是纯文字资料,重点是字体子集化和流压缩;如果是由绘图软件导出的矢量 PDF,那么压缩空间往往不大。
1.2 有损压缩和无损压缩的区别
PDF 压缩方法按对内容的影响可以分为两类:
第一类是有损压缩,最典型的是 Ghostscript 的 pdfwrite 引擎。它会将页面中的图片重新采样到较低分辨率,再用 JPEG 等格式重新编码,同时丢弃一些不重要的元数据。这种方式的优点是压缩率非常高,缺点是放大查看时图片会模糊,文字边缘可能发生轻微变化,如果目标只是屏幕阅读或邮件传输,影响不大。
第二类是无损压缩,代表是 qpdf 和 pikepdf。它们不会重新编码图片,而是优化 PDF 内部结构:把对象合并成对象流、对未压缩的数据流重新压缩、删除未被引用的垃圾对象、合并重复内容。这类方法优点是内容完全不变,缺点是体积下降有限,通常只能减少 10% 到 30%,无法替代图片重压缩。
实际项目中,两者经常组合使用:先用 qpdf 或 pikepdf 做无损整理,再用 Ghostscript 做有损压缩,或者反过来在压缩后检查文件质量。
1.3 压缩场景决定参数
压缩前先明确这份 PDF 是给谁看的、用在哪里,这决定了你能接受多少画质损失。
- 屏幕阅读、邮件附件、网盘存储:可以用 /screen 或 /ebook 档位,体积优先,画面够清晰就行。
- 打印使用:建议用 /printer 甚至 /prepress,保持较高图片分辨率和字体完整性。
- 正式归档、法律材料、需保留原始观感:尽量使用无损优化,或只做最小幅度的有损压缩,并保留原文件。
- 重复压缩:不建议同一份 PDF 反复用有损方式压缩,每压一次画质就下降一次,第二遍的压缩率也会明显变小。
很多新手一上来就追求最小体积,结果压缩后文字显示异常、图片模糊得没法看。更稳妥的做法是先备份原文件,再按用途选档位,最后抽查几页确认质量。
2. 工具选型与环境准备
2.1 常见方案对比
先看下面的对比表,再根据你的习惯选工具。
| 工具 | 压缩方式 | 适合场景 | 批量支持 | 上手难度 |
|---|---|---|---|---|
| Ghostscript | 有损 + 无损 | 图片多的扫描件、大文件 | 配合命令行循环非常方便 | 中 |
| qpdf | 无损 | 需要保留内容完整性的场景 | 配合命令行循环方便 | 低 |
| PyMuPDF | 无损 + 轻度清理 | Python 工作流、批量处理 | 脚本化最好 | 中 |
| pikepdf | 无损 | Python 工作流、依赖 qpdf 内核 | 脚本化最好 | 中 |
| 在线压缩工具 | 不确定 | 少量非敏感文件 | 差 | 低 |
如果你处理的文件属于公司内部资料、客户合同等敏感材料,尽量不要上传到第三方在线工具。本地使用 Ghostscript 或 Python 脚本,既安全又能精确控制参数。
2.2 安装 Ghostscript
Ghostscript 是一套历史悠久的开源 PDF/PostScript 渲染与转换引擎,它的 pdfwrite 输出设备是目前最常用的 PDF 压缩方案之一。
- Windows:到 Ghostscript 官网下载对应 64 位安装包,安装后把
C:\Program Files\gs\gs10.xx\bin加入系统 PATH。压缩时直接执行gswin64c.exe。 - Ubuntu/Debian:使用
sudo apt-get install ghostscript安装。 - CentOS/RHEL:使用
sudo yum install ghostscript安装。 - macOS:使用
brew install ghostscript安装。
安装完成后,在终端执行gs --version,能输出版本号说明安装成功。Windows 下如果提示命令不存在,可以先用完整路径测试。注意,不同版本的压缩行为略有差异,本文示例以现在的常见稳定版为准,如果你的版本较旧,建议升级后再跑批量任务。
2.3 安装 qpdf
qpdf 是一个专门用于 PDF 结构转换与修复的开源工具,通过命令行即可完成无损优化。
- Ubuntu/Debian:
sudo apt-get install qpdf - macOS:
brew install qpdf - Windows:从 qpdf 官网 releases 页面下载安装包或二进制,加入 PATH。
安装后用qpdf --version验证。qpdf 的命令风格非常一致,绝大多数场景只需要一行命令,适合和 Shell 脚本、CI 流程配合。
2.4 准备 Python 环境
如果希望在项目中通过脚本完成压缩、生成报告、接入后续任务,建议使用 Python。本文示例以 Python 3.10 及以上版本为例,重点演示配置思路,低版本一般也能运行,但个别库接口需要微调。
pip install pymupdf pikepdf这里有两个容易混淆的点:
- PyMuPDF 的安装包名是
pymupdf,但导入名称通常是fitz。老项目里写import fitz,新版也可以写import pymupdf,具体以你安装的版本为准。 - pikepdf 是 qpdf 的 Python 封装,底层的无损压缩能力来自 qpdf C++ 库,安装后可以直接在 Python 中使用与 qpdf 命令等价的能力。
准备一个干净的目录,比如pdf-compressor/,把后续的脚本都放在里面。
3. 方案一:Ghostscript 命令行批量压缩
3.1 单文件压缩命令
先用最简单的命令压缩单个 PDF:
gs -sDEVICE=pdfwrite \ -dCompatibilityLevel=1.4 \ -dPDFSETTINGS=/ebook \ -dNOPAUSE -dQUIET -dBATCH \ -sOutputFile=output.pdf input.pdf这段命令表示:用 pdfwrite 设备重新输出 PDF,输出兼容 PDF 1.4 版本,压缩设置使用 /ebook 档位,静默运行,输出到 output.pdf。
第一次运行建议不要直接处理原始文件,而是输出到一个新的文件,方便对比压缩前后体积和质量。
3.2 压缩参数说明
Ghostscript 的可用参数非常多,这里先掌握最核心的几个:
| 参数 | 作用 |
|---|---|
| -dPDFSETTINGS=/screen | 屏幕阅读档,体积最小,图片分辨率最低 |
| -dPDFSETTINGS=/ebook | 电子书档,平衡体积和清晰度,日常推荐 |
| -dPDFSETTINGS=/printer | 打印档,适合较高要求输出 |
| -dPDFSETTINGS=/prepress | 印刷档,保持高质量 |
| -dCompatibilityLevel=1.4 | 设置输出 PDF 版本,1.4 兼容性最好 |
| -dDetectDuplicateImages=true | 检测并合并重复图片 |
| -dEmbedAllFonts=true | 嵌入所有字体,避免换电脑后文字变样 |
| -dSubsetFonts=true | 只嵌入用到的字符,减小字体体积 |
| -dNOPAUSE -dBATCH | 关闭交互暂停,批量处理时必须有 |
| -dQUIET | 静默模式,减少输出干扰 |
需要注意的是,-dPDFSETTINGS的值必须带斜杠,例如/ebook,不能写成ebook。dpi 数值会根据 Ghostscript 版本和具体上下文变化,不要指望两个版本的压缩效果完全一致,生产环境里最好锁定版本。
3.3 Linux/macOS 批量脚本
假设所有 PDF 都放在~/pdfs/目录下,我们要把压缩后的文件输出到~/pdfs/compressed/目录,脚本如下:
#!/bin/bash # 批量压缩 PDF:Linux / macOS 可用 set -e INPUT_DIR="$HOME/pdfs" OUTPUT_DIR="$INPUT_DIR/compressed" mkdir -p "$OUTPUT_DIR" cd "$INPUT_DIR" for f in *.pdf; do name="${f%.pdf}" echo "正在压缩: $f" gs -sDEVICE=pdfwrite \ -dCompatibilityLevel=1.4 \ -dPDFSETTINGS=/ebook \ -dNOPAUSE -dQUIET -dBATCH \ -sOutputFile="$OUTPUT_DIR/${name}.pdf" \ "$f" done echo "批量压缩完成,输出目录: $OUTPUT_DIR"建议先用两三份文件测试,确认输出文件能正常打开、页数不变,再处理全部文件。脚本中的set -e表示出现错误就停止,避免批量任务在失败后继续处理造成混乱。
3.4 Windows 批量脚本
Windows 下推荐使用批处理脚本:
@echo off setlocal enabledelayedexpansion if not exist compressed mkdir compressed for %%f in (*.pdf) do ( echo 正在压缩: %%f gswin64c.exe -sDEVICE=pdfwrite ^ -dCompatibilityLevel=1.4 ^ -dPDFSETTINGS=/ebook ^ -dNOPAUSE -dQUIET -dBATCH ^ -sOutputFile=compressed\%%~nf.pdf ^ "%%f" ) echo 批量压缩完成%%~nf表示取文件名主体部分。如果 Ghostscript 安装后没有加入 PATH,把gswin64c.exe换成完整路径,例如:
set GS_PATH=C:\Program Files\gs\gs10.xx\bin\gswin64c.exeWindows 控制台对中文文件名有时会出现乱码,可以在脚本开头执行chcp 65001,或者干脆用后面的 Python 方案处理,中文兼容性更好。
3.5 自定义压缩档位
-dPDFSETTINGS只能选固定档位,如果我们希望更精细地控制图片分辨率,可以在命令行中追加这些参数:
gs -sDEVICE=pdfwrite \ -dCompatibilityLevel=1.4 \ -dPDFSETTINGS=/ebook \ -dColorImageDownsampleType=/Bicubic \ -dColorImageResolution=120 \ -dGrayImageResolution=120 \ -dMonoImageResolution=300 \ -dJPEGQ=70 \ -dNOPAUSE -dQUIET -dBATCH \ -sOutputFile=output.pdf input.pdf这段命令把彩色和灰度图片分辨率降到 120 DPI,黑白图片保持 300 DPI,JPEG 质量设为 70。数值越低,体积越小,画质损失也越大。对于分辨率特别高的扫描件,这种自定义方式往往比固定档位更有效。
4. 方案二:qpdf 无损优化
4.1 qpdf 的设计思路
如果你不希望内容有任何肉眼可见的变化,用 Ghostscript 可能太“激进”。这时可以试试 qpdf。
qpdf 不会重新编码图片,它做的是结构级优化。正常情况下,PDF 中的对象会被压缩后再存放到文件里,但有些工具生成的 PDF 存在大量冗余对象、未压缩的数据流,或者把图片以效率很低的方式重复保存。qpdf 会把这些对象统一处理,该合并的合并,该压缩的压缩,从而让文件体积下降。
这种优化方式非常适合以下场景:
- PDF 由某些办公软件导出,结构混乱、体积虚高。
- 文件需要保持原始排版、图片像素和精度。
- 压缩前做一次“结构整理”,为后续处理打基础。
4.2 常用优化命令
最简单的无损优化命令:
qpdf --object-streams=generate \ --compress-streams=y \ --recompress-flate \ input.pdf output.pdf三个参数的含义分别是:
--object-streams=generate:将零散对象合并到对象流中,减少文件内部碎片。--compress-streams=y:对未压缩的数据流启用压缩。--recompress-flate:对已经用 Flate 算法压缩过的流重新压缩一次,去掉低效压缩的冗余数据。
实际使用时,不同版本的 qpdf 对新参数的支持略有差异,建议先执行qpdf --help确认参数名。如果版本不支持--recompress-flate,去掉这一项也能完成大部分优化。
4.3 批量处理与脚本
qpdf 配合 Shell 循环即可批量处理:
#!/bin/bash # qpdf 批量无损优化 INPUT_DIR="$HOME/pdfs" OUTPUT_DIR="$INPUT_DIR/qpdf_compressed" mkdir -p "$OUTPUT_DIR" for f in "$INPUT_DIR"/*.pdf; do name="$(basename "$f" .pdf)" qpdf --object-streams=generate \ --compress-streams=y \ --recompress-flate \ "$f" "$OUTPUT_DIR/${name}.pdf" doneqpdf 的优点是命令简单、不容易出错;缺点也很明显,如果 PDF 体积主要来自 JPEG 图片本身,无损优化的压缩率会比较有限。想进一步缩小图片主导的文件,最好还是用 Ghostscript 或 Python 脚本配合图片重压缩。
5. 方案三:Python 脚本压缩
5.1 PyMuPDF 轻量压缩
PyMuPDF 是功能很强的 PDF 解析库,它自带一些轻量压缩能力,适合清理垃圾对象、子集化字体、压缩数据流。
# 文件路径:pdf-compressor/pymupdf_compress.py import fitz def light_compress(in_path: str, out_path: str) -> None: doc = fitz.open(in_path) try: # 子集化字体,只保留实际用到的字符 doc.subset_fonts() # garbage=4 表示最彻底地清理未引用对象 # deflate=True 表示对数据流启用压缩 doc.save(out_path, garbage=4, deflate=True) finally: doc.close() if __name__ == "__main__": light_compress("input.pdf", "output.pdf")这种做法的特点是安全、快速,特别适合处理以文字和矢量内容为主的 PDF。但它没有对图片进行重新编码,所以对扫描件、照片型 PDF 的压缩效果有限。如果你的场景是这类文件,更推荐调用 Ghostscript 处理图片,下面会给出完整脚本。
5.2 pikepdf 无损压缩
pikepdf 提供的是 qpdf 能力,在 Python 中操作起来更灵活。它的用法同样简单:
# 文件路径:pdf-compressor/pikepdf_compress.py import pikepdf def whitelist_compress(in_path: str, out_path: str) -> None: with pikepdf.open(in_path) as pdf: pdf.save( out_path, compress_streams=True, object_stream_mode=pikepdf.ObjectStreamMode.generate, ) if __name__ == "__main__": whitelist_compress("input.pdf", "output.pdf")注意,pikepdf 的接口在不同版本之间有过调整,如果代码执行时提示某个参数不存在,参考当前安装版本的官方示例修改即可。它的优势是保留一个“原生”的 PDF 对象模型,在批量处理时还能顺便检查页数、元数据、加密状态等信息。
5.3 如何选择 Python 方案
简单总结一下:
- 只要清理垃圾对象和字体子集化,选择 PyMuPDF。
- 只要无损结构优化,选择 pikepdf。
- 需要真正压缩图片、把大扫描件变小,推荐在 Python 中调用 Ghostscript 子进程,这也是下一节实战脚本的核心方案。
三种方案并不冲突,完全可以在同一个流程里先无损优化,再做有损压缩。不过要注意,处理加密的 PDF 时必须确认你有合法授权,不要尝试绕过权限保护。
6. 实战:带日志和自动备份的批量压缩脚本
6.1 脚本需求与设计
本节我们把前面的知识整合成一个可以直接用到项目里的脚本。脚本需要满足:
- 扫描指定目录下所有 PDF 文件。
- 用 Ghostscript 批量压缩,输出到
compressed子目录,不覆盖原文件。 - 对每个文件记录压缩前后体积和压缩率。
- 压缩失败时不中断整体任务,并删除失败产生的半成品输出。
- 如果输出文件已存在,自动跳过,方便断点重跑。
设计上尽量保持简单,核心逻辑放在compress_pdf和main两个函数中,后续加参数、加并行处理都很方便。
6.2 核心脚本代码
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 文件路径:pdf-compressor/pdf_compressor.py """ 批量压缩 PDF 文档:基于 Ghostscript 用法: python pdf_compressor.py <输入目录> [quality] quality 可选:screen / ebook / printer / prepress,默认 ebook """ import logging import subprocess import sys from pathlib import Path logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) # Windows 下如果 gs 不在 PATH,改成完整路径 # 示例:r"C:\Program Files\gs\gs10.xx\bin\gswin64c.exe" GS_COMMAND = "gs" QUALITY_MAP = { "screen": "/screen", "ebook": "/ebook", "printer": "/printer", "prepress": "/prepress", } def compress_pdf(in_path: Path, out_path: Path, quality: str = "ebook") -> bool: """用 Ghostscript 压缩单个 PDF,成功返回 True,失败返回 False。""" cmd = [ GS_COMMAND, "-sDEVICE=pdfwrite", f"-dPDFSETTINGS={QUALITY_MAP.get(quality, '/ebook')}", "-dCompatibilityLevel=1.4", "-dDetectDuplicateImages=true", "-dEmbedAllFonts=true", "-dSubsetFonts=true", "-dNOPAUSE", "-dQUIET", "-dBATCH", f"-sOutputFile={out_path}", str(in_path), ] logging.info("开始压缩: %s", in_path.name) result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: logging.error("压缩失败: %s,错误信息: %s", in_path.name, result.stderr[-500:]) return False return True def main() -> None: if len(sys.argv) < 2: print("用法: python pdf_compressor.py <输入目录> [quality]") sys.exit(1) src_dir = Path(sys.argv[1]).resolve() if not src_dir.is_dir(): print(f"目录不存在: {src_dir}") sys.exit(1) quality = sys.argv[2] if len(sys.argv) > 2 else "ebook" out_dir = src_dir / "compressed" out_dir.mkdir(exist_ok=True) for pdf_file in sorted(src_dir.glob("*.pdf")): out_file = out_dir / pdf_file.name if out_file.exists(): logging.info("已存在输出文件,跳过: %s", out_file.name) continue if compress_pdf(pdf_file, out_file, quality): before_mb = pdf_file.stat().st_size / 1024 / 1024 after_mb = out_file.stat().st_size / 1024 / 1024 ratio = (1 - after_mb / before_mb) * 100 if before_mb > 0 else 0 logging.info( "完成: %s -> %.2f MB / %.2f MB(压缩 %.1f%%)", pdf_file.name, before_mb, after_mb, ratio, ) else: # 失败时删除半成品,避免下次误判 out_file.unlink(missing_ok=True) if __name__ == "__main__": main()这个脚本有几个细节值得注意:
- 输出到
compressed子目录,而不是覆盖原文件,这样即使压缩出错,原始 PDF 也不会丢。 subprocess.run捕获了标准错误,失败时只打印最后 500 个字符,方便定位原因。- 已存在输出文件就跳过,可以实现断点重跑。
- Windows 下如果命令找不到,把
GS_COMMAND改成完整路径。
6.3 运行与验证
把脚本保存后,在终端执行:
python pdf_compressor.py /path/to/pdf_dir ebook预期的日志类似下面:
2025-01-12 10:00:01 [INFO] 开始压缩: 合同扫描版.pdf 2025-01-12 10:00:05 [INFO] 完成: 合同扫描版.pdf -> 25.4 MB / 8.1 MB(压缩 68.1%) 2025-01-12 10:00:06 [INFO] 开始压缩: 产品手册.pdf 2025-01-12 10:00:09 [INFO] 完成: 产品手册.pdf -> 12.2 MB / 5.6 MB(压缩 54.1%)运行结束后,一定要抽查输出文件:
- 用 PDF 阅读器打开几份,确认页数、文字、图片没有明显异常。
- 对比压缩前后文件大小,确认是不是真的变小。
- 如果某些文件压缩率极低,说明它们可能是纯矢量文件,或者图片已经是高压缩格式,不需要强压。
6.4 扩展改进方向
这个脚本是一个最小可用版本,实际项目中可以根据需要扩展:
- 添加目录树递归扫描,使用
src_dir.rglob("*.pdf")。 - 支持按文件大小过滤,只处理大于某个阈值的文件。
- 增加
--resume参数,配合跳过逻辑实现断点续传。 - 使用
ThreadPoolExecutor并行压缩,同时处理多份文件,但要注意 Ghostscript 本身比较吃 CPU,建议并发数控制在可用核心数之内。 - 输出 CSV 格式的压缩报告,方便后续统计。
扩展时保持“不覆盖原文件”和“保留日志”两个底线,其余都可以按需求灵活调整。
7. 常见问题与排查思路
7.1 问题速查表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 压缩后文件反而变大 | PDF 主要是矢量内容,或图片已是高压缩格式 | 改用 qpdf 无损优化,或调高图片分辨率参数 |
| 压缩后文字变方框 | 字体未嵌入或子集化失败 | 增加 -dEmbedAllFonts=true 和 -dSubsetFonts=true |
| Windows 下 gs 命令找不到 | PATH 未配置 Ghostscript | 使用 gswin64c.exe 完整路径 |
| 中文文件名乱码 | 控制台编码不一致 | 使用 Python 脚本,或执行 chcp 65001 |
| 扫描件压缩不明显 | 图片分辨率未有效降低 | 调整 -dColorImageResolution 和 -dJPEGQ |
| 压缩进程卡在某个文件 | 文件损坏、权限不足或带密码 | 单独测试该文件,确认是否有合法授权 |
| 输出 PDF 页数变少 | 参数不当或原文件结构异常 | 备份原文件,先小范围测试再全量处理 |
7.2 典型问题详解
第一个高频问题:压缩后文件变大。很多人想不通,为什么明明执行了压缩,文件却更大了。原因通常是这个 PDF 已经是高度压缩过的,Ghostscript 重新输出时反而增加了冗余对象或者加密、嵌入元数据。遇到这种情况,先运行 qpdf 无损优化看看,如果还是大,就不要对其强行压缩。
第二个高频问题:压缩后文字变成方框。这通常是字体没有嵌入导致的,Ghostscript 处理时未保留原字体,换一台机器打开就会缺字。解决办法是显式加上字体参数,并避免使用过于激进的 PDFSETTINGS 档位。严谨的做法是压缩前记录原文件页数和字体列表,压缩后逐项对比。
第三个高频问题:批量脚本中途失败。排查时先看日志,如果 Ghostscript 报错,单独运行一份文件复现问题。如果单份文件可以,说明是批量脚本的路径或文件名处理有问题,重点检查特殊字符、空格和目录权限。
8. 最佳实践与工程建议
8.1 文件安全和备份
批量压缩涉及大量文件的修改,安全是第一优先级。无论脚本写得多好,都建议遵守下面几条纪律:
- 永远保留原始文件,压缩结果输出到新目录,不要直接覆盖输入文件。
- 批量处理前先做小规模测试,熟悉参数效果后再全量执行。
- 敏感材料使用本地工具处理,不要上传到在线网站。
- 处理带密码的 PDF 时,先确认你有合法授权,再用合法的密码方式打开,不要试图绕过加密保护。
- 备份可以简单到“复制一份到 backup 目录”,关键时候能救命。
8.2 压缩参数取舍
参数的取舍取决于你对画质和体积的优先级。下面是几条经过实践检验的经验:
- 日常传输优先使用
/ebook,它不会太损耗文字清晰度,体积也相对合理。 - 如果是高清扫描件,自定义图片分辨率比固定档位更可控。例如彩色图片降到 100-150 DPI 已经能满足大多数阅读场景。
- 不要反复压缩。第一次压缩可能减少 60%,第二次往往只有 5% 到 10%,还会明显损失画质。
- 压缩后抽查关键页面,特别是扫描件的盖章、签字区域,确保法律意义上的完整性没有问题。
- 记录每次使用的参数和版本,保证同样的输入可以得到可复现的输出。
8.3 自动化与集成
批量压缩 PDF 很适合放进自动化流程。如果是在服务器上定期压缩文件,可以用 cron 或计划任务跑脚本;如果是在 CI 流水线里压缩交付物,可以把 Ghostscript 参数固化成变量,让过程清晰可控。
自动化流程中还要考虑日志和监控。最简单的方式是把脚本日志重定向到文件,例如:
python pdf_compressor.py ./docs ebook >> compress.log 2>&1这样即使任务在深夜运行,第二天也能从日志里看到每个文件的压缩结果。日志里建议包含文件名、体积变化、耗时和错误信息,方便日后统计和历史追溯。
8.4 安全提醒
Ghostscript 历史上出现过由恶意 PDF 触发的问题,不建议使用太老的版本处理来源不明的文件。生产环境尽量使用官方最新稳定版,并且不要让脚本以过高权限运行。处理不可信来源的 PDF 时,最好在隔离的容器或沙箱中执行,避免潜在风险。
9. 总结与下一步
批量压缩 PDF 文档这个需求,核心思路就三条:图片多的文件用 Ghostscript 做有损重压缩;内容需要完整保留用 qpdf 做无损优化;需要自动化时用 Python 把这些能力串起来。文中给出的完整脚本可以直接运行,也可以按需扩展成带递归、并发、报告输出的工具。
下一步建议先拿几份有代表性的文件测试,记录不同档位下的压缩率和画质,找到适合自己业务的默认参数。之后再考虑把脚本接到实际工作流中,把这些经验固化下来。如果本文对你有帮助,可以收藏备用,下次遇到大量 PDF 需要压缩时直接照着操作就好。