1. 为什么网页直接丢给模型,RAG 效果总是不对劲
做 RAG 和 Agent 数据准备时,最容易踩的坑不是模型选错,而是喂进去的网页太脏。整页 HTML 里塞着导航栏、广告位、页脚版权、Cookie 弹窗、推荐文章、脚本样式,模型真正能用的正文可能只占 20%。检索时命中一堆无关片段,回答自然飘。
Crawl4AI 解决的就是这件事:把网页转成适合大模型阅读的干净 Markdown。它和 MarkItDown 是一对搭档——MarkItDown 处理 PDF/Word/PPT/Excel,Crawl4AI 处理在线网页,两者输出统一成 Markdown 后进入同一条 RAG 流水线。
这篇聚焦一个具体目标:用 Crawl4AI 抓网页输出 Markdown,再通过 TaoToken 统一 Key 和 API 通道接入模型做摘要验证,一次跑通「抓取 → Markdown → 模型摘要」的链路。适合正在搭知识库、做 Agent 网页阅读工具、或者被脏数据折磨过的开发者。下面所有代码和配置都可以直接复制运行。
2. TaoToken 前置:统一 Key 与 API 通道
Crawl4AI 负责把网页变成 Markdown,但 Markdown 质量好不好、摘要准不准,需要一个模型来验证。如果每个环节都单独配一套 Key 和 Base URL,调试成本会很高。TaoToken 在这里的作用是提供统一的 API 通道,一个 Key 走完模型调用。
你需要先拿到 API Key。访问控制台创建:
https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite创建后保存好 Key,后面 Python 脚本里通过环境变量读取,不要硬编码进代码。TaoToken 的 API 入口是:
https://taotoken.net/api注意这个地址不带 UTM 参数,是纯 API 端点。模型对话调试可以用:
https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite如果你后续要做长期编码或 Agent 任务,可以了解 Coding Plan:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite接入文档在这里,遇到参数问题可以对照:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite环境变量配置建议这样写,Windows PowerShell 和 macOS/Linux 分别处理:
# macOS / Linux export TAOTOKEN_API_KEY="你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"# Windows PowerShell $env:TAOTOKEN_API_KEY="你的Key" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api"这样 Crawl4AI 抓取和模型摘要验证共用一套通道,排查问题时只需要关注一个入口。
3. 可复制配置:Crawl4AI 抓取骨架与 Markdown 输出
3.1 环境准备
先建虚拟环境,避免依赖冲突:
mkdir crawl4ai-rag-demo cd crawl4ai-rag-demo python -m venv .venv source .venv/bin/activate # Windows 用 .\.venv\Scripts\Activate.ps1 pip install -U crawl4ai openai crawl4ai-setup crawl4ai-doctor如果 doctor 报浏览器问题,手动装 Chromium:
python -m playwright install --with-deps chromium3.2 基础抓取脚本
新建crawl_to_markdown.py,这是最小可运行骨架:
import asyncio from pathlib import Path from urllib.parse import urlparse from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode def filename_from_url(url: str) -> str: parsed = urlparse(url) path = parsed.path.strip("/").replace("/", "_") or "index" return f"{parsed.netloc}_{path}.md" async def crawl_one(url: str, output_dir: Path): browser_cfg = BrowserConfig(headless=True, verbose=False) run_cfg = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, word_count_threshold=10, exclude_external_links=True, remove_overlay_elements=True, ) async with AsyncWebCrawler(config=browser_cfg) as crawler: result = await crawler.arun(url=url, config=run_cfg) if not result.success: print(f"failed: {url} -> {result.error_message}") return out = output_dir / filename_from_url(url) out.write_text(result.markdown, encoding="utf-8") print(f"saved: {out} ({len(result.markdown)} chars)") async def main(): urls = [ "https://docs.crawl4ai.com/", "https://docs.crawl4ai.com/core/quickstart/", ] output_dir = Path("output") output_dir.mkdir(exist_ok=True) for url in urls: await crawl_one(url, output_dir) if __name__ == "__main__": asyncio.run(main())关键参数说明:
| 参数 | 作用 | 建议值 |
|---|---|---|
headless | 是否无头浏览器 | True,服务器环境必须 |
cache_mode | 缓存策略 | BYPASS 调试,ENABLED 生产 |
word_count_threshold | 过滤过短片段 | 10 起步,正文短可调低 |
exclude_external_links | 去掉外链 | True,减少噪声 |
remove_overlay_elements | 去弹窗遮罩 | True,去 Cookie 弹窗 |
3.3 加元数据头,方便追溯来源
Markdown 文件顶部加 YAML front matter,后续 RAG 回答可以引用来源:
from datetime import datetime def with_metadata(markdown: str, url: str) -> str: header = ( "---\n" f"source_url: {url}\n" "source_type: web\n" f"crawled_at: {datetime.utcnow().isoformat()}\n" "---\n\n" ) return header + markdown在crawl_one里把result.markdown换成with_metadata(result.markdown, url)即可。
4. 验证请求:用 TaoToken 接入模型做摘要验证
抓完 Markdown 不能直接入库,先让模型读一遍,看摘要是否抓住重点。这一步同时验证 TaoToken 通道是否通。
新建verify_with_llm.py:
import os from pathlib import Path from openai import OpenAI client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url=os.environ["TAOTOKEN_BASE_URL"], ) def summarize(md_text: str, max_chars: int = 6000) -> str: snippet = md_text[:max_chars] resp = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是 RAG 数据质检员,用 5 条要点总结网页正文,忽略导航和广告。"}, {"role": "user", "content": snippet}, ], temperature=0.2, ) return resp.choices[0].message.content if __name__ == "__main__": md_file = Path("output/docs.crawl4ai.com_index.md") text = md_file.read_text(encoding="utf-8") print(summarize(text))运行:
python verify_with_llm.py成功时会输出 5 条要点摘要。如果摘要里出现「点击这里」「版权所有」「订阅」这类内容,说明 Markdown 清洗不够,需要回到 Crawl4AI 配置调exclude_external_links或加 CSS 选择器限定正文区域。
4.1 用 CSS 选择器锁定正文
很多文档站正文在<article>或<main>里,直接指定能大幅提升质量:
run_cfg = CrawlerRunConfig( css_selector="article, main", cache_mode=CacheMode.BYPASS, word_count_threshold=10, )实测下来,加了css_selector后导航和页脚基本消失,摘要准确率明显提升。
5. 本篇常见错排查
5.1 抓取返回空 Markdown
页面是 JS 渲染的,或者被反爬拦截。先确认headless=True下浏览器能加载,再检查是否需要等待:
run_cfg = CrawlerRunConfig( wait_for="css:article", page_timeout=30000, )wait_for让 Crawl4AI 等到正文元素出现再抓。
5.2 TaoToken 调用报 401
检查环境变量是否生效。在 Python 里打印:
import os print(os.environ.get("TAOTOKEN_API_KEY", "NOT SET")) print(os.environ.get("TAOTOKEN_BASE_URL", "NOT SET"))如果 Key 有但报 401,确认base_url结尾没有多余斜杠,正确写法是https://taotoken.net/api。
5.3 摘要里混入大量无关内容
三种处理方式按优先级:加css_selector限定正文;调高word_count_threshold过滤短片段;在模型 prompt 里明确要求忽略导航和广告。三者叠加效果最好。
5.4 表格在 Markdown 里散架
复杂表格 Crawl4AI 可能转不好。可以保留 HTML 片段:
run_cfg = CrawlerRunConfig( css_selector="article", keep_data_attributes=True, )然后在后处理阶段单独提取<table>转 Markdown。
5.5 批量抓取被限流
不要高频打爆目标站。加间隔和重试上限:
import asyncio for url in urls: await crawl_one(url, output_dir) await asyncio.sleep(2)尊重 robots.txt 和站点条款,不做违规绕过。
5.6 把抓取服务暴露公网的安全风险
如果做成 API 让用户提交 URL,必须防 SSRF。禁止localhost、127.0.0.1、169.254.169.254、内网 IP 和file://协议。生产环境加 URL 白名单和鉴权。
6. 从抓取到入库的完整链路
把前面几步串起来,实际流水线是这样:
urls.txt -> Crawl4AI 抓取(css_selector 限定正文) -> Markdown 文件(带 YAML 元数据) -> TaoToken 模型摘要验证 -> 人工抽样检查 -> 按标题切块 -> 向量化 -> 导入 Dify / Open WebUI / 自研 RAG -> 真实问题测试命中率批量脚本可以这样组织:
async def pipeline(urls_file: str, output_dir: str): urls = [u.strip() for u in Path(urls_file).read_text().splitlines() if u.strip()] out = Path(output_dir) out.mkdir(exist_ok=True) for url in urls: await crawl_one(url, out) await asyncio.sleep(2)跑完先人工看 3 个文件的 Markdown 质量,确认标题层级、表格、链接都正常,再进入切块和向量化。数据质量决定 RAG 上限,这一步省不得。
如果你在接入模型做摘要验证时遇到通道问题,回到 API Keys 页面确认 Key 状态:
https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite参数细节对照接入文档:
https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite长期做 Agent 网页阅读和编码任务,Coding Plan 会更省心:
https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite先把 3 个公开文档页跑通,确认 Markdown 干净、摘要准确,再扩到批量抓取和定时更新。链路一次跑通,后面就是重复和优化的事。