Firecrawl 爬虫工具实战:从网页抓取到 Markdown 结构化输出全攻略

发布时间:2026/8/29 8:16:16
Firecrawl 爬虫工具实战:从网页抓取到 Markdown 结构化输出全攻略 Firecrawl 是一个把网页抓取、JavaScript 渲染、内容清洗和结构化输出打包到一起的开源爬虫工具核心能力是输入一个 URL输出一份干净、可以直接喂给大模型的 Markdown 内容。以前做 RAG、AI 搜索或者文档知识库最烦人的不是数据源少而是网页抓回来以后要花大量时间洗数据导航栏、广告位、弹窗、动态加载的内容、各种样式标签全混在一起。Firecrawl 把这些处理内置了所以这两年做 AI 应用的团队用它的频率非常高。这篇文章我会按一条完整的落地路线来讲先搞清楚它究竟解决什么问题再理清云端 API、免费额度和本地部署三条路怎么选然后从单页抓取一步步跑到整站爬取和结构化提取最后把常见报错和排查链路也列出来。1. Firecrawl 解决什么问题网页内容到 LLM 可用格式的最后一公里1.1 普通爬虫抓回来的 HTML为什么没法直接用很多人会想网页抓取不是很简单吗用 Python 的 Requests 拿到 HTML再用 BeautifulSoup 提取正文就行了。如果你只抓静态官网确实可以。但只要站点稍微复杂一点问题就来了。第一是动态渲染。现在大量站点是 Vue、React 或 Nuxt 做的正文内容要通过 JavaScript 请求接口后再渲染到页面上。直接用普通 HTTP 请求拿到的 HTML 里往往只有一个空的页面骨架和一段脚本正文一个字都没有。这种情况你只能上 Playwright 或 Puppeteer 这类浏览器自动化工具自己控制浏览器去渲染页面再等网络请求完成。第二是页面噪音。就算你把页面渲染出来了里面还有网站导航、侧边栏推荐、页脚链接、Cookie 弹窗、广告位、相关文章模块。这些内容对浏览器用户是正常的但对大模型来说全是干扰。如果不处理干净切分出来的文本块质量非常差检索准确率也会明显下降。第三是格式问题。大模型应用通常需要干净的 Markdown 或纯文本而不是一堆 div、span、class 嵌套的 HTML。你当然可以写正则或者 XPath 去抽取但每个站的页面结构都不一样写一套规则只能对付一个站换一个站又要重新调。我自己最早做采集脚本时就是这样Requests 抓静态站还行遇到 Vue 写的文档站就抓不到正文后来换 Playwright能渲染了但又要为每个站单独写内容提取规则。这个痛点不是“抓不到”而是“抓到了但没法直接用”。1.2 Firecrawl 的核心能力拆解Firecrawl 把上面这些环节打包成一组 API核心功能可以分成五块功能对应接口解决什么问题单页抓取 scrapePOST /v1/scrape抓取单个 URL转成 Markdown整站爬取 crawlPOST /v1/crawl从入口 URL 开始按链接关系爬完整站搜索抓取 searchPOST /v1/search先搜索互联网再把结果页转成 Markdown链接映射 mapPOST /v1/map列出指定站点下的所有 URL 清单结构化提取 extractPOST /v1/extract按 Prompt 从页面中提取 JSON 结构化数据scrape 是最常用的起点解决单个页面的问题。crawl 解决的是“我有一整个文档站或资讯站想一次性全抓下来”的问题。map 可以在爬全站之前先摸清楚站点规模。search 适合做 AI 搜索类应用。extract 则让输出从“一篇 Markdown”变成“一条结构化记录”。需要强调的是这几个能力不是孤立的实际使用时经常组合。比如先 map 看站点有哪些页面再 crawl 批量抓取最后 extract 提取关键字段。1.3 适合谁用不适合谁用先说适合的人。做 RAG 知识库采集的、做 AI 智能体网页检索工具的、整理文档站和博客站做二次分析的、需要把竞品页面定期存档做内容监控的这几个场景都很合适。Firecrawl 的最大价值是帮你把“清洗网页”这层重复劳动省掉让你把精力放在业务逻辑上。不太适合的场景也有。如果你需要每天抓几十万个页面而且对成本极其敏感那需要认真算一笔账云端的按量计费不一定划算。如果你需要抓的是强反爬、强验证码的站点Firecrawl 能帮你一部分但不能解决所有商业级反爬难题。如果你希望完全掌控每一步的并发、调度、失败重试、存储逻辑那么把它当底层组件用而不是当成一个黑盒来用会更合适。2. 动手前先理清运行条件云端 API、免费额度、本地部署三条路2.1 云端 API 的基本门槛Firecrawl 的使用方式很直接去官网注册账号拿到一个以 fc- 开头的 API Key然后调用远程接口。云端方案的好处是 JavaScript 渲染、队列调度、代理池、内容清洗这些事都由官方托管处理你不需要自己维护浏览器服务。使用云端 API 只需要满足几个条件能正常访问 Firecrawl 的 API 服务也就是 api.firecrawl.dev 这个地址。有一个有效账号和 API Key。调用时把 Key 放在 Authorization 请求头里。我个人建议第一步先用云端 API 做验证不要一上来就自己部署。原因很简单云端能帮你确认“Firecrawl 本身能不能满足需求”。如果云端都抓不到的内容自托管大概率也抓不到因为核心渲染和清洗逻辑是同一套。2.2 免费额度到底够不够用和 Firecrawl 相关的搜索热词里“免费额度”出现频率很高这确实是大多数人最关心的点。新账号注册后一般会送一定量的免费额度这个额度足够用来跑通整个流程。按我注册时的情况免费额度以 credit 为单位计量数量大约在几百个这个量级。一次 scrape 通常消耗一个 creditcrawl 是按实际抓取的页面数量扣费search 按返回的结果数扣费。不同活动的赠送政策、不同时段的计费规则可能会有差别所以具体数值要以官网注册页面和定价页面显示为准。免费额度够干什么够你抓十几篇到几十篇文章够你跑一次小规模的整站爬取够你把所有 API 都试一遍确认输出格式是否满足需求。但如果你要持续每天抓几百个页面免费额度很快就会用完。这里有一个容易忽略的点额度消耗不是按“调用次数”简单算的。一个 crawl 任务如果设置了很大的 limit一次任务就可能扣掉几十个 credit。所以控制任务范围特别重要这一点后面详细说。2.3 本地部署需要哪些前置条件如果你不想被额度绑住可以自托管。Firecrawl 是开源项目仓库里提供了 Docker 部署方式。自托管的大致组件包括Firecrawl API 服务负责接收请求和调度任务。Worker 服务负责实际执行抓取和内容清洗。Playwright 浏览器服务负责渲染 JavaScript 页面。Redis负责任务队列和缓存。这些服务可以一键用 docker compose 启动但第一次部署时需要注意网络、端口和依赖版本。官方仓库的 README 会给出环境变量清单包括 API Key、Redis 连接地址、Playwright 服务地址等。自托管对机器配置的要求不算高主要消耗内存不太吃 GPU。一台 4 核 8G 内存的服务器可以跑小型采集任务但如果同时开很多并发任务Redis 队列和 Playwright 浏览器实例会吃掉不少内存需要根据任务量调整。2.4 三条路到底怎么选方案优点代价云端免费额度零成本、快速验证、不需要运维额度有限只适合测试和小批量云端付费稳定、有代理池、省心按量计费量大会持续花钱本地自托管没有额度限制、数据在自己手里要自己维护服务、处理反爬和 IP 问题我的建议是学习期和原型验证用云端免费额度小规模生产可以继续用云端付费如果是高频大规模采集且团队有运维能力再考虑自托管。不要一开始就部署先把需求验证清楚省得白忙。3. 最小流程先跑通一次单页面抓取3.1 注册账号并拿到 API Key先做最小验证。注册账号之后进入 Dashboard 找到 API Key复制出来。注意这个 Key 相当于账号凭证不要写到公开仓库里。拿到 Key 以后不要急着写业务代码先想清楚两件事第一Firecrawl 返回的 Markdown 质量是不是你想要的第二它处理动态页面和复杂站点的能力够不够。这两件事用一条 API 请求就能验证。3.2 先不用 SDK直接用 curl 验证为什么先用 curl 而不是直接写 Python因为 curl 能隔离掉 SDK 版本、依赖安装、代理配置这些变量。如果 curl 返回正常说明网络和 Key 没问题如果 curl 都报错那就先排查网络和 Key不要怪 SDK。curl -X POST https://api.firecrawl.dev/v1/scrape \ -H Content-Type: application/json \ -H Authorization: Bearer fc-你的API_KEY \ -d { url: https://example.com, formats: [markdown] }正常返回的结构大致是{ success: true, data: { markdown: # Example Domain\nThis domain is for use..., metadata: { title: Example Domain, description: ..., language: en } } }判断成功的标准有三个success 为 true、markdown 字段不为空、metadata 里有页面标题。只要这三个条件满足说明 Firecrawl 的链路是通的。3.3 用 Python SDK 跑同样的事curl 验证通过之后再用 SDK 做正式开发。安装命令很简单pip install firecrawl-py然后写一个最简脚本from firecrawl import FirecrawlApp app FirecrawlApp(api_keyfc-你的API_KEY) result app.scrape_url( https://example.com, params{ formats: [markdown], onlyMainContent: True, } ) print(result.get(markdown, )[:500])这里我把 onlyMainContent 设成了 True。这个参数的作用是过滤掉导航、页脚、侧边栏等非正文内容只保留页面主体区域。如果你要抓的内容是文章或文档建议直接开启。需要注意不同 SDK 版本的参数命名可能有细微差别。比如有些版本里爬取参数是 params有些版本里是 options。遇到报错先看对应版本的 SDK 文档别照着旧版本硬套。3.4 常用参数说明scrape 接口参数比较多但核心就这几个参数作用建议formats返回格式如 markdown、html、rawHtml、links、screenshotRAG 场景用 markdownonlyMainContent是否只保留页面主体内容抓文档和文章时建议 TruewaitFor等待指定毫秒数让 JS 渲染完成动态站点设 2000 到 5000timeout单次请求超时时间网络不稳定时适当调大includeTags只提取指定 HTML 标签内容内容聚焦时可用excludeTags排除指定 HTML 标签内容可用来去掉广告区域headers自定义请求头部分站点需要携带特定 UA我一般会先只用 markdown 加 onlyMainContent 跑一次看输出是否干净。如果正文缺失再考虑加 waitFor 或者调整 tags 参数。不要一上来就把参数堆满先最小条件跑通再逐步加。3.5 输出质量怎么判断抓回来的 Markdown 不一定直接用要检查三件事正文是否完整。把输出和浏览器里看到的页面对比一下看有没有缺段落、缺标题。噪音是否够少。如果导航和页脚都进 Markdown 了说明 onlyMainContent 没有生效或者这个站的结构比较特殊。格式是否正常。标题层级、列表、代码块、表格是否保留正确。Firecrawl 对多数站点处理得不错但遇到极端布局的页面Markdown 结构还是可能乱。如果发现输出不理想不要立刻怀疑工具不行。先用 rawHtml 格式把原始 HTML 拉下来看看页面本身长什么样再判断是渲染问题还是提取问题。4. 从单页到整站crawl 任务的参数与队列逻辑4.1 crawl 和 scrape 的区别是什么scrape 是单页操作请求发出后同步等待结果返回。crawl 是异步任务你先提交一个入口 URL 和爬取参数服务端创建任务返回一个任务 ID然后你通过这个 ID 轮询任务状态直到任务完成。为什么 crawl 要做成异步因为整站爬取可能涉及几十上百个页面每个页面都要渲染、清洗、转换单次 HTTP 请求根本等不了那么久。异步任务加队列是这类系统的标准设计。所以用 crawl 时的核心思路是提交任务、拿任务 ID、轮询状态、任务完成后拉取结果。不要指望一次请求直接返回所有页面。4.2 crawl 的关键参数用 crawl 时我最关心的参数是以下四个crawl_result app.crawl_url( https://docs.example.com, params{ maxDepth: 2, limit: 50, formats: [markdown], onlyMainContent: True, } ) job_id crawl_result.get(jobId) or crawl_result.get(id) print(任务 ID:, job_id)maxDepth爬取深度。0 表示只抓入口页1 表示抓入口页和所有直接链接的页面2 表示再往下一层。limit最多抓取页面上限。这是保护额度的最重要参数。ignoreSitemap是否忽略站点地图。有些站点 sitemap 特别大不设 limit 的话会一直抓下去。deduplicateSimilarURLs是否去重相似 URL。像带排序参数、翻页参数的链接去重后能省不少额度。我建议先设一个小 limit比如 10跑通流程后再放大。不要一上来就设 500等任务卡住或者额度被扣完再后悔。提交任务后需要轮询状态import time status app.check_crawl_status(job_id) while status.get(status) in [active, pending]: time.sleep(5) status app.check_crawl_status(job_id) if status.get(status) completed: pages status.get(data, []) for page in pages: url page.get(metadata, {}).get(url) markdown page.get(markdown, ) print(url, len(markdown))这段是示意性写法具体返回字段以你用的 SDK 版本为准。核心逻辑就是拿到任务 ID循环检查状态变成 completed 之后处理结果数组。4.3 crawl 任务真正容易踩的坑第一个坑是并发提交太多。不要对同一个站点同时开十几个 crawl 任务这会导致额度快速消耗也会让站点负载变大更容易触发反爬。第二个坑是 limit 范围没控制好。有些站点结构很复杂链接互相交叉爬取数量可能远超你预期。提交之前先用 map 接口看一下站点规模再确定 limit 值。第三个坑是结果落盘。crawl 返回的是内存里的一个数组如果任务中断或者进程退出结果就丢了。正式使用时每轮询到 completed 状态就应该把页面数组写入文件或数据库。第四个坑是页面顺序。crawl 返回的页面顺序不保证和站点目录结构一致不要依赖顺序做业务逻辑。按页面 URL 建立索引更靠谱。第五个坑是登录墙。如果站点需要登录才能看到正文crawl 默认情况下是抓不到内容的。可以先试试直接访问是否能看到正文看不到的话就需要考虑带 Cookie 或 header或者放弃这个站点。5. 搜索、映射与结构化提取把抓取变成数据管线5.1 map 先摸清站点规模在跑 crawl 之前我建议先用 map 接口看站点结构。map 的作用是列出某个站点下所有能被发现的 URL。curl -X POST https://api.firecrawl.dev/v1/map \ -H Content-Type: application/json \ -H Authorization: Bearer fc-你的API_KEY \ -d {url: https://docs.example.com}返回结果会包含一个 links 数组。你可以数一下 links 数量看这个站点的页面规模是几十还是几千。如果 links 数量超过 1000而你的免费额度只有几百个 credit那 crawl 之前就要想清楚是只爬其中一部分还是改用筛选条件缩小范围。5.2 search 解决“先找再抓”的场景search 接口适合做 AI 搜索类应用。它先执行一次搜索然后把搜索结果中的网页转成 Markdown 返回。这样你得到的不只是一串链接而是已经清洗好的内容。curl -X POST https://api.firecrawl.dev/v1/search \ -H Content-Type: application/json \ -H Authorization: Bearer fc-你的API_KEY \ -d { query: firecrawl markdown scraping, limit: 5 }返回结果里每个条目会包含页面 URL、标题、描述和 Markdown 内容。这个功能做合规的公开信息检索是很好用的但要注意搜索结果经常包含同一站点的多个页面如果只需要最新内容可以按时间排序并做去重。5.3 extract 提取结构化字段extract 接口解决的是“从网页里抽出结构化数据”的问题。你给它一批 URL 和一段 Prompt它返回的是 JSON而不是 Markdown。curl -X POST https://api.firecrawl.dev/v1/extract \ -H Content-Type: application/json \ -H Authorization: Bearer fc-你的API_KEY \ -d { urls: [https://example.com/news/1, https://example.com/news/2], prompt: 提取每篇文章的标题、作者、发布时间和正文摘要输出 JSON 数组 }使用 extract 时Prompt 写得越具体字段越明确返回结果越稳定。比如你要提取文章发布时间就应该在 Prompt 里明确说“时间格式为 YYYY-MM-DD”。如果 Prompt 描述模糊模型可能自由发挥字段名和格式就不统一了。5.4 组合出一个 RAG 数据采集管线实际做 RAG 数据采集我常用的组合是先用 map 拿到站点 URL 清单。根据清单筛选出需要的分区构造一个待抓取 URL 列表。用 crawl 或逐个 scrape 抓取页面保存 Markdown。用 extract 对每篇页面提取标题、日期、作者等结构化字段。把 Markdown 和结构化字段一起存入向量数据库或对象存储。这个流程跑通后还可以加一层定时调度。Firecrawl 本身没有内置定时任务你需要用外部调度器比如 Linux 的 cron 或者 CI 服务定期触发一次采集任务然后对比上次结果只增量更新变化页面。增量更新能省掉大量重复抓取成本。6. 免费额度怎么用才划算6.1 先理解额度是怎么扣的Firecrawl 的免费额度按 credit 计量。一次 scrape 消耗一个 credit 左右crawl 按实际抓取页面数量扣search 按返回结果数量扣extract 也会消耗较多 credit。不同页面类型可能还有差别比如需要长时间渲染的页面消耗会更高。这里有一个容易被忽略的点crawl 任务即使中途失败已经抓取过的页面也会扣费。所以在提交大任务前一定要先用小 limit 测试确认目标站点能稳定抓取再放大规模。免费额度的具体数值和使用情况以官网 Dashboard 显示为准。不用过于依赖“网上说送多少”的说法注册后直接看后台最准确。6.2 省额度的几个实际操作我总结了几条能实际省 credit 的策略第一先 map 再 crawl。map 的消耗远低于 crawl先看清站点规模避免全站盲抓。第二给 crawl 设置合理的 limit。limit 是硬性上限能防止站点结构异常时无限爬下去。对于一般文档站limit 设 50 到 100 已经不少了。第三优先抓高价值页面。如果你的知识库只需要文章正文就不应该把标签页、作者页、归档页、登录页都抓进来。这些页面内容重复度高浪费额度。第四开启 onlyMainContent。虽然它不直接影响扣费数量但能减少返回内容的体积让后续处理更省事。第五做缓存和增量更新。已经抓过的 URL 不要重复抓。采集脚本里维护一个 URL 到内容哈希的映射发现页面没变就跳过。第六本地自托管跑普通站点云端额度留给需要代理池和复杂渲染的站点。这样能把免费额度的效益放大。6.3 额度超限会怎样额度用完后调用会报错通常是认证相关错误或 429 类限流错误。判断方式很简单控制台看返回码去 Dashboard 看剩余额度。遇到超限不要慌先把任务队列停掉避免反复重试继续消耗。如果只是偶尔超限可以等额度刷新或者换一个账号测试。如果是生产环境就必须购买付费套餐或者切到自托管。免费额度适合验证和不定期小批量采集不适合长期依赖。把它当成“试用装”不要当成生产资源。7. 常见报错与排查链路7.1 先按现象分类再逐层排查我在使用过程中遇到的报错基本可以归成这几类现象常见原因排查方向返回 401/403API Key 无效、额度超限检查 Key、后台剩余额度返回 429请求过于频繁加间隔、减小并发、看限流响应头markdown 为空页面需要 JS 渲染、被拦截调大 waitFor、抓 rawHtml 对比返回内容缺正文登录墙、跳转页、页面结构特殊查看原网页、确认 URL 是否真实crawl 长时间 pending站点响应慢、任务积压等待、查看后台任务状态本地部署抓取失败Redis、Playwright 服务没起来看容器日志、检查服务健康状态排查时不要直接改代码。先看返回的响应体Firecrawl 一般会在错误信息里说明原因。再看你的输入 URL很多问题根本不是 Firecrawl 的问题而是 URL 本身就是登录页或者 404 页面。7.2 抓到的内容和浏览器看到的不一样这种情况经常出现原因有几种网站根据登录状态返回不同内容。网站根据地区或 IP 返回不同内容。网站有 A/B 测试不同访客看到不同版本。页面内容是通过多次异步请求加载的等待时间不够。遇到这种情况先手工打开网页确认内容是否稳定。然后尝试在请求里带上对应的 Cookie 或 header。Firecrawl 支持自定义 headers你可以把浏览器里的 Cookie 复制过来测试。不过我要提醒一点携带 Cookie 抓页面只适合你自己有权限访问的站点。抓公共站点时不要试图绕过登录、验证码等访问控制。做内容采集要遵守目标站点的服务条款和 robots 协议。7.3 本地部署常见问题自托管遇到的典型问题Redis 没启动任务提交后一直排队不执行。Playwright 浏览器服务没启动页面渲染失败。环境变量缺失服务启动报错。内存不足多开几个浏览器实例后进程被系统杀掉。排查顺序是先看所有服务的容器是否存活再看日志里有没有 Redis 连接错误最后用小量测试确认渲染正常。自托管不是装上就能跑完的环境变量和依赖版本要严格按文档来。7.4 通用排查顺序如果问题不确定我建议按这个顺序排查看返回码和响应体。先确认 Firecrawl 是否正常处理了你的请求。看目标 URL 是否可以直接访问。把 URL 粘到浏览器里看内容是否正常。看原始 HTML。用 formats 里的 rawHtml 返回看页面源码里有没有正文。看日志。如果是自托管看 API 服务、Worker、Playwright 各自的日志。看参数。确认 waitFor、timeout、limit 这些参数是否和页面情况匹配。看资源。如果是自托管确认 CPU、内存、磁盘是否够用。这个顺序能解决大部分问题。最忌讳的是跳过前面几步直接去调参数。很多时候调半天参数最后发现是 URL 写错了或者 API Key 复制多了个空格。8. 云端托管还是自托管生产环境怎么选8.1 云端托管的价值在哪里云端方案最大的价值不是“不用部署”而是“不用维护反爬策略”。Firecrawl 团队维护了代理池、浏览器版本、渲染服务这些在自托管时都是要自己处理的。第二个价值是稳定性。云端有完整的任务队列和重试机制你不用操心 Redis 挂掉、浏览器崩溃、磁盘写满这些问题。对于小团队来说省掉的运维时间价值很高。第三个价值是响应速度。云端服务的出口 IP 和渲染资源是现成的你不用买服务器、配代理注册完就能跑。8.2 自托管的真实成本自托管的成本不只是服务器费用。你要维护的东西包括Docker 镜像的版本更新。Playwright 浏览器及系统依赖的更新。反爬策略目标站点会限制访问频率你需要自己处理 IP 封禁和访问间隔。任务队列的监控Redis 积压、任务失败、重试策略都要自己盯。内容清洗逻辑的版本维护。如果只是个人学习和原型验证自托管更像负担。如果是团队生产环境每天要抓大量页面且需要控制数据不出内网那么自托管有其合理性但前提是团队有人能扛住这些运维成本。8.3 我的建议我更推荐分阶段走第一个阶段用云端免费额度跑通全部功能验证输出质量。第二个阶段购买云端最低档套餐跑一个月真实业务统计日均页面数和成本。第三个阶段如果成本可控继续用云端如果成本很高再考虑自托管或混合模式。混合模式也很常见普通站点用自托管难抓的站点用云端。这样能平衡成本和成功率。最忌讳的是跳过验证直接上量。先抓 10 个页面再看 10 个页面的质量再决定抓 1000 个页面这是做任何采集任务都通用的节奏。最后留一个实际经验。Firecrawl 这类工具真正落地时最核心的问题往往不是“能不能抓”而是“抓完之后怎么持续维护”。站点改版、页面结构变动、链接失效、额度消耗这些都是长期要面对的。我现在的做法是所有抓回来的 Markdown 都落盘到本地目录按域名和日期分目录存放再建一个简单的索引文件记录 URL、抓取时间、内容长度和状态。这样即使后续 Firecrawl 某个版本行为变了我至少不会丢掉已经抓到的数据。如果你正在做 RAG 或 AI 搜索相关的采集工作先用免费额度把单页抓取和整站爬取各跑一遍确认输出质量能接受再决定要不要上量。很多问题不是工具能力不够而是目标页面本身有登录墙、结构特殊或者任务范围没有控制好。把这些边界摸清楚Firecrawl 会是一个相当好用的网页数据入口。