pdf-inspector:10-50 毫秒判定 PDF 是否需要 OCR 的 Rust 解析库
处理 PDF 的流水线常有一个默认动作:不管文件内容是什么,先进 OCR,让 GPU 和视觉模型跑一遍。Firecrawl 开源库 pdf-inspector 给出的替代做法是——先花 10-50 毫秒判断这份 PDF 属于「纯文本 / 扫描件 / 图像 / 混合」中的哪一类,纯文本的本地直接抽取,真正需要时才把特定页面路由给 OCR。按 Firecrawl 的测算,约 54% 的 PDF 本身带可抽取的文本层,根本不需要 OCR。
pdf-inspector 是 Firecrawl(网页抓取与文档解析公司)出品的 Rust 库,GitHub 上 16.7k star / 1.2k fork,MIT 协议,最新版 1.17.0 于 2026-08-21 发布,491 次提交。纯文本 PDF 的本地解析全程 200ms 内完成。在 opendataloader-bench 的 200 份 PDF 基准上,它综合分第一(0.875),并且是全组最快:处理完整语料 0.470 秒,而 PyMuPDF4LLM 用了 17.1 秒、MarkItDown 用了 16.2 秒。
分类先行:判断该抽取还是送 OCR
pdf-inspector 的检测器不整页渲染,只解析 PDF 的 xref 表和页面树,扫各页内容流里的文本操作符(Tj/TJ)与图像操作符(Do),即可判定文档类型:
- TextBased:有可解码文本,本地抽取,生成 Markdown;
- Scanned:没有文本操作符、主体是页面图像,交给 OCR;
- ImageBased:有少量文本信号但主体是图片或轮廓字,优先 OCR;
- Mixed:文本页与图像页混合,只 OCR 需要的那几页。
分类结果除文档类型外还带一个 0.0-1.0 的置信度,以及一份按页的 pages_needing_ocr 列表——具体哪几页缺文本、需要单独送 OCR,而不是整份文档二选一。扫描策略可配置:默认 EarlyExit(扫到第一个非文本页即停)、Full(扫全量,适合精确区分 Mixed 与 Scanned)、Sample(n)(均匀抽 n 页,适合超大型 PDF)、Pages(vec)(只扫指定的页码)。对 300 页以上的 PDF,检测同样在毫秒级完成。
位置感知抽取:把版面还原成结构化 Markdown
判定为纯文本后,抽取器按内容流还原文本的坐标与字体信息:每个 TextItem 带 X/Y 位置、字号、字体样式,再据此重建阅读顺序。它能处理报纸式多栏排版、RTL 文字(阿拉伯语、希伯来语)、跨行连字符重连,也支持财务表格与跨页续表。
Markdown 转换不是把字符按文件顺序倒出来,而是按文档内统计识别结构:标题按相对正文的字号层级映射为 H1-H4(0.5pt 聚类分档),代码块靠等宽字体检测(Courier、Consolas、Monaco 等),列表识别项目符号与数字/字母序号,表格走「矩形网格检测 + 文本对齐启发式」双模式,粗斜体按字体名模式判断,URL 转为 Markdown 链接,页码直接从输出中剔除。
对 CID 字体(Type0/Identity-H 这类日韩中文字体常用编码),它通过 ToUnicode CMap 解码,支持 UTF-16BE、UTF-8、Latin-1。字体编码损坏无法解码时,页面会被标记为 suspected_garbled_text 而不是输出看似正常的乱码,调用方可以据此回退 OCR。
选择性 OCR:按需加载,纯文本页不碰检测模型
在依赖编排上,默认的 Rust 构建与浏览器 WASM 构建保持纯抽取,不打包任何 OCR 组件。Python 与 Node 原生包带 OCR 集成,但 PDFium 与 ONNX Runtime 仍是外部依赖,只有页面被路由到 OCR 时才加载。
OCR 环节在本地跑 PP-OCRv6 Small 模型,只渲染需要 OCR 的页面,保留逐页出处(provenance),并给出继续走托管文档管线的回退建议。以一份 200 页的报告为例:150 页是纯文本时,那 150 页在本地以毫秒/页级完成,一页都不碰 GPU。
同一套核心,五种接法:Python、Node、WASM、Rust、CLI
Python:
# Python
pip install pdf-inspector
import pdf_inspector
result = pdf_inspector.process_pdf("document.pdf")
print(result.pdf_type) # "text_based"
print(result.markdown) # Markdown 字符串
ocr = pdf_inspector.process_pdf_with_ocr("document.pdf") # 纯文本 PDF 不会加载 OCR 运行时
# CLI
cargo install pdf-inspector
pdf2md document.pdf
detect-pdf document.pdf # 只分类,不抽取
detect-pdf document.pdf --analyze --json # 附带版面(表格/多栏)分析
Node.js 装 @firecrawl/pdf-inspector(napi-rs 原生绑定),浏览器装 @firecrawl/pdf-inspector-wasm,在 Web Worker 里跑同一解析器,无需服务器往返。文档只解析一次:load_document_from_path 或 load_document_from_mem 得到的 Document 在检测与抽取阶段共享,不做重复 I/O。
基准:200 份 PDF 的综合分与速度均列第一
Firecrawl 在 opendataloader-bench 语料(200 份 PDF)上对比了五个本地解析引擎(关闭 OCR,仅比原生文本解析,2026-07-31 刷新,测试机为 Apple M4 Pro):
| 引擎 | 综合分 | 阅读顺序 | 表格 TEDS | 200 份耗时 |
|---|---|---|---|---|
| pdf-inspector | 0.875 | 0.915 | 0.814 | 0.470s |
| liteparse | 0.873 | 0.913 | 0.693 | 0.750s |
| opendataloader | 0.831 | 0.902 | 0.489 | 2.569s |
| pymupdf4llm | 0.735 | 0.886 | 0.401 | 17.117s |
| markitdown | 0.589 | 0.844 | 0.273 | 16.165s |
综合分、阅读顺序、表格 TEDS 三项均列第一:综合分 0.875 比第二位 liteparse 高 0.002,阅读顺序 0.915 比 liteparse 的 0.913 略高,表格 0.814 明显领先;速度是第二名的 1.6 倍、PyMuPDF4LLM 的 36 倍。--compact 模式会折叠目录这类连续点线,输出对 LLM 更省 token;--pages 可只输出指定页面,--select-pages 1,3,5-10 支持区间。
适合放进哪类流水线
官方 README 对它的定位是报告、研究论文、财务文档、发票、法律 PDF 的本地解析默认项——这类文件大多是软件生成的,自带完整文本层,需要的正是干净、结构化的 Markdown,又不该为它们承担 OCR 的延迟与成本。如果流水线里目前是「上传的 PDF 一律过 OCR」,pdf-inspector 起的就是路由层作用:判断 + 本地抽取,只有扫描页才送视觉模型。
一句话带走:先花 10-50 毫秒判断 PDF 需不需要 OCR,让约 54% 的纯文本文档在本地 200ms 内完成抽取,是批量文档流水线省成本、降延迟最直接的改动之一。