基于Python实现简单的PDF批量压缩工具

 更新时间:2025年12月01日 09:34:35   作者:weixin_46244623  
在日常工作中,我们经常会遇到 PDF 文件太大、不便于传输或上传的问题,下面我们就来看看如何使用Python实现PDF批量压缩,支持逐个确认,批量处理,多级压缩

前言

在日常工作中,我们经常会遇到 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。

程序代码(可直接运行)

#!/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:下载地址

macOS

brew install ghostscript

Linux

sudo apt install ghostscript

常见问题

Q1:压缩后图片模糊怎么办?

使用 /printer 或 /prepress:

  • 打印模式(300dpi)
  • 印刷模式(最高质量)

总结

这是一个非常实用的 Python 工具脚本:

  • 零依赖(只需要 Ghostscript)
  • 支持批量、单文件
  • 支持交互式选择
  • 安全(完全本地)

以上就是基于Python实现简单的PDF批量压缩工具的详细内容,更多关于Python PDF压缩的资料请关注脚本之家其它相关文章!

相关文章

  • Python实现arctan换算角度的示例

    Python实现arctan换算角度的示例

    本文主要介绍了Python实现arctan换算角度的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • pytorch张量和numpy数组相互转换

    pytorch张量和numpy数组相互转换

    在使用pytorch作为深度学习的框架时,经常会遇到张量tensor和矩阵numpy的类型的相互转化的问题,本文主要介绍了pytorch张量和numpy数组相互转换,感兴趣的可以了解一下
    2024-02-02
  • 深入理解python虚拟机如何实现闭包

    深入理解python虚拟机如何实现闭包

    当能够从设计者的层面去理解闭包就再也不用死记硬背一些闭包的概念了,所以本文就来从虚拟机层面和大家一起讨论函数闭包是如何实现的
    2023-10-10
  • Python快速转换numpy数组中Nan和Inf的方法实例说明

    Python快速转换numpy数组中Nan和Inf的方法实例说明

    今天小编就为大家分享一篇关于Python快速转换numpy数组中Nan和Inf的方法实例说明,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • Python利用pandas和matplotlib实现绘制堆叠柱状图

    Python利用pandas和matplotlib实现绘制堆叠柱状图

    在数据可视化中,堆叠柱状图是一种常用的图表类型,它能够清晰地展示多个类别的数据,本文将演示如何使用 Python 的 pandas 和 matplotlib 库绘制优化的堆叠柱状图,需要的可以参考下
    2023-11-11
  • 解决NameError:name'pip'is not defined使用pip时报错问题

    解决NameError:name'pip'is not defined使用pip

    使用pip时遇到NameError:name ‘pip’ is not defined错误通常是由于在Python环境内直接尝试运行pip命令导致的,正确的做法是在Python外部的命令行中运行pip命令,这个错误提醒我们在使用pip时,应确保在正确的环境中执行相关命令
    2024-10-10
  • 基于keras中import keras.backend as K的含义说明

    基于keras中import keras.backend as K的含义说明

    这篇文章主要介绍了keras中import keras.backend as K的含义说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-05-05
  • 网易有道2017内推编程题 洗牌(python)

    网易有道2017内推编程题 洗牌(python)

    这篇文章主要为大家详细介绍了网易有道2017内推编程题:洗牌,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06
  • 关于Python字典(Dictionary)操作详解

    关于Python字典(Dictionary)操作详解

    这篇文章主要介绍了关于Python字典(Dictionary)操作详解,Python字典是另一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型,需要的朋友可以参考下
    2023-04-04
  • Python Json数据文件操作原理解析

    Python Json数据文件操作原理解析

    这篇文章主要介绍了Python Json数据文件操作原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05

最新评论