最近 GitHub 上有个项目在技术圈热度涨得很快:Firecrawl。原本它做的是把网页抓下来转成干净的 Markdown 喂给大模型,后来新增的 anydoc 能力直接把 PDF、Word、PPT 这类办公文档整份丢进去,出来就是结构清晰的 Markdown。这让我彻底扔掉了用了两年的"截图→OCR→手动整理"老流程。这篇就拆一拆,Firecrawl anydoc 到底是怎么做到的,以及我在实际项目中拿它替代截图 OCR 的完整记录和踩坑经验,适合做知识库、文档分析、RAG 管线的朋友参考。
1. 截图再 OCR 这套老流程,痛点到底在哪
1.1 一次合同扫描件让我彻底崩溃
先说一个真实场景。去年底整理项目合同,几十份 PDF 全是扫描件,没有文字层。我当时的习惯动作很固定:打开文件、找到关键页、截图、丢进 OCR 工具、复制文字到备忘录、手动排格式。一份合同四五页,看着不多,但每页都要截图、识别、清理,遇到印章盖住了关键条款、表格斜着拍、页码混进正文的情况,一次识别结果基本不能直接用。
最崩溃的是表格。合同里的付款计划表、验收标准表,截图 OCR 出来就成了一坨换行混乱的纯文本,哪列是金额、哪行是时间节点,全靠眼睛重新对齐。我算过一次,一份 20 页的扫描版财报,光整理表格就花了三个多小时。这个效率放在"喂给 RAG 做问答"的工作流里完全不可接受——文档问答的核心是保留结构,纯文字流进去,检索效果一定差。
那次之后我开始认真找方案:有没有一个工具,能把整个 PDF 丢进去,输出的不是乱糟糟的文本,而是保留标题层级、表格、代码块的 Markdown?结果就撞上了 Firecrawl 的 anydoc。
1.2 截图 OCR 的四个硬伤
回头看,截图 OCR 在办公文档场景里有四个绕不开的硬伤:
- 分辨率瓶颈。屏幕截图受显示尺寸限制,一张 A4 纸压缩到屏幕宽度后,一行文字的像素高度可能只有十几像素。OCR 对这种低分辨率文字非常敏感,识别率肉眼可见地往下掉,小字号、细体字尤其严重。
- 版式信息丢失。截图本身就是"拍平"的操作,文档里的标题层级、列表嵌套、表格行列、页码全部变成了一张位图。OCR 只能输出文字流,结构信息只能靠人后期脑补。
- 表格基本靠人工。多列表格、合并单元格、跨页表格,截图 OCR 基本无解。即便识别出了行列,语义上的对齐关系也丢了,输出结果需要大量手工修复。
- 批量处理等于噩梦。一页页截图、一张张识别、一段段粘贴,20 页的资料意味着至少 20 次重复劳动。中间只要搞错一页顺序,整个整理工作就乱套。
这还只是文字提取层面。如果目的是给 LLM 应用准备数据,纯文本 OCR 输出几乎无法直接使用:没有结构、没有分块边界、上下文碎片化。所以我需要的不是"又一个 OCR 工具",而是一个能输出结构化 Markdown 的文档解析器,anydoc 恰好是这个定位。
2. Firecrawl 和 anydoc:从"抓网页"到"啃文档"的进化
2.1 Firecrawl 本来是干嘛的
Firecrawl 最早解决的是另一个问题:网页抓取。
传统爬虫抓下来的是 HTML,带着大量导航、广告、脚本、样式标签,直接喂给大模型既浪费 token 又干扰理解。Firecrawl 的思路是先把网页渲染成干净内容,再转成结构化的 Markdown 或 JSON,让 LLM 拿到的是一份"清爽的正文"。因为对 RAG 管线友好,它在 GitHub 上一直保持很高的热度,也成了不少本地知识库项目的数据管道组件。
我自己用它的场景是抓取技术文档和博客文章。以前用 Requests + BeautifulSoup 写解析规则,换个网站结构就得改代码,维护成本很高。Firecrawl 做了很多页面智能处理,比如移除导航、识别正文主区域、把表格转成 Markdown,能省下不少写规则的精力。不过这些都是网页层面的能力,直到 anydoc 出现,它才从"网页抓取工具"进化成"全文档解析工具",这也是它在技术讨论区重新被刷屏的原因。
2.2 anydoc 在 Firecrawl 里承担什么角色
anydoc 字面意思就是"任何文档"。它是 Firecrawl 在文档解析方向的延伸:输入可以是 PDF、DOCX、PPTX、XLSX,甚至是扫描件图片,输出是统一结构的 Markdown。
定位上,它和网页抓取是互补的。Firecrawl 原来的抓取流程针对 HTML 文档优化,但现实世界的知识库里大量资料是办公文档——产品手册、研究报告、合同协议、会议 PPT。这些文件不能像网页那样通过 DOM 解析,必须先经过版面分析和 OCR 才能进入 Markdown 化流程。anydoc 干的就是这层脏活累活。
使用方式上,anydoc 没有搞一套独立体系,而是延续了 Firecrawl 的 SDK 和 API 风格。本地部署一套服务后,抓网页和解析文档用的是同一套调用入口,只是参数不同。这对已经有 Firecrawl 使用习惯的人非常友好,学习成本几乎为零。
2.3 为什么偏偏是 Markdown 而非其他格式
你可能会有疑问:转换输出为什么选 Markdown,而不是直接输出 HTML 或纯文本?我在实际使用中体会很深:
- Markdown 兼顾人读和机读。它保留标题层级、列表、表格、代码块、引用这些语义信息,人可以直接打开编辑,机器也能无损解析,作为 RAG 管线的中间格式特别合适。
- Token 利用率高。同样的内容,Markdown 比 HTML 少很多标签噪音,喂给 LLM 时占用的 token 更少,回答质量反而更高。
- 生态成熟。Typora、VS Code、Obsidian 都原生支持 Markdown,转换结果可以直接进笔记系统、知识库或后续的自动化流程,不需要额外开发适配。
- 一个格式打通多处。文档分析、自动摘要、问答检索、报告生成,几乎所有的 LLM 应用都接受 Markdown 输入。输出一种格式,就能服务下游所有任务。
3. anydoc 内部是怎么把文档"拆"成 Markdown 的
3.1 文档类型探测与预处理
拿到一份文件后,anydoc 首先做的是判断"这是什么文档"。这一步不是简单的看扩展名,而是真的读文件头、解析内部结构。
如果 PDF 本身带文字层(数字生成的 PDF),处理走的是解析路线:直接从内容流里提取文本和排版信息,准确率高且速度快。如果文件是扫描版 PDF 或图片,没有文字层,就走 OCR 路线:先把每页渲染成图像,再交给 OCR 引擎识别。
预处理环节也很关键。很多扫描件存在页面倾斜、背景色偏、边缘阴影的问题,直接丢给 OCR 会严重影响准确率。anydoc 会先做旋转矫正、去噪、增强对比度,把图像调整到适合识别的状态。我在自托管环境跑过一次歪斜严重的扫描合同,关闭预处理和开启预处理的输出差异非常明显,前者出现了大量错字。
3.2 版面分析与 OCR 引擎的配合
这是把"图片变成文字"和"文字摆回正确位置"两步分开的关键。
OCR 引擎解决的是像素到字符的转换,它认识字,但不理解版面。一份 PDF 扫描件里,标题字号大、正文段落有缩进、表格有行列线、图片有边框,这些都是版面信息。anydoc 通过版面分析把页面划分成不同区域:标题区、正文区、表格区、图片区、页眉页脚区,然后对每个区域分别处理。
这样做的优势很明显。标题区域识别后可以映射为 Markdown 的#层级,正文区域映射为普通段落,表格区域映射为 Markdown 表格,图片区域映射为![]()。OCR 引擎负责"认出某个单元格里的数字是 1200 还是 120O",版面分析负责"这个数字属于表格第三行第二列"。两者配合,才能输出结构正确的 Markdown。
自托管模式下 OCR 引擎的选择比较灵活,常见的开源方案 Tesseract、PaddleOCR 都可以接。PaddleOCR 对中文识别效果好一些,Tesseract 胜在部署简单稳定。我自己处理中文扫描件时,会优先考虑中文优化过的引擎,识别率有明显差距。
3.3 表格、标题层级、代码块是怎么还原的
文档里最难还原的三个元素:表格、标题、代码块。
表格处理上,anydoc 会检测页面里的表格结构:先找表格线或行列对齐特征,再推断单元格边界,最后按行列关系输出为 Markdown 表格语法。合并单元格会被尽量展开成占位单元格,跨页表格会尝试拼接。实测下来,带明显表格线的表格还原效果最好,无边框表格仍然会有一定概率把列内容混在一起。
标题层级的还原逻辑有点像"给文字分类"。通过字号大小、字体粗细、段落前后间距、是否处于页面顶端等特征,判定一个文本块是第一级标题、第二级标题还是正文。然后对应输出#、##或普通段落。PPT 文件的标题识别会更简单一些,因为每页都有明确的标题占位符。
代码块的还原在技术文档里特别实用。等宽字体区域、特定背景色、行首缩进模式都会被识别为代码块,输出为 Markdown 代码块语法并保留语言标注。我处理过一些技术手册 PDF,代码部分被完整还原成可复制执行的代码块,这在纯 OCR 流程里几乎不可能实现。
4. 实操记录:拿一份真实报告跑通全流程
4.1 自托管部署时要注意的几个点
Firecrawl 支持云端 API 和自托管两种方式。涉及企业内部文档、合同这类敏感内容时,我建议优先自托管,数据不出内网,心里踏实。
自托管基于 Docker 部署,核心组件包括 API 服务、工作队列、Redis、数据库和 Worker。我的环境配置是 4 核 CPU、8GB 内存起步,如果经常处理上百页的大文件,16GB 会更稳。磁盘空间要留足,文档解析过程会产生中间缓存和处理日志。
部署命令不复杂:
git clone https://github.com/firecrawl/firecrawl.git cd firecrawl cp .env.example .env docker compose up -d启动完成后,默认 API 地址一般在http://localhost:3002,可以通过健康检查接口确认服务状态。整个启动过程大概几分钟,第一次启动需要拉取基础镜像,后面再启动就很快了。
一个小提醒:.env里的一些密钥配置,生产环境务必替换成自己的随机值,不要沿用示例配置。这块以前吃过亏,虽然不影响功能,但安全性不能偷懒。
4.2 提交文档与拿结果:核心调用逻辑
我实际用的时候,直接用 Python SDK 比较顺手,调用逻辑非常直接:
from firecrawl import FirecrawlApp app = FirecrawlApp( api_key="your-api-key", api_url="http://localhost:3002" ) result = app.scrape_url( "file:///data/report.pdf", params={"formats": ["markdown"]} ) markdown_content = result["markdown"] with open("report.md", "w", encoding="utf-8") as f: f.write(markdown_content)核心就一个scrape_url调用,传文件路径和输出格式,返回结果里取markdown字段。如果你用的是云端托管版本,也可以直接传公网文件链接或直接上传文件流,原理一样。
这里注意一点:不同版本的 SDK 字段名可能略有差异,实际使用前先打印一下返回结果的结构,确认markdown字段是否存在。我升级过一次 SDK 版本,返回值结构就有细微变化,盲取字段容易踩坑。
4.3 同一份文档,传统 OCR 和 anydoc 的对比结果
为了验证效果,我拿一份 15 页的混合型报告做过对比测试。这份报告包含数字生成的文字页、2 页扫描件、3 张复杂表格和一些代码片段。
| 维度 | 截图+传统 OCR | Firecrawl anydoc |
|---|---|---|
| 处理方式 | 逐页截图、逐张识别 | 整份 PDF 一次提交 |
| 耗时 | 约 40 分钟人工操作 | 约 2 分钟自动完成 |
| 表格还原 | 基本不可用,需手工重建 | 表格语法完整还原 |
| 标题层级 | 丢失,纯文字流 | 按层级输出为 H1/H2/H3 |
| 代码块 | 无法识别 | 输出代码块并保留缩进 |
| 人工整理时间 | 3 小时以上 | 约 15 分钟校对 |
| 扫描件准确率 | 视截图质量波动大 | 预处理后相对稳定 |
当时最直观的感受是:以前需要大半个下午做的事,现在泡杯咖啡的功夫就完成了。当然,这不是说 OCR 类工具就没用了,轻量场景下截图 OCR 依然有它的便利性,但一旦涉及整份文档、结构化输出、批量处理,anydoc 的路线明显是更优解。
5. 踩坑记录:格式、扫描件和混合内容
5.1 扫描件质量直接决定输出上限
先泼一盆冷水:anydoc 不是魔法,扫描件的质量决定了输出质量的上限。
我试过一份 150dpi 扫描且页面倾斜的 PDF,转换结果里错字明显增多,表格边界也识别得磕磕绊绊。提高扫描分辨率到 300dpi 以上,倾斜校正做好,输出质量立刻上了一个台阶。如果原始文件本身是手机随手拍的照片,透视变形很严重,建议在进入转换前先用图像处理工具做一次校正。
还有一类问题是印章和背景干扰。合同上常见的红色印章、文件背景的水印,有时候会被 OCR 误识别成正文内容。这种情况下,输出里会出现一些奇怪的字符片段,需要在后处理时过滤。anydoc 虽然做了一些预处理,但复杂背景下的误识别仍是当前技术上限,提前有心理预期会好很多,至少比传统截图 OCR 的出错方式要集中、可控。
5.2 最难的永远是表格和页眉页脚
混合内容文档里,我踩得最多的坑集中在表格和页眉页脚两处。
表格方面,带明显网格线的表格还原效果不错,但无边框表格、用 Tab 对齐的伪表格、跨页断行的表格,输出结果仍然可能出现列错位。特别是那种用空格和制表符硬排版的"财务表格",单元格边界全靠目测,任何解析工具都会头疼。我的处理策略是:这类关键表格不指望完全自动,解析后用脚本快速校验列数,再手动微调几十个单元格,比从零开始快得多。
页眉页脚则是另一个问题。文档页码、公司名称、日期这些页眉页脚信息,有时会被混入正文段落,导致内容衔接出问题。这个问题在传统 OCR 里更严重,因为纯文本流里根本没有页的概念。anydoc 对页眉页脚做了区域识别,大部分情况下能过滤掉,但遇到页眉样式和正文接近的文档,还是会有漏网之鱼。我在清理阶段会专门检查开头和结尾几行,把混入的页码和重复标题删掉。
5.3 输出前的清洗:Markdown 不是终点
转换完成只是第一步,离"能用"还差一次清洗。
常见的清洗任务包括:删除多余空行、合并被错误断行的段落、清理 OCR 产生的特殊字符、校验表格列数是否一致、处理未正确转义的 Markdown 符号。我自己写了一个简单的后处理脚本,用正则批量处理那些高频问题,比如把连续三个以上的空行压缩成一个,把行尾多余空格去掉,把误识别的中文标点转回全角形式。
import re with open("report.md", "r", encoding="utf-8") as f: content = f.read() # 压缩连续空行 content = re.sub(r"\n{3,}", "\n\n", content) # 去掉行尾多余空格 content = re.sub(r"[ \t]+\n", "\n", content) # 修复常见的全角/半角标点错乱 content = content.replace("。", "。").replace(",,", ",") with open("report_clean.md", "w", encoding="utf-8") as f: f.write(content)清洗的目的不是追求完美还原,而是让 Markdown 达到"可以安全进入下游流程"的程度。对 RAG 场景来说,结构正确、内容干净比逐字逐句精确更重要。
6. 这类工具对 RAG 和 LLM 工作流的实际意义
6.1 喂给大模型之前,文档结构就是上下文
做 RAG 的朋友一定知道,文档切块策略直接影响检索效果。以前用纯文本 OCR 输出的内容切块,经常出现一个块里既包含标题又包含正文、甚至横跨两个表格片段的情况,检索时上下文混乱,回答质量自然上不去。
Markdown 输出改变了这件事。标题层级可以作为切块的天然边界,表格整体可以作为一个结构化块保存,代码块可以被单独索引。检索时,用户问到某一章内容,系统能精确定位到对应标题下的块,上下文完整度远高于无差别切块。我接的一个知识库问答项目,切块策略从固定字符数改成"按 Markdown 标题层级切块"之后,回答准确率提升非常明显。
另外,LLM 对 Markdown 的语义理解比对纯文本好得多。喂进去的如果是带结构的 Markdown,模型在生成答案时能正确参考表格对应关系和层级逻辑,而不是靠概率推断句子关系。这一点在所有文档类应用里都会体现出来。
6.2 适合谁用、不适合谁用
聊几个我在实践中总结的适用边界。
适合用的场景:搭建企业内部知识库、合同条款分析、论文文献整理、产品手册转问答机器人、财报数据提取。共同点是文档量大、需要保留结构、输出要进入自动化管道。
不太适合的场景:只想从手机拍的照片里提取一小段文字、快速复制聊天记录里的内容、偶尔一次性的轻量识别。这些场景截图 OCR 依然是更轻便的选择,没必要为了一句话启动一整套文档解析服务。
另外,如果文档里全是复杂数学公式、手写批注、精细排版的艺术类 PDF,目前的解析效果还不能让人满意。公式识别、手写识别本身是独立的难题,不是文档转 Markdown 能顺手解决的。遇到这类内容,我通常会把关键公式截出来单独处理。
6.3 我现在的工作流变成了什么样
经过这段时间的实际使用,我处理办公文档的第一动作已经从"截图"变成了"先丢给 anydoc"。日常工作流大致是:收到文档 → 提交转换 → 快速清洗 → 进入知识库或直接问 LLM。
整个过程自动化之后,最明显的变化不是我省了多少时间,而是心态上的改变。以前看到扫描版合同会很烦躁,因为知道接下来要花大量时间做机械劳动。现在完全不怕,扔进 pipeline 里,几分钟后拿到的就是能直接用的 Markdown。这种"脏活有人承包"的感觉,可能是这类工具给我最大的价值。
如果你也在搭知识库、做文档分析,或者只是手上囤了一堆扫描版 PDF 不知道怎么处理,可以试试把 Firecrawl 的 anydoc 加进你的工具链。先拿一份有代表性的真实文档跑一遍,感受一下表格还原和标题层级保留的效果,再决定要不要用它替换现在的 OCR 流程。