过去我们为了从网页上扒数据,动辄要写上百行 Scrapy 配置、处理反爬、清洗标签——这些事 Crawl4AI 一行异步调用就解决了。而且它产出的直接就是大模型吃得动的干净 Markdown 和结构化 JSON。
一、问题引入:为什么传统爬虫工具在大模型时代捉襟见肘
在 AI 应用落地过程中,数据获取几乎是绕不过去的第一个坎。RAG 系统需要干净的 Markdown 文本,智能体需要从网页提取结构化信息,数据分析管道需要批量抓取并格式化多页面内容。
传统方案有两条路:
| 方案 | 痛点 |
|---|
| Scrapy / BeautifulSoup | 配置工程量大,HTML 清洗需手写 XPath/CSS 选择器,JS 动态渲染要额外集成 Selenium |
| 商用 API(Diffbot / Zyte) | 按调用量计费,数据量大时成本陡增,且输出格式未必直接适配 AI 管道 |
| 自己拼装 Selenium + BS4 | 维护反爬策略极度耗时,页面结构一变就要重写解析逻辑 |
这些方案的共性问题:产出的是未经处理的 HTML 片段,而非 LLM 可直接消费的内容。
Crawl4AI 正是在这个痛点下诞生的。它是一个完全开源、MIT 协议授权的 Python 库,专为 LLM 和 AI 智能体设计——把"抓取"和"AI-ready 格式化"两步合为一体。
二、Crawl4AI 核心能力拆解
以下基于最新版本(V0.4.x,截至 2026 年 6 月稳定分支)的能力梳理:
2.1 关键特性一览
| 特性 | 说明 | 对开发者的实际价值 |
|---|
| LLM 友好输出 | 原生支持 Markdown、Clean HTML、结构化 JSON 三种输出 | 省去后处理清洗环节,直接喂给 LLM 或向量库 |
| JS 动态渲染 | 内置 Playwright,自动执行页面 JavaScript | 无需额外集成无头浏览器 |
| 多种提取策略 | CSS 选择器、XPath、LLM 语义提取、JsonCssExtractionStrategy | 既能精确抽取也能语义理解 |
| 异步+并行 | 全异步架构,支持多 URL 并发抓取 | 百级页面抓取从分钟级降到秒级 |
| 会话管理 | 跨请求保持 Cookie 和上下文 | 登录后分步骤抓取不再麻烦 |
| 反爬绕过 | 自定义 User Agent、代理、JS 钩子、截图验证 | 减少被屏蔽的概率 |
| 零成本 | MIT 协议,完全免费 | 无商业许可顾虑 |
2.2 性能参考
基于官方基准测试和社区反馈,在标准宽带环境下:
- 单 URL 抓取 + Markdown 转换:0.8-1.5 秒
- 10 个 URL 并行抓取:3-5 秒(传统方案需 30-60 秒)
- 动态 JS 页面(如 SPA):额外耗时 1-3 秒(首次启动 Playwright 浏览器实例)
三、从零搭建:完整的代码实战
3.1 环境准备
# Python 3.9+ 环境
pip install crawl4ai
# 安装 Playwright 浏览器驱动(首次使用必须执行)
crawl4ai-setup
# 手动安装备选:
# playwright install chromium
3.2 基本使用:抓取单页面并输出 Markdown
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://example.com/article",
)
# 直接拿到干净的 Markdown,省去一切清洗步骤
print(result.markdown)
# 也可以获取完整 HTML、提取的媒体链接等
print(f"页面标题: {result.metadata['title']}")
print(f"提取到 {len(result.media['images'])} 张图片")
asyncio.run(main())
3.3 进阶实战:面向 RAG 系统的批量抓取流水线
下面是一个接近生产级别的示例——从多个技术博客抓取内容,清洗后存入本地文件,随时可接入向量数据库:
import asyncio
import json
import os
from datetime import datetime
from crawl4ai import AsyncWebCrawler, CacheMode
# 目标 URL 列表
TARGET_URLS = [
"https://news.ycombinator.com/",
"https://simonwillison.net/",
"https://lilianweng.github.io/",
]
async def crawl_single(crawler: AsyncWebCrawler, url: str):
"""抓取单个 URL,返回结构化结果"""
result = await crawler.arun(
url=url,
cache_mode=CacheMode.BYPASS,
word_count_threshold=100, # 过滤短内容
exclude_external_links=True, # 去掉外链噪音
remove_overlay_elements=True, # 自动移除弹窗
)
return {
"url": url,
"title": result.metadata.get("title", ""),
"markdown": result.markdown[:5000], # 截断长文本,按需调整
"timestamp": datetime.now().isoformat(),
}
async def batch_crawl(urls: list[str], concurrency: int = 5):
"""并行批量抓取"""
async with AsyncWebCrawler() as crawler:
semaphore = asyncio.Semaphore(concurrency)
async def bounded_crawl(url):
async with semaphore:
return await crawl_single(crawler, url)
tasks = [bounded_crawl(url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
# 过滤掉异常
valid_results = [r for r in results if not isinstance(r, Exception)]
return valid_results
async def main():
results = await batch_crawl(TARGET_URLS, concurrency=5)
# 保存为 JSONL——方便直接导入向量库或做后续处理
output_dir = "crawled_data"
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, "corpus.jsonl")
with open(output_path, "w", encoding="utf-8") as f:
for item in results:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f"完成!{len(results)}/{len(TARGET_URLS)} 个页面抓取成功")
print(f"数据已保存至: {output_path}")
asyncio.run(main())
3.4 使用 LLM 进行语义提取
对于结构不规则的页面,CSS 选择器写起来费劲?直接上 LLM 提取策略:
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import LLMExtractionStrategy
async def extract_with_llm():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://example.com/product-page",
extraction_strategy=LLMExtractionStrategy(
provider="openai/gpt-4o-mini",
api_token="your-api-key",
instruction="提取产品名称、价格、库存状态和用户评分"
),
)
# result.extracted_content 直接就是结构化的提取结果
print(result.extracted_content)
3.5 截图与自定义 JS 钩子
遇到需要登录或验证的页面?用 JS 钩子在抓取前执行自定义逻辑:
async def login_then_scrape():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://example.com/dashboard",
js_code=[
# 抓取前自动填写登录表单并提交
"document.querySelector('#username').value = 'myuser';",
"document.querySelector('#password').value = 'mypass';",
"document.querySelector('#login-btn').click();",
],
wait_for="css:#dashboard-content", # 等待登录后的内容加载
screenshot=True, # 截图留档
)
# 保存截图
with open("page_screenshot.png", "wb") as f:
import base64
f.write(base64.b64decode(result.screenshot))
四、适用场景与选型建议
4.1 最适合的场景
| 场景 | 为什么 Crawl4AI 是首选 |
|---|
| RAG 知识库构建 | 直接输出 Markdown,零清洗成本,批量并行抓取效率高 |
| AI 智能体数据采集 | LLM 提取策略 + 结构化 JSON 输出,智能体可直接消费 |
| 竞品监控/舆情分析 | 会话管理 + 代理支持,可持续抓取登录后的页面 |
| 个人知识管理 | 一键把网页转为本地 Markdown 笔记 |
| 数据科学原型验证 | 10 行代码搭起数据采集管道,快速验证想法 |
4.2 不太适合的场景
- 超大规模分布式抓取(亿级页面):不是 Crawl4AI 的设计目标,建议走 Scrapy + 分布式调度
- 需要复杂爬虫调度逻辑:Crawl4AI 不内置任务队列和调度器,需自行集成 Celery / RabbitMQ
4.3 与同类方案横向对比
| 维度 | Crawl4AI | Scrapy | Playwright 裸用 | Diffbot API |
|---|
| 上手成本 | 极低(10 行) | 高(项目骨架 + 配置) | 中(自己写解析) | 低(调 API) |
| AI 输出适配 | 原生支持 | 需后处理 | 需后处理 | 原生支持 |
| 动态 JS 渲染 | 内置 | 需中间件 | 原生 | 内置 |
| 并行能力 | 异步原生 | Scrapy 引擎 | 需自己实现 | 由 API 侧处理 |
| 成本 | 免费 | 免费 | 免费 | 按调用计费 |
| 定制深度 | 中等 | 极深 | 极深 | 低(受 API 限制) |
五、实战踩坑与注意事项
- 首次安装后别忘了 crawl4ai-setup:这条命令会下载 Chromium 浏览器内核(约 150MB),忘了执行会直接报 Browser closed unexpectedly。
- 内存占用:每个并行任务都会启动一个浏览器上下文,concurrency 不建议超过 10,否则 16GB 内存机器会吃紧。
- 反爬对抗:遇到强反爬(Cloudflare 五秒盾等),配合 proxy 参数和合理的 user_agent 可以大幅改善。
- 输出截断:默认情况下超长页面的 Markdown 输出可能被截断,设置 max_content_length 参数可以调整。
- 缓存机制:开发调试时建议 CacheMode.BYPASS,生产环境使用默认缓存可以减少对目标站点的请求频率。
六、总结
Crawl4AI 不是一个试图取代 Scrapy 的"全能爬虫框架",它的定位非常克制且精准:做 LLM 和 AI 应用与网页数据之间最短、最高效的那条管道。
如果你正在做 RAG 系统、AI 智能体、或者任何需要从网页获取数据喂给大模型的项目——把 Crawl4AI 加到依赖里,几乎不需要犹豫。MIT 协议、零成本、三五行代码出结果,这种工具在开源社区里并不多见。
说实话,用惯 Crawl4AI 之后再回头看以前手写的那些 Scrapy 爬虫配置和 HTML 清洗正则,会有一种"时代真的变了"的感觉。
421

被折叠的 条评论
为什么被折叠?



