ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PDF批量压缩实战:Ghostscript与PyMuPDF脚本方案详解

PDF批量压缩实战:Ghostscript与PyMuPDF脚本方案详解

PDF 压缩这件事,绝大多数人第一次接触是从“文件太大传不出去”开始的。单个文件还好说,随便找个在线工具就能解决;但如果手上是几十个、上百个 PDF,逐个上传下载,既慢又容易被各种网站限制大小和次数。真正的问题不是“能不能压”,而是有没有一套可以批量跑、参数可控、效果稳定的方案。

这次我们把这几个问题一次说清:先对比主流批量压缩 PDF 路线的适用场景,再分别给出 Ghostscript 命令行方案、Python + PyMuPDF 脚本方案和在线工具方案,最后补一部分参数调整、质量评估与排查方法。看完之后,你可以根据自己的使用习惯,直接选一条能落地的路线开始处理文件。

先给结论:追求稳定和可控,优先用 Ghostscript;想顺手写进自动化流程,就选 Python + PyMuPDF;只是偶尔压几个几十 MB 的临时文件,在线工具完全够用。核心不是“能不能压缩”,而是压缩之后清晰度是否还够用、批量任务能不能稳定跑完。

1. 核心方案速览

方案适用人群批量能力质量可控程度是否需要安装备注
Ghostscript 命令行有命令行基础的用户强,可配合 bat/shell 脚本处理整个目录高,可设置压缩级别和分辨率参数需要安装 Ghostscript推荐主力方案,稳定且通用
Python + PyMuPDF开发者、数据分析师强,可直接遍历目录并输出报告中高,可自定义重压缩逻辑需要安装 Python 和 PyMuPDF适合集成到业务脚本中
Python + pypdf/pdf2image 组合开发者中上中,依赖额外转图像步骤需要安装多个依赖复杂度偏高,适合特殊需求
在线工具普通办公用户弱,一般需要逐个上传下载低,只能选预设参数无需安装适合临时轻量任务
桌面版 PDF 软件普通办公用户中,部分软件支持批量处理中需要安装商业软件需要授权,注意版权

从整个技术选型来看,Ghostscript 是开源方案里最值得掌握的一个。它本身是 PDF 和 PostScript 的解释器,文件压缩只是它的能力之一,命令行参数稳定,跨平台支持好,也不存在“试用次数上限”这类限制。在线工具虽然现在体验也不错,但批量任务里文件数量一多,光上传下载的等待时间就够受的。

2. 方案一:Ghostscript 命令行批量压缩

Ghostscript(简称 gs)是 PDF 处理领域的老牌开源工具,很多桌面 PDF 软件在底层调用的其实就是它。它的优势在于参数足够丰富:既可以按预设级别压缩,也可以手动控制分辨率和图像质量,适合对输出结果有要求的批量任务。

2.1 安装 Ghostscript

Windows 系统可以直接到 Ghostscript 官方网站下载安装包,安装完成后把安装目录下的bin文件夹加入系统 PATH,或者直接在命令行里写完整路径调用。也可以通过包管理器安装:

# Windows 使用 winget 安装 winget install Ghostscript

Linux 和 macOS 用户用系统包管理器即可:

# Ubuntu / Debian sudo apt install ghostscript # CentOS / RHEL sudo yum install ghostscript # macOS brew install ghostscript

验证是否安装成功:

gs --version

能输出版本号说明安装正常。Windows 下如果安装后提示找不到命令,优先检查 PATH 环境变量或者确认安装包架构是否和系统一致。

2.2 Ghostscript 压缩命令解析

Ghostscript 压缩 PDF 的核心命令格式如下:

gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook \ -dNOPAUSE -dBATCH -sOutputFile=compressed.pdf input.pdf

参数说明:

参数作用
-sDEVICE=pdfwrite指定输出设备为 PDF 写入器,是压缩 PDF 的核心参数
-dCompatibilityLevel=1.4设置输出 PDF 版本兼容级别,1.4 对应 Adobe Acrobat 5 及以上,通用性最好
-dPDFSETTINGS=/ebook预设压缩级别,/screen体积最小画质最差,/ebook中等,/printer质量较高
-dNOPAUSE处理过程中不暂停
-dBATCH处理结束后自动退出
-sOutputFile=compressed.pdf指定输出文件名
input.pdf输入文件路径

补充两个常用参数:如果 PDF 里主要是扫描图片,可以追加-dJPEGQ=80调整 JPEG 图像质量,-r72或-r150可以手动指定输出分辨率(单位是 DPI)。分辨率设得越低,体积越小,同时清晰度会下降,具体网点点需要根据使用场景权衡。

2.3 Windows 批量压缩脚本

下面是一个 Windows 批处理脚本,可以一次性压缩某个文件夹下所有 PDF,并把压缩后的文件输出到output目录:

@echo off setlocal enabledelayedexpansion set INPUT_DIR=.\input set OUTPUT_DIR=.\output set GS_PATH=gs if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%" for %%f in ("%INPUT_DIR%\*.pdf") do ( echo Processing: %%f "%GS_PATH%" -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dBATCH -sOutputFile="%OUTPUT_DIR%\%%~nf_compressed.pdf" "%%f" if !errorlevel! == 0 ( echo OK: %%~nf ) else ( echo FAILED: %%~nf ) ) echo Batch compression finished. pause

使用时把INPUT_DIR和OUTPUT_DIR换成实际路径,GS_PATH如果没加入系统 PATH,就写 Ghostscript 安装目录下的完整路径,例如:

set GS_PATH=C:\Program Files\gs\gs10.03.0\bin\gs.exe

这个脚本会逐文件处理,每个文件生成独立的压缩结果,并在控制台输出成功或失败状态。实际批量运行前,先拿一个试水文件跑一遍。

2.4 Linux / macOS 批量压缩脚本

Linux 或 macOS 下用 bash 脚本即可:

#!/bin/bash INPUT_DIR="./input" OUTPUT_DIR="./output" mkdir -p "$OUTPUT_DIR" for f in "$INPUT_DIR"/*.pdf; do [ -e "$f" ] || continue base=$(basename "$f") name="${base%.pdf}" echo "Processing: $base" if gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook \ -dNOPAUSE -dBATCH -sOutputFile="$OUTPUT_DIR/${name}_compressed.pdf" "$f"; then echo "OK: $base" else echo "FAILED: $base" fi done

保存为compress_pdf.sh,加上执行权限后运行:

chmod +x compress_pdf.sh ./compress_pdf.sh

[ -e "$f" ] || continue这行是为了避免目录里没有 PDF 文件时,脚本把字面量*.pdf也当作文件处理。

3. 方案二:Python + PyMuPDF 批量压缩

如果你平时写 Python 脚本,或者希望把压缩逻辑集成进自己的业务系统里,PyMuPDF(导入名 fitz)是比命令行更灵活的选择。它可以直接打开 PDF、读取页面数量、遍历图像对象,然后把处理结果重新保存为新的 PDF 文件。

3.1 安装 PyMuPDF

pip install pymupdf

安装后可以在 Python 里确认版本:

import fitz print(fitz.__doc__)

PyMuPDF 目前支持 Windows、Linux 和 macOS 的主流 Python 版本。注意它的开源许可是 AGPL,如果用在商业闭源软件里需要评估许可影响,这点后面专门提醒。

3.2 基础压缩逻辑

PyMuPDF 提供一种相对直接的压缩方式:打开 PDF,逐页提取内容,再保存到一个新的输出文件。保存时通过垃圾回收参数和压缩参数来控制体积。下面是一个最简版本:

import fitz def compress_pdf(input_path, output_path, garbage=3, deflate=True): doc = fitz.open(input_path) # garbage=3 表示最激进的清理方式,回收无用对象 doc.save(output_path, garbage=garbage, deflate=deflate) doc.close() compress_pdf("input.pdf", "output.pdf")

这种方式的原理是把 PDF 内部冗余对象清理掉,对文本类 PDF 比较有效。但如果是扫描件或图片型 PDF,单纯的保存操作体积变化不够明显,需要结合图像重压缩进一步处理。

3.3 批量遍历目录并压缩

下面是更完整的批量脚本,支持遍历子目录、输出压缩报告,并且过滤掉已经处理过的文件:

import fitz from pathlib import Path INPUT_ROOT = Path("./input") OUTPUT_ROOT = Path("./output") OUTPUT_ROOT.mkdir(parents=True, exist_ok=True) results = [] for pdf_path in INPUT_ROOT.rglob("*.pdf"): output_path = OUTPUT_ROOT / pdf_path.name if output_path.exists(): print(f"跳过已存在文件: {output_path.name}") continue original_size = pdf_path.stat().st_size try: doc = fitz.open(pdf_path) page_count = len(doc) doc.save(output_path, garbage=3, deflate=True) doc.close() compressed_size = output_path.stat().st_size ratio = (1 - compressed_size / original_size) * 100 results.append({ "file": pdf_path.name, "pages": page_count, "original_mb": round(original_size / 1024 / 1024, 2), "compressed_mb": round(compressed_size / 1024 / 1024, 2), "ratio": round(ratio, 1) }) print(f"OK: {pdf_path.name} | 原大小 {original_size / 1024 / 1024:.2f} MB | " f"压缩后 {compressed_size / 1024 / 1024:.2f} MB | 减少 {ratio:.1f}%") except Exception as e: print(f"失败: {pdf_path.name} | 错误: {e}") results.append({"file": pdf_path.name, "error": str(e)}) print("\n=== 批量压缩完成 ===") for item in results: print(item)

这段脚本的优点是每一步都有输出,批量任务里哪份文件成功、哪份失败一目了然。实际项目里可以在results基础上增加 CSV 或 Excel 导出,方便归档审计。

3.4 图像型 PDF 的深度压缩

PyMuPDF 做图像型 PDF 压缩时,可以先提取页面图像,缩放到合适尺寸并降低 JPEG 质量,再替换回页面。常见做法是把 PDF 页面转成 pixmap,经过缩放后另存为新图,再嵌入新 PDF。这种方式适合扫描件,但需要注意,文字会变成图像一部分,后期无法再选中复制。

给出一个可运行的图像压缩版示例:

import fitz def compress_by_image(input_path, output_path, zoom=1.2, jpg_quality=70): doc = fitz.open(input_path) new_doc = fitz.open() for page in doc: # 渲染当前页面为图像 pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom)) # 新建同尺寸页面 new_page = new_doc.new_page(width=page.rect.width, height=page.rect.height) # 将渲染图像写入新页面 new_page.insert_image(new_page.rect, pixmap=pix) new_doc.save(output_path, garbage=3, deflate=True) new_doc.close() doc.close()

这里zoom是渲染倍率,jpg_quality对应后续图像导出质量,实际按需求调整。要真正控制 JPEG 质量,还需要先把 pixmap 导出为 JPEG 文件,再判断大小,具体可以用 PyMuPDF 的pix.tobytes("jpeg", jpg_quality=70)完成。图像方式压缩率通常高于纯重存方式,但处理时间更长,大文件多页时要注意内存占用。

3.5 批量监听文件夹的自动化方案

如果希望做到“文件丢进文件夹就自动压缩”,可以把上面的脚本包装成 watchdog 监听任务。安装watchdog:

pip install watchdog

然后写一个监听脚本,监控指定目录新增的 PDF 文件,自动调用压缩函数。这种方式适合企业内部文件系统里的自动归档流程,原始文件可以移动到done子目录避免重复处理。

import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler from pathlib import Path class PdfHandler(FileSystemEventHandler): def on_created(self, event): if event.is_directory: return path = Path(event.src_path) if path.suffix.lower() == ".pdf": # 调用前面定义的 compress_pdf pass observer = Observer() observer.schedule(PdfHandler(), "./watch") observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()

关键点在于监听目录和处理目录要分开,否则压缩后新文件出现在同目录会再次触发事件,造成死循环。

4. 方案三:在线工具与桌面软件

在线工具适合临时处理单份文件,优势是零安装、操作直观;劣势是上传下载受网络影响,部分网站对文件大小和数量有限制,涉及敏感资料时也不建议上传到第三方服务器。

常用处理方式:搜索“PDF 压缩”相关在线服务,选择站点后上传 PDF,选择压缩级别,等待处理后下载结果。在线工具给出的“压缩率”通常只是一个估算值,最终结果需要自己下载后对比清晰度。如果文件包含个人隐私或商业保密信息,请先评估上传风险,或者改用本地方案。

桌面版 PDF 软件则适合办公场景,部分商业软件支持批量压缩。如果已经有此类软件授权,可以在软件内选择“批量优化”、“批量缩小文件”等入口完成处理。桌面软件和在线工具相同的问题是无法完全控制底层压缩算法,对某些混合内容 PDF 的处理效果不稳定。

从批量和可控角度衡量,在线工具和桌面软件都不是首选。真正能反复使用、可进自动化的方案,还是集中在命令行和脚本工具上。

5. 批量压缩前的准备与文件管理

5.1 目录规划建议

批量处理 PDF 时,强烈建议把输入、输出、临时目录分开:

project/ ├── input/ # 存放原始 PDF ├── output/ # 存放压缩后的 PDF ├── backup/ # 原始文件的备份(可选) └── logs/ # 处理日志或导出报告

输出文件名建议保留原文件名,再增加_compressed后缀,避免覆盖原始文件。后续如果要对比压缩前后的效果,也方便识别。

5.2 备份策略

压缩过程有极小概率损坏文件,尤其是来源复杂、结构异常的 PDF。批量压缩前应该先备份原文件。最简单的方式是把input文件夹整体复制一份到backup。如果空间有限,也可以把原始 PDF 先打包成 zip 存放在本地。

5.3 小批量试运行

不要直接拿全部几百个文件跑批。正确顺序是:

  1. 挑 2 到 3 个文件做测试。
  2. 确认输出质量符合要求。
  3. 对比压缩前后大小变化。
  4. 确认没有敏感信息泄漏到输出 PDF。
  5. 再放行整个目录的批量任务。

6. 压缩效果评估与参数选择

PDF 压缩的难点在于体积与质量永远需要平衡。评估结果时,可以从三个维度观察:

维度观察方式判断标准
文件大小查看输出文件体积是否达到预期压缩比例
清晰度放大页面的文字和图片边缘是否明显模糊、出现锯齿或色块
可用性打开输出文件并搜索文字内容文本是否可复制、书签是否保留、页面顺序是否正确

如果压缩后文字出现乱码、页面排版错乱,说明参数压缩过狠,需要调低压缩级别。比如从/screen改成/ebook,或将图像质量从 70 提高到 85。

参数选择参考表:

使用场景推荐参数说明
网页预览、移动端阅读/screen或-r72体积最优先,画质要求低
普通文档存储、邮件发送/ebook或-r150通用选择,可读性好
正式打印、归档/printer或-r300清晰度优先,压缩幅度有限

处理带大批量图片的 PDF 时,扫描件通常有较大压缩空间,因为原图往往存在冗余信息;而由程序直接导出的文本型 PDF,压缩后体积变化有限,这属于正常现象,不是说压缩失败了。

7. 常见问题与排查方法

批量压缩 PDF 时最常遇到的问题集中在依赖缺失、文件损坏、输出质量异常三类。下面用表格说明。

问题现象可能原因排查方式解决方案
提示gs不是内部或外部命令Ghostscript 未安装或未加入 PATH运行gs --version,如果提示不存在则未安装重新安装 Ghostscript,或使用完整路径调用
PyMuPDF 导入报错未安装依赖或 Python 版本不兼容pip list查看 pymupdf 是否安装升级 pip 后重新安装pip install --upgrade pymupdf
输出 PDF 体积反而变大原文件已经过优化,或使用了过高参数对比同一文件在不同参数下的输出大小改用/screen级别,或检查原 PDF 是否包含大量已压缩图片
压缩后文字模糊分辨率设置过低放大页面检查清晰度提高分辨率和质量参数,如-r150、/ebook以上级别
批量脚本处理到某个文件卡住文件结构损坏或存在超长路径单独处理该文件,查看报错信息先将该文件用其他工具修复,或从批量目录中移出
输出文件无法打开参数设置不兼容或内存不足查看命令行错误日志降低并发数量,逐文件运行,检查临时磁盘空间
在线工具处理速度极慢文件较大或网络带宽受限观察上传速度和网站排队情况改用本地 Ghostscript 或脚本方案
批量处理生成的日志乱码Windows 控制台编码与脚本字符集不一致检查 bat 文件编码格式bat 文件保存为 ANSI 编码,或使用 UTF-8 并设置chcp 65001

批量脚本里出现单文件失败时,不要让整个任务中断。建议为脚本增加错误收集机制,失败的路径单独记录到failures.txt,最后统一复查原因。

# 在脚本里记录失败项示例(Linux/macOS) if ! gs -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dNOPAUSE -dBATCH \ -sOutputFile="$output_file" "$input_file"; then echo "$input_file" >> failures.txt fi

8. 最佳实践与合规提醒

8.1 工程化处理建议

  • 第一次批量处理时,优先用 3 到 5 个文件验证整套流程,再跑全量。
  • 保留一套最小的可运行命令,后续换机器只要调整路径和版本号即可。
  • 日志和输出报告是排查批量任务的基础,不要只靠肉眼确认。
  • 对已压缩完成的文件,可以定期抽查几个,确认页面数、文件尺寸和打开正常。
  • 如果原始 PDF 涉及个人隐私、商业机密或版权内容,不要使用在线工具上传,优先选择本地方案。

8.2 版权、隐私和安全边界

处理 PDF 时要注意文件的来源。压缩别人发来的文件,最好先确认你是否拥有处理该文件的权利;对外发布或商用压缩后的 PDF,要保证原始内容不存在版权争议。

PyMuPDF 采用 AGPL 许可协议,开源项目集成时通常没有问题,但作为闭源商业产品分发时,需要额外评估开源许可的约束。这也提醒一个细节:选技术方案时要先看许可证,再看功能。

处理含人脸信息或隐私数据的扫描件时,应提前告知相关方并获得授权。批量脚本如果部署在共享目录或服务器上,注意限制目录的访问范围,避免未授权用户将文件带入处理队列。

8.3 结果复核习惯

压缩完成后不要直接删除原始文件。正确流程是:压缩 → 抽查质量 → 确认可用 → 备份原文件 → 归档压缩结果。直接销毁原文件存在不可逆风险,特别是遇到压缩异常时连恢复的余地都没有。

9. 总结与下一步

批量压缩 PDF 的方案已经全部走了一遍。最值得优先验证的是 Ghostscript 命令行方案,安装一个开源工具,用一段 bat 或 shell 脚本就能处理整个目录的 PDF,压缩级别、分辨率、图像质量都能自己控制。如果你平时写 Python,则可以直接把 PyMuPDF 版本的脚本接进文件自动化流程,效果立竿见影。

最容易踩的坑有三个:一是压缩参数设置过狠导致文字模糊;二是输出文件名覆盖原始文件;三是批处理某个异常 PDF 时脚本中断。把这三点提前规避掉,整套流程基本可以稳定运行。

留给你的下一步动作很简单:拿几份不同类型的 PDF(文本型、扫描型、图文混排型各一份),分别用/screen、/ebook、/printer跑一遍,对比体积和清晰度,找到适合自己的参数组合。再加一个建议:把批处理脚本存好,这类需求通常不是一次性的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进