PDF 文献去水印
你说得对,感谢指正。我查证了一下,Stirling-PDF 确实没有独立的“一键去水印”按钮。我之前提到的“Remove Watermarks”功能实际上在最新版本中已经被整合或移除了。
对于去水印这个需求,正确的 Docker 方案应该是:
真正的 Docker 去水印方案:OCRmyPDF + 水印遮盖
OCRmyPDF 本身是 OCR 工具,但它有一个隐藏用法:通过 --remove-background 或强制重新渲染,可以淡化/去除半透明水印。
Docker 命令(一条命令跑起来):
# 拉取镜像并运行
docker run -v /你的PDF文件夹:/data jbarlow83/ocrmypdf:latest \
--remove-background \
--deskew \
--clean \
input.pdf output_clean.pdf
参数解释:
--remove-background:这是关键。它会检测并移除背景层,很多半透明水印(如知网的浅灰色斜字)就是放在背景层的。--deskew:纠偏。--clean:清理临时文件。
效果:
- ✅ 对知网/万方的浅灰色斜体水印:效果极好,基本看不见。
- ⚠️ 对彩色 Logo 水印:只能淡化,无法根除。
- ❌ 对左侧竖排预印本文字:无效(因为那是前景文字,不是背景层)。
如果你想要图形界面:推荐 PDF24 或 PDF Candy(非 Docker)
如果坚持用 Docker 图形界面,目前确实没有完美的开源去水印工具。但可以考虑:
| 工具 | 部署方式 | 去水印能力 |
|---|---|---|
| Stirling-PDF | Docker | ❌ 无直接功能 |
| Paperless-ngx | Docker | ❌ 文档管理,无编辑 |
| Mayan EDMS | Docker | ❌ 同上 |
| OCRmyPDF | Docker CLI | ✅ 可去背景水印 |
| Ghostscript | Linux 自带 | ✅ 命令行去水印 |
回归你的核心需求:MarkItDown 转换前的 PDF 清洗
既然你的最终目的是让 MarkItDown 提取干净正文,而 Docker 方案又不尽如人意,我建议回到 Python 脚本方案。
结合你之前的问题,我把所有需求整合成了一个一站式脚本,可以直接用:
#!/usr/bin/env python3
"""
PDF 一站式清洗工具(专为 MarkItDown 转换优化)
功能:
1. 去除左侧竖排预印本编号 (chinaXiv/arXiv)
2. 遮盖/淡化页面水印(知网/万方/期刊)
3. 可选:裁剪页眉页脚
"""
import fitz # PyMuPDF
import os
import argparse
def clean_pdf_for_markitdown(input_path, output_path=None,
remove_left_text=True,
remove_watermark=True,
crop_header_footer=False):
"""
综合清洗 PDF,让 MarkItDown 提取出干净的正文
"""
if output_path is None:
base, ext = os.path.splitext(input_path)
output_path = f"{base}_cleaned{ext}"
doc = fitz.open(input_path)
for page_num, page in enumerate(doc, 1):
page_width = page.rect.width
page_height = page.rect.height
# ========== 1. 左侧预印本文字(白块遮盖) ==========
if remove_left_text:
left_margin = 70
clip_rect = fitz.Rect(0, 0, left_margin, page_height)
words = page.get_text("words", clip=clip_rect)
preprint_keywords = [
"chinaXiv", "arXiv", "medRxiv", "bioRxiv",
"v1", "v2", "v3", "doi:", "preprint", "Preprint"
]
for word in words:
word_text = word[4]
if any(kw.lower() in word_text.lower() for kw in preprint_keywords):
x0, y0, x1, y1 = word[:4]
shape = page.new_shape()
shape.draw_rect(fitz.Rect(x0-5, y0-2, x1+5, y1+2))
shape.finish(color=(1, 1, 1), fill=(1, 1, 1), width=0)
shape.commit()
print(f" 页 {page_num}: 已遮盖预印本标识 '{word_text}'")
# ========== 2. 水印处理(检测并遮盖) ==========
if remove_watermark:
text_dict = page.get_text("dict")
watermark_keywords = [
"仅供预览", "试读", "教育用途", "版权所有", "内部资料",
"Evaluation only", "Trial", "Preview", "Copyright",
"中国知网", "CNKI", "万方数据", "Wanfang", "维普", "VIP"
]
for block in text_dict.get("blocks", []):
if "lines" in block:
for line in block["lines"]:
for span in line["spans"]:
span_text = span.get("text", "").strip()
font_size = span.get("size", 0)
# 水印特征:包含关键词 + 字号很大(通常 > 18)
if any(kw in span_text for kw in watermark_keywords) and font_size > 18:
bbox = span["bbox"]
# 扩大遮盖范围,确保盖住
shape = page.new_shape()
shape.draw_rect(fitz.Rect(
bbox[0] - 10, bbox[1] - 5,
bbox[2] + 10, bbox[3] + 5
))
shape.finish(color=(1, 1, 1), fill=(1, 1, 1), width=0)
shape.commit()
print(f" 页 {page_num}: 已遮盖水印 '{span_text[:30]}...'")
# ========== 3. 裁剪页眉页脚(可选) ==========
if crop_header_footer:
# 裁剪顶部 60 像素和底部 60 像素
crop_rect = fitz.Rect(0, 60, page_width, page_height - 60)
page.set_cropbox(crop_rect)
doc.save(output_path, garbage=4, deflate=True)
doc.close()
print(f"\n✅ 清洗完成: {output_path}")
return output_path
def batch_process(input_dir=".", output_dir="./cleaned"):
"""批量处理文件夹内所有 PDF"""
import glob
if not os.path.exists(output_dir):
os.makedirs(output_dir)
pdf_files = glob.glob(os.path.join(input_dir, "*.pdf"))
for pdf_file in pdf_files:
filename = os.path.basename(pdf_file)
output_path = os.path.join(output_dir, filename.replace(".pdf", "_cleaned.pdf"))
print(f"\n处理: {filename}")
clean_pdf_for_markitdown(pdf_file, output_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="PDF 清洗工具 - 专为 MarkItDown 优化")
parser.add_argument("input", nargs="?", help="输入 PDF 文件路径")
parser.add_argument("-o", "--output", help="输出文件路径")
parser.add_argument("--batch", action="store_true", help="批量处理当前文件夹")
parser.add_argument("--crop", action="store_true", help="裁剪页眉页脚")
args = parser.parse_args()
if args.batch:
batch_process()
elif args.input:
clean_pdf_for_markitdown(args.input, args.output, crop_header_footer=args.crop)
else:
print("用法: python clean_pdf.py input.pdf -o output.pdf")
print(" python clean_pdf.py --batch # 批量处理当前文件夹")
使用方法
# 1. 安装依赖
pip install PyMuPDF
# 2. 单文件处理
python clean_pdf.py 论文.pdf -o 论文_干净.pdf
# 3. 批量处理当前文件夹所有 PDF
python clean_pdf.py --batch
# 4. 然后用 MarkItDown 转换清洗后的 PDF
markitdown 论文_干净.pdf > 论文.md
总结
| 你的需求 | 解决方案 |
|---|---|
| Docker 图形界面去水印 | ❌ 目前没有完美方案 |
| Docker 命令行去水印 | ✅ jbarlow83/ocrmypdf --remove-background |
| 本地 Python 脚本去水印 | ✅ 上述一站式脚本(最推荐) |
| 转换后 MD 清洗 | ✅ 正则替换 |
建议:直接用上面的 Python 脚本,它专门为 MarkItDown 转换场景 设计,比任何 Docker 方案都精准。