用 Python 将网页批量整理为 RAG 文档
教程通过 Firecrawl 展示单页提取、批量 URL 保存及整站文档抓取,将网页转换为保留标题和代码块的 Markdown。示例强调提取正文,减少导航、页脚等内容占用模型上下文。
AI 深度解读
把网页交给 LLM,先要解决的往往不是提示词,而是输入里混进了多少导航、脚本和侧栏。作者用 Firecrawl 的 Python SDK,把网页转成保留标题、代码块的 Markdown,再按单页、已知 URL 列表、整站文档三种规模组织采集。
这套流程的价值在于减少逐站维护爬虫的工作,并为检索增强生成(RAG)准备材料。但抓到 Markdown 只是第一步:采集范围、空页面和分块方式,仍然需要自己控制。
先提取正文,再考虑塞进上下文
作者的核心主张是:原始 HTML 会让导航、脚本和标记占用上下文,LLM 更需要带有结构的正文。Firecrawl 提供 API 完成转换,原文称它也支持 JavaScript 渲染的页面。
示例固定 SDK 版本,安装命令是 pip install firecrawl==4.55.0。单页抓取可以无 API key 试用,但每个 IP 的速率限制较低。正式使用时,作者通过 export FIRECRAWL_API_KEY="fc-YOUR_API_KEY" 配置密钥。
下面这段代码解决单页正文转 Markdown 的问题:
from firecrawl import Firecrawl
firecrawl = Firecrawl() # 读取 FIRECRAWL_API_KEY
doc = firecrawl.scrape(
"https://docs.firecrawl.dev/introduction",
formats=["markdown"],
only_main_content=True,
)
print(doc.markdown)
重点看 only_main_content=True:它用于去掉页头、页脚、Cookie 横幅和侧栏。这个选项默认开启,作者显式写出,是为了让读代码的人知道采集范围。
正文提取能减少输入噪声,但不能保证内容完整、正确。
原文没有给出清理前后的 token 数、正文保留率或模型回答质量对比,因此「能省多少上下文」仍需拿自己的目标页面验证。
已知链接用批量任务,整站采集先摸清范围
如果已经知道要哪些页面,作者使用 batch_scrape,把 URL 列表作为一个任务提交。示例参数是 formats=["markdown"]、poll_interval=2、timeout=120,结果保存到 pages 文件夹。
文件名取自 page.metadata.source_url 的路径:去掉首尾 /,把路径中的 / 换成 _,空路径用 index。这样便于辨认页面,但它并没有把完整来源地址保存进文件,后续要准确引用来源,最好另外保留 URL 映射。
整站采集则先调用 firecrawl.map("https://docs.firecrawl.dev", limit=500),查看能发现的链接,再决定范围。原文称 map 快且便宜,却没有说明它的具体计费方式,也没有保证发现数量就是最终抓取数量。
下面的代码展示了如何限制采集页数,并在抓取前排除无关栏目:
from pathlib import Path
from urllib.parse import urlparse
from firecrawl import Firecrawl
firecrawl = Firecrawl()
job = firecrawl.crawl(
"https://docs.firecrawl.dev",
limit=50,
exclude_paths=["blog/.+", "changelog.*"],
scrape_options={"formats": ["markdown"], "only_main_content": True},
)
out = Path("docs")
out.mkdir(exist_ok=True)
for page in job.data:
slug = urlparse(page.metadata.source_url).path.strip("/").replace("/", "_") or "index"
(out / f"{slug}.md").write_text(page.markdown or "", encoding="utf-8")
print(f"{job.status}: {len(job.data)} pages")
要留意 limit=50 和 exclude_paths:前者控制规模,后者提前排除博客和更新日志。抓完才过滤,已经消耗的额度不会因此收回。
按原文给出的方案,免费账号每月有 1,000 credits,每页消耗 1 credit。这是文章中的额度说明,不能直接当作长期不变的服务承诺。
crawl 会阻塞到任务结束。大站点可以用 start_crawl 立即取得任务 ID,再通过 get_crawl_status 查询进度,避免脚本一直卡在一次调用上。
Markdown 落盘后,为什么还不能直接建索引
作者建议先检查近乎空白的页面。登录墙、反机器人验证可能只返回几行提示,文件生成成功并不代表拿到了正文。跳过少于几百字符的内容,可以拦住一部分噪声。
不过,这只是粗筛规则:短小的有效文档也可能被误删,较长的验证提示也可能漏过。原文没有给出精确阈值或识别准确率。
分块时,作者建议按标题切分,例如按 ## 分段,而不是机械地按固定字符数切。理由是 Firecrawl 保留标题结构,按标题切更容易让一个片段围绕同一主题。
这个建议适合作为起点,但章节长度并不均匀,实际入库仍需检查过长片段。原文没有展开分块大小、重叠策略或检索评测。
如果需要结构化字段,scrape 还可以接收 JSON schema;文章只提到这一能力,没有提供具体示例。
我的判断
这篇最有用的是采集顺序:先确认范围,再限制抓取,随后清理内容,最后分块建索引。它把费用和检索噪声提前纳入流程,适合程序员拿来搭文档采集原型。
但它是一份操作教程。文中给出了代码,没有展示运行结果,也没有与自建爬虫、站点原生 Markdown 或其他正文提取方案做对照。省维护成本、标题分块更合适,都有合理依据,效果仍属于经验判断。
对结构稳定、已有 Markdown 源文件的文档,直接读取源文件也值得比较;对大量依赖浏览器渲染的外部页面,API 转换更有吸引力。选择要看目标页面,而不是只看调用代码有多短。
另外,原文披露了推荐链接会给双方增加额度。它不影响示例的阅读价值,但有关成本和便利性的判断,仍应结合自己的验证。
能拿来做什么
- 给前端项目准备依赖文档上下文时,先用
scrape抓一页,检查代码块、标题和正文是否完整,再扩大范围。 - 给日常 AI 编程整理一组指定文档时,用
batch_scrape批量保存,并额外记录完整来源 URL,方便核对回答出处。 - 搭建文档 RAG 原型时,先
map查看范围,再设置limit和exclude_paths,入库前筛查异常短页面,并抽查按标题切出的片段。