首页
/ OCRmyPDF 批量处理实战:GNU Parallel 批处理、watcher.py 热文件夹监控与 Docker 服务部署

OCRmyPDF 批量处理实战:GNU Parallel 批处理、watcher.py 热文件夹监控与 Docker 服务部署

2026-09-05 17:15:44作者:宣海椒Queenly

本文围绕 OCRmyPDF 的批量处理方案展开,覆盖用 GNU Parallel 和 find 对多文件并行执行 OCR、用 Docker 流式处理整棵目录树,以及如何把 watcher.py 热文件夹服务跑在本机或 Docker 容器中实现"文件落地即自动 OCR"。读完本文,你可以按自己的硬件与部署形态(裸机、NAS、Docker、macOS 自动化)选择并落地的批量 OCR 流水线,并理解 watcher 的环境变量配置背后的源码实现。

1. 用 GNU Parallel 执行批量作业

对多个文件一次性应用 OCRmyPDF,推荐借助 GNU Parallel。关键点是:parallelocrmypdf 各自都会尝试用满所有可用处理器,为避免进程把系统压垮,建议用 -j 2 限制 parallel 同时只跑两个作业。

以下命令对当前目录中所有 *.pdf 文件执行 ocrmypdf,结果写入事先创建好的 output/ 目录,且不会搜索子目录:

parallel --tag -j 2 ocrmypdf '{}' 'output/{}' ::: *.pdf

其中 --tag 让 parallel 在打印任何消息时都带上文件名前缀,这样日志中的任何错误都能追溯到产生它的那个文件。另外需要说明:OCRmyPDF 在解析 PDF、收集页面信息之前会自动尝试修复(repair)损坏的 PDF,因此一批文件里混入少量格式不规范的扫描件也不会让整个批量作业失败。

2. 目录树的批量处理

2.1 用 find 原地处理

遍历目录树并对所有文件"原地"执行 OCR(输入与输出指向同一文件),每处理完一个文件打印一次文件名:

find . -name '*.pdf' -printf '%p\n' -exec ocrmypdf '{}' '{}' \;

注意这个写法同一时间只运行一个 ocrmypdf 进程。若要并行化,可以改用 find 生成文件列表、parallel 负责并行执行,仍然是原地更新:

find . -name '*.pdf' | parallel --tag -j 2 ocrmypdf '{}' '{}'

2.2 Windows 批处理

在 Windows 的 batch 文件中使用递归 for 循环即可:

for /r %%f in (*.pdf) do ocrmypdf %%f %%f

2.3 Docker 容器:标准输入/输出流式处理

如果通过 Docker 容器处理目录树,则必须经由标准输入/输出流式传输文件,因为容器内部无法直接看到宿主机路径:

find . -name '*.pdf' -print0 | xargs -0 | while read pdf; do
    pdfout=$(mktemp)
    docker run --rm -i jbarlow83/ocrmypdf - - <$pdf >$pdfout && cp $pdfout $pdf
done

该模式与仓库中 misc/synology.py 的做法一致:Synology NAS 脚本正是通过 docker run --rm -i jbarlow83/ocrmypdf --deskew - - 把本地 PDF 从 stdin 喂给容器、从 stdout 取回 OCR 结果,再写回宿主机。

3. 仓库内置的批处理示例脚本

3.1 misc/batch.py:递归目录批处理模板

misc/batch.py 是一个用户贡献的批处理示例,演示了如何以库的方式调用 ocrmypdf 对整棵目录树做递归 OCR 并记录日志。它的核心逻辑:

  • start_dir.glob("**/*.pdf") 递归收集所有 PDF(start_dir 由命令行第一个参数给出,默认为当前目录);
  • 先调用 ocrmypdf.pdfa.file_claims_pdfa(filename) 检查文档是否已含文本层,若已符合 PDF/A(说明已有文本)则跳过;
  • 需要时先把原件复制归档到 archive_dir(脚本中默认 /pdfbak,可按需修改);
  • 再执行 ocrmypdf.ocr(filename, filename, deskew=True) 原地 OCR;
  • 通过捕获 EncryptedPdfError(加密文档)、PriorOcrFoundError(已含文本)、DigitalSignatureError(含数字签名)、TaggedPDFError(已是 Tagged PDF)等异常分别记录"跳过原因",而不是让整个批次崩溃。

这个脚本的注释明确提示"应当按自己的需求修改脚本",可作为自建批量工具的最短起步模板。

3.2 misc/synology.py:针对 Synology DiskStation 的适配脚本

Synology DiskStation(NAS)在安装 Docker 套件后同样可以运行 OCRmyPDF 镜像。misc/synology.py 是为这类设备编写的示例脚本,要点:

  • os.walk 遍历起始目录,对每个 .pdf 文件生成带时间戳的输出名(%Y-%m-%d-%H%M_OCR_ 前缀);
  • 以 root 身份经 cron 调度执行,通过 subprocess.run 启动 docker run --rm -i jbarlow83/ocrmypdf --deskew - -,把文件内容经 stdin 输入、stdout 输出;
  • 处理完成后对 OCR 结果和原件分别 chmod 0o664(适配 NAS 的共享目录权限约定),再把 OCR 产物移入归档目录、原件移入 no_ocr 子目录。

文档同时提醒:该脚本编写时仅在 x86 架构的 Synology 产品上验证过,在 ARM 产品上能否运行未知,且针对 Synology 相对有限的 CPU 与内存可能还需要进一步调参。

对于有数千个文件量级的超大批量任务,原作者在文档中建议直接联系作者,相关咨询工作也是该开源项目的重要资金来源。

4. 热文件夹(Hot/Watched Folder)

4.1 watcher.py:随源码分发的目录监视器

OCRmyPDF 自带一个文件夹监视器 misc/watcher.py,它包含在源码分发包中,但不是主程序的一部分。它可以本机原生运行,也可以放在 Docker 容器里运行,原生实例通常性能更好,且 watcher.py 支持所有平台。使用者需要按自己的需求对脚本做定制。

安装依赖。 监视器依赖三个额外包,在 pyproject.toml 中定义为可选依赖 watcher

watcher = ["watchdog>=1.0.2", "cyclopts>=3", "python-dotenv"]

因此安装方式为:

# 使用 uv(推荐)
uv sync --extra watcher

# 或使用 pip
pip3 install ocrmypdf[watcher]

运行示例。 以下配置监视 /mnt/input-pdfs,结果按年月写入 /mnt/output-pdfs

env OCR_INPUT_DIRECTORY=/mnt/input-pdfs \
    OCR_OUTPUT_DIRECTORY=/mnt/output-pdfs \
    OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1 \
    python3 watcher.py

watcher.py 环境变量说明(文档官方表格):

环境变量 说明
OCR_INPUT_DIRECTORY 设置要监视的输入目录(递归监视)
OCR_OUTPUT_DIRECTORY 设置输出目录(不应位于输入目录下)
OCR_ARCHIVE_DIRECTORY 设置处理完原件的归档目录(不应位于输入目录下,且要求设置了 OCR_ON_SUCCESS_ARCHIVE
OCR_ON_SUCCESS_DELETE 当退出码为 0(OK)时,把已处理的原件移动到 OCR_ARCHIVE_DIRECTORY;注意该选项优先级高于归档选项,即两者都设置时输入文件会被删除
OCR_OUTPUT_DIRECTORY_YEAR_MONTH 设置后输出文件按 {output}/{year}/{month}/{filename} 存放
OCR_DESKEW 对倾斜的输入 PDF 应用去歪斜(deskew)
OCR_JSON_SETTINGS 一个 JSON 字符串,指定传给 ocrmypdf.ocr 的其他参数,如 'OCR_JSON_SETTINGS={"rotate_pages": true, "optimize": "3"}'
OCR_POLL_NEW_FILE_SECONDS 轮询间隔(秒)
OCR_LOGLEVEL 日志级别

源码层面的补充。misc/watcher.py 的参数定义(main() 函数,L158-L248)可以看到,除了文档表格列出的变量外,源码还暴露了更多可通过环境变量(或同名命令行参数,由 cyclopts 解析)控制的配置:

环境变量 默认值 说明
OCR_INPUT_DIRECTORY /input 输入目录
OCR_OUTPUT_DIRECTORY /output 输出目录
OCR_ARCHIVE_DIRECTORY /processed 归档目录
OCR_USE_POLLING False 使用轮询(PollingObserver)替代文件系统事件,适合不支持 inotify 类事件的挂载
OCR_RETRIES_LOADING_FILE 5 文件就绪前的最大重试次数
OCR_PATTERNS *.pdf,*.PDF 要监视的文件模式(逗号分隔)
OCR_LOGLEVEL INFO 取值为 DEBUG/INFO/WARNING/ERROR/CRITICAL

几个值得注意的实现细节:

  1. 文件就绪等待。 wait_for_file_ready()(misc/watcher.py L63-L88)会循环尝试用 pikepdf.Pdf.open 打开新文件,失败则按 OCR_POLL_NEW_FILE_SECONDS 间隔重试。注释说明这是为了应对 Docker 场景:容器有时会在文件真正落盘完成之前发布 watchdog 事件,直接读取会导致解析失败。
  2. OCR 调用与退出码。 execute_ocrmypdf()(misc/watcher.py L91-L133)通过 ocrmypdf.ocr(ocrmypdf.OcrOptions(input_file=..., output_file=..., **ocrmypdf_kwargs)) 以库方式执行 OCR(对应 src/ocrmypdf/api.py 中的 API 入口,OCR_JSON_SETTINGS 的内容会被展开为 OcrOptions 关键字参数)。仅当返回退出码为 0 时才会执行删除或归档动作;失败时原件保留在输入目录中,便于排查。
  3. 年月归档路径。 get_output_path()(misc/watcher.py L48-L60)在开启 OCR_OUTPUT_DIRECTORY_YEAR_MONTH 时创建 {year}/{两位月} 两级子目录并把输出固定为 .pdf 后缀。
  4. 安全约束。OCR_JSON_SETTINGS 中出现 input_fileoutput_file 键,脚本会直接报错退出——这两个键必须由监视器自身控制,不允许被 JSON 设置覆盖。

仓库自带的 tests/test_watcher.py 以子进程方式启动 misc/watcher.py,向输入目录拷贝测试 PDF 后断言输出文件出现在预期位置(含年月子目录两种情形),可作为行为验证的参考。

一个典型的落地形态是把网络扫描仪或扫描用电脑配置为把文件丢进被监视的文件夹,后续 OCR 完全自动化。

4.2 用 Docker 运行 watcher 服务

watcher 服务已包含在 OCRmyPDF Docker 镜像中,启动命令如下:

docker run \
    --volume <path to files to convert>:/input \
    --volume <path to store results>:/output \
    --volume <path to store processed originals>:/processed \
    --env OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1 \
    --env OCR_ON_SUCCESS_ARCHIVE=1 \
    --env OCR_DESKEW=1 \
    --env PYTHONUNBUFFERED=1 \
    --interactive --tty --entrypoint python3 \
    jbarlow83/ocrmypdf \
    /app/watcher.py

watcher Docker 参数说明:

参数 说明
--volume <待转换文件路径>:/input 放入此位置的文件将被 OCR
--volume <结果存储路径>:/output OCR 结果文件的存放位置
--volume <原件归档路径>:/processed 已处理原件的归档位置
--env OCR_OUTPUT_DIRECTORY_YEAR_MONTH=1 输出文件按 {output}/{year}/{month}/{filename} 存放
--env OCR_ON_SUCCESS_ARCHIVE=1 把已处理原件移入归档目录
--env OCR_DESKEW=1 对倾斜输入 PDF 应用去歪斜
--env PYTHONUNBUFFERED=1 强制 STDOUT 无缓冲,便于在 docker logs 中实时看到消息
--env OCR_LOGLEVEL='DEBUG' 日志级别
--env OCR_JSON_SETTINGS={"language":"deu+eng", "rotate_pages": true} 一个 JSON 字符串,指定传给 ocrmypdf.ocr 的其他参数

权限注意事项: 镜像默认以非 root 的 app 用户(uid 1000)运行,因此除非追加 --user 参数,容器可能没有权限写入 /output/processed 卷。具体取值取决于你使用 rootful Docker、rootless Docker 还是 Podman,可参阅 docs/docker.md 中 "Bind-mounted volumes" 一节的详细说明。

轮询机制与可用性保障。 该服务依赖轮询来检查文件系统变化,因此未必适合所有环境(如慢速网络共享文件系统——此时也可在原生 watcher 中用 OCR_USE_POLLING 明确切换为 PollingObserver)。可以使用 Docker Compose 这类配置管理工具保证服务常驻,仓库中的示例 misc/docker-compose.example.yml 展示了典型配置:

services:
  ocrmypdf:
    restart: always
    container_name: ocrmypdf
    image: jbarlow83/ocrmypdf-alpine
    volumes:
      - "/media/scan:/input"
      - "/mnt/scan:/output"
    environment:
      - OCR_OUTPUT_DIRECTORY_YEAR_MONTH=0
    user: "<SET TO YOUR USER ID>:<SET TO YOUR GROUP ID>"
    entrypoint: python3
    command: /app/watcher.py

其中 user 一项的注释解释了取值逻辑:rootful Docker 填宿主机 uid:gid;rootless Docker 填 0:0(容器 root 映射到宿主机用户);Podman 则填宿主机 uid:gid 并附加 userns_mode: "keep-id"

4.3 注意事项(Caveats)

  • watchmedo 在网络文件系统上可能无法正常工作,具体取决于文件客户端与服务端的能力;
  • 这个简单方案不过滤文件系统事件类型,因此文件复制、删除、移动以及目录操作都会被送入 ocrmypdf,多种情况下会产生错误。如果对监视文件夹做任何"复制文件进去"以外的操作,应先禁用该文件夹的监视;
  • 如果源目录与目标目录相同,watchmedo 可能造成死循环(输出文件再次触发事件);
  • 在 BSD、FreeBSD 和较旧版本的 macOS 上,可能需要用 ulimit -n 1024 提高文件描述符上限,才能监视多达 1024 个文件的文件夹。

4.4 替代方案

  • 在 Linux 上,可以配置 systemd 用户服务,对一批文件自动执行 OCR;
  • Watchman 是 watchmedo 的一个功能更强的替代品。

5. macOS Automator 工作流

在 macOS 上可以用 Automator 应用创建 Workflow 或 Quick Action,在流程中使用 Run Shell Script 动作。需要注意:在 Automator 上下文中,PATH 可能与终端中的不同,可能必须显式设置 PATH 以包含 ocrmypdf。以下截图可作为起步参考(可自定义传给 ocrmypdf 的命令):

macOS Automator 工作流示例:拖放 PDF 触发 ocrmypdf 命令

6. 小结:按场景选择批量方案

场景 推荐方案
一次性处理当前目录/目录树 parallel --tag -j 2 ocrmypdf '{}' 'output/{}' ::: *.pdffind 变体
Windows 环境 for /r %%f in (*.pdf) do ocrmypdf %%f %%f
只装了 Docker、无原生依赖 stdin/stdout 流式 docker run(见 2.3 节)
长期"文件落地即处理" 原生 watcher.py(性能更好)或 Docker 版 watcher + Compose 常驻
Synology NAS misc/synology.py + cron
macOS 桌面 Automator Quick Action

所有脚本类方案都强调一点:仓库提供的示例脚本(batch.pysynology.pywatcher.py)均是可定制的模板,上线前应按自身的目录结构、权限和硬件资源做相应修改。

登录后查看全文
热门项目推荐
相关项目推荐