PDF批量删除水印工具:优雅地清理PDF文档中的水印文字

在日常工作和学习中,我们经常会遇到一些带有水印的PDF文档。这些水印可能会影响阅读体验,特别是当它们与正文内容重叠时。我从网上下载了一些论文的压缩包,里面不出意外的有某些论文辅导机构的水印,看着很是讨厌(就像下图一样),网上查了一下,对于水印来说,大多数采用PyMuPDF库,用白色色块覆盖,我也感觉不是很优雅,所以小小折腾了一下,做一个记录。

功能特点

  • 支持递归搜索:自动处理指定文件夹及其所有子文件夹中的PDF文件
  • 精确匹配:只删除完全匹配的文字内容,不会影响其他内容
  • 原地处理:直接修改原文件,节省磁盘空间
  • 安全可靠:使用临时文件机制确保文件安全
  • 错误处理:完善的错误处理机制,确保批处理不会因单个文件失败而中断
  • 详细日志:提供处理过程的详细信息

使用方法

  1. 确保已安装Python环境
  2. 安装必要的依赖:
pip install PyMuPDF
  1. 下载并配置脚本(完整代码见文末):
import fitz  # PyMuPDF 库
import os

# --- 用户配置区 ---

# 1. 在这个列表中,输入您想要删除的所有文字内容
TEXTS_TO_DELETE = [
    "你需要删除的水印文字"
]

# 2. 包含您所有 PDF 文件的文件夹路径
SOURCE_FOLDER = "你的文件路径"
  1. 运行脚本,它会自动:
  • 搜索指定文件夹及其子文件夹中的所有PDF文件
  • 查找并删除指定的水印文字
  • 原地更新处理后的文件
  • 显示处理进度和结果统计

工作原理

  1. 文件搜索:使用os.walk()递归遍历所有文件夹,找出所有PDF文件。
  2. 文本处理
  • 对每个PDF文件的每一页进行扫描
  • 使用PyMuPDF的search_for()方法定位目标文本
  • 通过add_redact_annot()apply_redactions()方法安全地删除文本
  1. 安全保存
  • 使用临时文件机制确保文件安全
  • 只有在新文件成功保存后才替换原文件
  • 出错时自动清理临时文件
  1. 错误处理
  • 支持处理损坏的PDF文件
  • 跳过加密的PDF文件
  • 单页处理错误不影响整体进度
  • 详细的错误日志输出

完整代码

import fitz  # PyMuPDF 库
import os

# --- 用户配置区 ---

# 1. 在这个列表中,输入您想要删除的所有文字内容。
#    脚本会查找并删除所有与列表中字符串完全匹配的文本。
#    您可以添加任意多个字符串。
TEXTS_TO_DELETE = [
    "你需要删除的水印文字"
]

# 2. 包含您所有 PDF 文件的文件夹路径
SOURCE_FOLDER = "你的文件路径"

def process_pdf_file(filepath):
    """
    处理单个PDF文件,删除指定文本并原地覆盖。
    
    Args:
        filepath: PDF文件的完整路径
        
    Returns:
        bool: 是否对文件进行了修改
    """
    filename = os.path.basename(filepath)
    is_modified = False

    try:
        # 尝试打开PDF文件
        doc = fitz.open(filepath)
        print(f"\n正在处理: {filename}...")

        if doc.needs_pass:
            print(f"  -> 跳过:文件 {filename} 有密码保护")
            doc.close()
            return False

        # 遍历PDF的每一页
        for page_num in range(doc.page_count):
            try:
                page = doc[page_num]
            except Exception as e:
                print(f"  -> 警告:第 {page_num + 1} 页访问出错,已跳过此页: {e}")
                continue

            # 对本页应用所有删节标记
            for text in TEXTS_TO_DELETE:
                try:
                    # search_for() 会返回所有匹配文本的矩形区域列表
                    found_instances = page.search_for(text)

                    # 如果找到了匹配项
                    if found_instances:
                        is_modified = True
                        print(f"  -> 在第 {page_num + 1} 页找到并标记了文本: '{text}'")
                        # 为每一个找到的实例添加删节注释
                        for inst in found_instances:
                            try:
                                page.add_redact_annot(inst, fill=(1, 1, 1))
                            except Exception as e:
                                print(f"  -> 警告:在第 {page_num + 1} 页添加删除标记时出错: {e}")
                                continue
                except Exception as e:
                    print(f"  -> 警告:在第 {page_num + 1} 页搜索文本时出错: {e}")
                    continue
            
            # 应用本页的所有删节标记,实现真正的删除
            if is_modified:
                try:
                    page.apply_redactions()
                except Exception as e:
                    print(f"  -> 警告:在第 {page_num + 1} 页应用删除时出错: {e}")
                    continue

        # 如果文件被修改过,则原地覆盖保存
        if is_modified:
            try:
                # 使用临时文件进行保存
                temp_filepath = filepath + ".temp"
                doc.save(temp_filepath, garbage=4, deflate=True, clean=True)
                doc.close()
                
                # 删除原文件并重命名临时文件
                os.remove(filepath)
                os.rename(temp_filepath, filepath)
                
                print(f"  -> 已成功删除指定文本并覆盖原文件")
            except Exception as e:
                print(f"  -> 保存文件时发生错误: {e}")
                # 清理临时文件(如果存在)
                if os.path.exists(temp_filepath):
                    os.remove(temp_filepath)
                return False
        else:
            print(f"  -> 未在该文件中找到任何需要删除的文本,已跳过。")
            doc.close()
        return is_modified

    except Exception as e:
        print(f"  -> 处理文件 {filename} 时发生错误: {e}")
        return False

def batch_delete_text_by_content():
    """
    递归搜索并处理所有PDF文件,删除指定的文本内容。
    """
    total_files = 0
    modified_files = 0

    # 递归遍历所有文件夹
    for root, _, files in os.walk(SOURCE_FOLDER):
        # 筛选出所有PDF文件
        pdf_files = [f for f in files if f.lower().endswith('.pdf')]
        total_files += len(pdf_files)

        # 处理当前文件夹中的所有PDF文件
        for pdf_file in pdf_files:
            filepath = os.path.join(root, pdf_file)
            if process_pdf_file(filepath):
                modified_files += 1

    print(f"\n🎉 处理完成!")
    print(f"共处理了 {total_files} 个PDF文件")
    print(f"其中 {modified_files} 个文件被修改")

# --- 运行脚本 ---
if __name__ == "__main__":
    if "此处填写" in SOURCE_FOLDER:
        print("错误:请先在脚本中设置 'SOURCE_FOLDER' 的正确路径!")
    else:
        batch_delete_text_by_content()

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇