📝 前言
在日常工作中,我们经常会遇到 PDF 文件太大、不便于传输或上传的问题。虽然网上有许多 PDF 压缩工具,但:
-
有些要付费
-
有些限制文件大小
-
有些需要上传到不可信的网站
因此,使用 Python + Ghostscript 自己写一个"本地 PDF 压缩工具"就非常有必要。
本文提供一个 完整的可运行脚本:
✔ 支持 单文件压缩
✔ 支持 批量扫描目录压缩 (可逐个确认)
✔ 支持选择不同压缩等级
✔ 自动检测 Ghostscript
✔ 输出压缩结果总结
非常适合作为实用脚本收藏!
✨ 功能特点
🔥 1. 支持四种压缩等级
-
/screen:最低质量,体积最小 -
/ebook:推荐,平衡质量 -
/printer:适合打印 -
/prepress:最高质量(印刷)
🔥 2. 支持两种输入模式
-
输入文件路径 → 压缩单个 PDF
-
直接回车 → 自动扫描
output_pdfs目录批量处理
🔥 3. 逐个确认压缩
每个文件你都可以选择:
-
y压缩 -
n跳过 -
a剩余全部压缩 -
q退出
🔥 4. 自动检测 Ghostscript
支持 Windows / macOS / Linux。
🧠 程序代码(可直接运行)
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import subprocess
import sys
import platform
import shutil
# ===== 配置 =====
BATCH_INPUT_DIR = "output_pdfs" # 批量模式默认扫描此文件夹
output_dir = "pdf_output" # 输出目录
# 压缩等级选项
COMPRESSION_LEVELS = {
"1": ("/screen", "屏幕查看 (72dpi, 最小文件)"),
"2": ("/ebook", "电子书 (150dpi, 平衡质量)"),
"3": ("/printer", "打印 (300dpi, 高质量)"),
"4": ("/prepress", "印刷 (300dpi+, 最高保真)")
}
DEFAULT_LEVEL = "2"
# ===== 交互式选择压缩等级 =====
def select_compression_level():
print("\n请选择压缩等级:")
for k, (_, desc) in COMPRESSION_LEVELS.items():
print(f" {k}. {desc}")
print(f"默认: {COMPRESSION_LEVELS[DEFAULT_LEVEL][1]}")
while True:
choice = input(f"\n请输入编号 (1-4) [回车默认 {DEFAULT_LEVEL}]: ").strip()
if not choice:
choice = DEFAULT_LEVEL
if choice in COMPRESSION_LEVELS:
level, desc = COMPRESSION_LEVELS[choice]
print(f"\n已选择: {desc}")
return level
print("无效输入,请输入 1-4")
# ===== 自动检测 Ghostscript =====
def find_gs_command():
system = platform.system()
candidates = ["gs"] if system != "Windows" else ["gswin64c", "gswin32c", "gs"]
for cmd in candidates:
if shutil.which(cmd):
return cmd
return None
gs_cmd = find_gs_command()
if not gs_cmd:
print("未找到 Ghostscript!")
print("\n请先安装:")
if platform.system() == "Windows":
print(" 下载:https://www.ghostscript.com/download/gsdnld.html")
elif platform.system() == "Darwin":
print(" brew install ghostscript")
else:
print(" sudo apt install ghostscript")
sys.exit(1)
print(f"使用 Ghostscript: `{gs_cmd}`")
# ===== 选择输入:单文件 or 批量 =====
print("\n" + "="*60)
print(" PDF 压缩工具 - 逐个确认压缩")
print("="*60)
input_path = input(f"\n输入 PDF 文件路径(或回车扫描 `{BATCH_INPUT_DIR}`): ").strip()
# 收集待处理文件
if input_path:
if not os.path.exists(input_path):
print("文件不存在")
sys.exit(1)
if not input_path.lower().endswith(".pdf"):
print("仅支持 .pdf")
sys.exit(1)
pdf_files = [(os.path.basename(input_path), input_path)]
print(f"单文件模式: {input_path}")
else:
if not os.path.isdir(BATCH_INPUT_DIR):
print(f"目录不存在: {BATCH_INPUT_DIR}")
sys.exit(1)
pdf_files = [
(f, os.path.join(BATCH_INPUT_DIR, f))
for f in os.listdir(BATCH_INPUT_DIR)
if f.lower().endswith(".pdf") and os.path.isfile(os.path.join(BATCH_INPUT_DIR, f))
]
if not pdf_files:
print(f"`{BATCH_INPUT_DIR}` 中无 PDF 文件")
sys.exit(0)
print(f"批量模式: 共 {len(pdf_files)} 个文件")
# ===== 选择压缩等级 =====
compression_level = select_compression_level()
# ===== 创建输出目录 =====
os.makedirs(output_dir, exist_ok=True)
# ===== 逐个确认压缩 =====
failed = []
skipped = []
compressed = []
print("\n" + "-"*60)
print("开始处理(输入 y=压缩, n=跳过, a=全部压缩, q=退出)")
print("-"*60)
all_yes = False
for idx, (filename, input_path) in enumerate(pdf_files, 1):
if all_yes:
action = 'y'
else:
while True:
prompt = f"\n[{idx}/{len(pdf_files)}] 压缩 '{filename}'? (y/n/a/q): "
action = input(prompt).strip().lower()
if action in {'y', 'n', 'a', 'q'}:
break
print("请输入 y, n, a 或 q")
if action == 'q':
print("用户退出")
break
if action == 'n':
print(f"跳过: {filename}")
skipped.append(filename)
continue
if action == 'a':
all_yes = True
print(f"全部压缩(剩余 {len(pdf_files)-idx} 个)")
output_path = os.path.join(output_dir, filename)
cmd = [
gs_cmd,
"-sDEVICE=pdfwrite",
"-dCompatibilityLevel=1.4",
f"-dPDFSETTINGS={compression_level}",
"-dNOPAUSE",
"-dQUIET",
"-dBATCH",
f"-sOutputFile={output_path}",
input_path
]
try:
print(f"压缩中: {filename} ...", end="")
subprocess.run(cmd, check=True, capture_output=True, text=True)
print(" 完成")
compressed.append(filename)
except subprocess.CalledProcessError as e:
print(f" 失败")
failed.append(filename)
except Exception as e:
print(f" 错误: {e}")
failed.append(filename)
# ===== 最终总结 =====
total = len(pdf_files)
done = len(compressed) + len(skipped) + len(failed)
print("\n" + "="*60)
print("压缩总结")
print(f" 总文件: {total}")
print(f" 已压缩: {len(compressed)}")
print(f" 已跳过: {len(skipped)}")
print(f" 失败: {len(failed)}")
if failed:
print(f" 失败文件: {', '.join(failed)}")
if skipped:
print(f" 跳过文件: {', '.join(skipped[:10])}{'...' if len(skipped)>10 else ''}")
print(f" 输出目录: {os.path.abspath(output_dir)}")
print("="*60)
📦 使用效果示例
(把你运行时的终端截图放到这里)
🛠 安装 Ghostscript(必须)
Windows
下载地址:
https://www.ghostscript.com/download/gsdnld.html
macOS
bash
brew install ghostscript
Linux
bash
sudo apt install ghostscript
❓ 常见问题
Q1:压缩后图片模糊怎么办?
使用 /printer 或 /prepress:
markdown
3. 打印模式(300dpi)
4. 印刷模式(最高质量)
🎯 总结
这是一个非常实用的 Python 工具脚本:
-
零依赖(只需要 Ghostscript)
-
支持批量、单文件
-
支持交互式选择
-
安全(完全本地)