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

功能特点
- 支持递归搜索:自动处理指定文件夹及其所有子文件夹中的PDF文件
 - 精确匹配:只删除完全匹配的文字内容,不会影响其他内容
 - 原地处理:直接修改原文件,节省磁盘空间
 - 安全可靠:使用临时文件机制确保文件安全
 - 错误处理:完善的错误处理机制,确保批处理不会因单个文件失败而中断
 - 详细日志:提供处理过程的详细信息
 
使用方法
- 确保已安装Python环境
 - 安装必要的依赖:
 
pip install PyMuPDF
- 下载并配置脚本(完整代码见文末):
 
import fitz  # PyMuPDF 库
import os
# --- 用户配置区 ---
# 1. 在这个列表中,输入您想要删除的所有文字内容
TEXTS_TO_DELETE = [
    "你需要删除的水印文字"
]
# 2. 包含您所有 PDF 文件的文件夹路径
SOURCE_FOLDER = "你的文件路径"
- 运行脚本,它会自动:
 
- 搜索指定文件夹及其子文件夹中的所有PDF文件
 - 查找并删除指定的水印文字
 - 原地更新处理后的文件
 - 显示处理进度和结果统计
 
工作原理
- 文件搜索:使用
os.walk()递归遍历所有文件夹,找出所有PDF文件。 - 文本处理:
 
- 对每个PDF文件的每一页进行扫描
 - 使用PyMuPDF的
search_for()方法定位目标文本 - 通过
add_redact_annot()和apply_redactions()方法安全地删除文本 
- 安全保存:
 
- 使用临时文件机制确保文件安全
 - 只有在新文件成功保存后才替换原文件
 - 出错时自动清理临时文件
 
- 错误处理:
 
- 支持处理损坏的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()
	