你的爬虫扛不住 JS 渲染,Firecrawl 用 5 个引擎接力跑完了一个 URL
- 多引擎接力系统根据 URL 特征预判路由,先试最便宜引擎失败再升级,避免通用引擎次优解
- 并发控制分 API 级别和 crawl 任务内部两层,用 Redis 跟踪状态并提供 WebSocket 实时进度
- Rust 原生模块用于文档解析二进制格式,提升性能与内存效率,TypeScript 负责业务编排
- extract 功能结合 LLM 和抓取内容,按 JSON Schema 提取结构化数据,品牌信息管道分析 CSS
- AGPLv3 协议限制商业二次开发,核心 fire-engine 闭源,自部署需依赖本地 Playwright 或托管 API
大家好,我是若风。
你写爬虫的时候有没有遇到过这种情况,一个 URL,用 requests 请求返回空壳,换成 Playwright 又超时,最后手动加个 waitForSelector 勉强跑通,过两天目标网站改了 DOM 结构,又挂了。
抓单个页面就这样,抓一整站更痛苦。代理轮换、并发控制、JS 阻塞、限流、去重,每一项都是独立的工程问题。
Firecrawl 想做的事情很简单,你给它一个 URL,它负责把网页变成干净的 Markdown 或结构化 JSON 返回给你。听起来不复杂,但它的解法值得拆,因为它在「一个 URL 到底怎么抓」这件事上做了大量非平凡的设计。
一句话定位
Firecrawl 是一个把网页抓取全流程(搜索、抓取、爬取、交互)封装成 API 的平台,核心交付物是 LLM 可直接消费的干净文本。15 万 Star,AGPLv3 开源,TypeScript 写的主干,底层嵌了 Rust 原生模块。
为什么不是一个简单的 Playwright 封装
很多人第一次看 Firecrawl 的代码,以为它就是个 Playwright wrapper。翻到 apps/api/src/scraper/scrapeURL/engines/ 目录就知道不是。
它的抓取层是一个多引擎接力系统。打开 engines/index.ts,你能看到至少 5 类引擎,按场景分工。
fetch/index.ts,最轻量,用原生 HTTP 请求,处理那些不需要 JS 渲染的静态页面playwright/index.ts,当 fetch 拿不到完整内容时升级到浏览器渲染fire-engine/,自研的远程浏览器引擎,处理需要复杂 JS 执行的页面wikipedia/index.ts和x-twitter/index.ts,针对特定站点的专用引擎,走官方 API 或专用解析逻辑pdf/,PDF 文档解析,内部还细分了 fire-pdf 异步管道和 runpodMU(MinerU on RunPod)两条路径
关键设计在 WebScraper/utils/engine-forcing.ts。这个文件做的事情是根据 URL 特征强制指定引擎。一个 Wikipedia 链接进来,直接跳过 fetch 和 playwright,路由到 wikipedia 专用引擎走 API。一个 X/Twitter 链接同理。这不是降级,是预判式路由。
为什么这么做?因为通用引擎在特定站点上永远是次优解。Playwright 抓 Wikipedia 能抓到内容,但 Wikipedia 的 API 返回的结构化数据比 DOM 解析干净一百倍。与其让通用引擎硬刚,不如在入口就分流。
接力机制的核心思路是,先用最便宜的引擎试,失败了再升级。fetch 失败上 playwright,playwright 搞不定上 fire-engine。每一次升级都是有代价的,浏览器渲染比 HTTP 请求慢一个数量级,而 fire-engine 这种远程浏览器农场更慢。这个降级链路直接决定了 Firecrawl 的成本和延迟。
并发控制的两个层级
抓一整站(crawl)和抓单个 URL(scrape)是完全不同的问题。scrape 是一次请求,crawl 是几百上千个页面的编排。
Firecrawl 在 apps/api/src/lib/ 下有两层并发控制。
第一层是 API 级别的,api-key-concurrency.ts 和 concurrency-redis.ts,用 Redis 跟踪每个 API key 的并发请求数。你的套餐决定上限,超了就排队。concurrency-queue-reconciler.ts 负责队列的对账,防止 Redis 状态和实际执行产生偏差。
第二层是 crawl 任务内部的,crawl-redis.ts 管理单个 crawl 任务的 URL 队列。一个 crawl 请求进来,先发现种子 URL,逐步扩展,每个 URL 的状态(待抓、抓取中、已完成、失败)都存在 Redis 里。crawl-status-ws.ts 甚至提供了 WebSocket 接口,让你实时看 crawl 进度。
说真的这个设计很实用。爬一整站可能要几分钟到几十分钟,同步等待不现实,用 Redis 做状态机 + WebSocket 推送进度,是最务实的方案。
Rust 原生模块藏在哪
Firecrawl 主体是 TypeScript,但 apps/api/native/ 下藏了一个 Rust 原生模块。看 native/src/ 的结构。
crawler.rs,Rust 实现的爬虫核心逻辑document/providers/,文档解析的 provider 工厂模式,支持 doc、docx、odt、rtf、xlsx 等格式document/renderers/html.rs,HTML 渲染器
TypeScript 调 Rust 的模式在 Node.js 生态里不算新鲜,但 Firecrawl 把它用在文档解析上是有道理的。解析 DOCX 和 XLSX 这种二进制格式,涉及大量 XML 解包和数据提取,Rust 的内存效率和速度比纯 JS 实现快得多。document/providers/factory.rs 用工厂模式做 provider 选择,和上面抓取引擎的路由思路一脉相承。
这是个很典型的混合语言架构,性能瓶颈用 Rust,业务编排用 TypeScript,各取所长。
结构化数据提取
scrape 返回 Markdown 是基础能力,Firecrawl 真正让 AI 开发者觉得好用的是 extract,把网页内容提取成结构化 JSON。
controllers/v1/extract.ts 和 controllers/v2/ 的 extract 端点,接受你定义的 JSON Schema,返回符合 schema 的数据。背后的实现是 LLM + 抓取内容的组合。抓取引擎负责拿到干净的页面文本,LLM 负责把文本理解并填充到你定义的结构里。
lib/branding/ 目录下的模块更有意思,这是一套品牌信息提取的专用管道。branding-script/ 下有完整的浏览器端注入脚本,专门提取 logo、颜色、字体、按钮样式等设计元素。logo-selector.ts 和 css-data.ts 说明它不只是抓图片,而是在分析 CSS 来理解设计系统。
这个功能的方向很明确,给做竞品分析、设计参考、品牌监控的人用的。
AGPLv3 的代价
Firecrawl 的协议是 AGPL-3.0,这个选择不是随便定的。
AGPL 的核心条款是,如果你通过网络提供服务,且你的服务用到了修改后的 Firecrawl 代码,你必须开源你的修改。这比 GPL 更严格,GPL 只在分发时触发,AGPL 在网络服务时也触发。
对个人开发者来说,自用没问题。但如果你想把 Firecrawl 二次开发后做成自己的 SaaS 产品对外卖,你得想清楚合规成本。要么开源你的全部修改,要么走商业授权。这是 Firecrawl 作为开源项目的商业护城河。
说句实话,这个策略在 2024 年很流行,Mendable(Firecrawl 母公司)不是第一家这么干的。但对使用者来说,这比 MIT 协议的隐性成本高很多。
自部署的现实
Firecrawl 提供 SELF_HOST.md 和 docker-compose.yaml,理论上可以自己部署。但实际跑起来你会发现,核心的 fire-engine(远程浏览器引擎)不在开源代码里,它是 Mendable 托管的浏览器农场。
也就是说,你自部署的 Firecrawl,在遇到需要浏览器渲染的 JS 重度页面时,要么回退到本地 Playwright(性能差,不稳定),要么还得调 Firecrawl 的托管 API(那就失去了自部署的意义)。
engines/fire-engine/ 目录下有调用 fire-engine 的客户端代码,但 fire-engine 本身是闭源的。这是 Firecrawl 开源策略的核心边界,开源了编排层,核心渲染引擎留着做商业服务。
你看 issues 区也能感觉到这个张力,#2175 那个 PR 标题是「Add Reducto as a fallback (for testing purposes)」,社区在尝试给 PDF 解析加第三方 fallback,就是因为单一依赖 fire-engine 的方案不够灵活。
Release 节奏和活跃度
从 release 时间看,Firecrawl 的迭代很稳。
- v2.11.0 (2026-06-19)
- v2.10 (2026-05-15)
- v2.9.0 (2026-04-10)
- v2.8.0 (2026-02-03)
- v2.7.0 (2025-12-05)
大概每 1-2 个月一个版本。30 个 contributors 的 API 调用返回了 30 条(gitHub API 的 per_page 上限),实际贡献者更多。考虑到 8559 个 fork,这个社区参与度对一个有商业公司的项目来说算健康。
一个值得注意的信号,最近的 issue 和 PR 里大量出现 nuq 关键字(#3981、#3980)。这是 Firecrawl 内部的一个新架构组件,从命名看可能是新的队列系统。issue #3386 提到「scrapeURL refactor with gateway support」,说明抓取层正在经历一次比较大的重构。
这个项目教给你什么
拆完 Firecrawl,我觉得最值得带走的不它的 API 设计,而是一个工程判断,对异构输入做多引擎路由。
抓取场景天然是异构的。静态 HTML、JS 渲染页面、PDF、特定站点 API,每种都需要不同的处理策略。很多爬虫项目的做法是,写一个 Playwright 封装,硬刚所有场景。结果是,简单的静态页面被拖慢,复杂的 JS 页面还是抓不稳。
Firecrawl 的思路是,在入口做预判,用最便宜的引擎试,失败了再升级。这个模式可以迁移到很多场景。
- 文件处理,按格式选 parser
- API 请求,按目标服务选 HTTP 客户端
- 数据清洗,按数据源选 pipeline
说到底就是别用一把锤子敲所有钉子。听起来是常识,但当你真正在一个项目里实现多引擎路由时,路由逻辑、降级策略、错误处理、性能监控,每一项都需要扎实的工程。Firecrawl 用 15 万 Star 证明,把这件事做扎实,本身就是价值。
如果你在选爬虫工具,Firecrawl 适合需要稳定抓取大量异构页面、且不想自己维护浏览器农场的团队。如果只是抓几个静态页面,requests + BeautifulSoup 就够了,别上这么重的工具。
评论互动