Crawl4AI 实战手册:从安装到并发爬取、动态页面与结构化提取的完整路径
【免费下载链接】crawl4ai🚀🤖 Crawl4AI: Open-source LLM Friendly Web Crawler & Scraper. Don't be shy, join here: https://discord.gg/jP8KfhDhyN项目地址: https://gitcode.com/GitHub_Trending/craw/crawl4ai
凌晨两点,你把一段 30 万字符的 HTML 原样丢给大模型,它回给你的答案却淹没在导航栏、广告位和页脚版权信息里;而一个同步爬虫卡在某个 JS 渲染的页面上,后面排队的 50 个 URL 全部干等。Crawl4AI 是一个开源的 LLM 友好型异步爬虫框架,把"浏览器渲染 → 内容清洗 → Markdown/结构化输出"这条链路压缩成几次 API 调用,这篇文章带你装好它、跑通基础案例,并把动态页面、并发控制和结构化提取这三块能力拆解到位。
它到底能帮你解决什么
🔍页面噪声拖垮 LLM 输入质量:原始 Markdown 把页面上的每个角落都翻译进去了。Crawl4AI 内置内容过滤器,fit_markdown会给你一份剪掉导航、页脚后的干净正文,直接可喂给模型。
⚡串行爬取慢得没法上生产:arun_many基于异步 IO 并发处理一批 URL,默认按可用内存自适应调节并发度,还能流式返回结果。
🕹️JS 渲染的页面抓回来是空壳:通过js_code注入脚本、wait_for等待条件、extraction_strategy输出 JSON 三个参数组合,动态站点和结构化数据都能拿到。
五分钟跑通第一个可运行案例
安装和浏览器内核准备各占一行,跑完crawl4ai-doctor无报错就可以开爬:
pip install -U crawl4ai crawl4ai-setup crawl4ai-doctor下面是最小可运行示例,一次arun调用拿到清洗后的 Markdown:
import asyncio from crawl4ai import AsyncWebCrawler async def main(): async with AsyncWebCrawler() as crawler: # 自动启动无头 Chromium result = await crawler.arun("https://example.com") print(result.markdown[:300]) # raw_markdown:HTML 转 Markdown 的完整结果 if __name__ == "__main__": asyncio.run(main())跑完你会在控制台看到 example.com 页面的 Markdown 开头部分——没有 requests 里那种<script>标签堆砌,这就是"LLM 友好"的第一层含义。
核心能力一:Markdown 输出的两种粒度怎么切换
解决的问题:result.markdown其实存了两份内容,用错了粒度,要么 LLM token 白白烧在导航上,要么解析时又丢了细节。
import asyncio from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode from crawl4ai import DefaultMarkdownGenerator from crawl4ai.content_filter_strategy import PruningContentFilter async def main(): config = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, # 默认即 BYPASS,这里显式声明 markdown_generator=DefaultMarkdownGenerator( content_filter=PruningContentFilter(threshold=0.4, threshold_type="fixed") ), ) async with AsyncWebCrawler() as crawler: result = await crawler.arun("https://news.ycombinator.com", config=config) print(len(result.markdown.raw_markdown)) # 全量转换,约 6 万字符 print(len(result.markdown.fit_markdown)) # 剪枝后,只剩正文主体 if __name__ == "__main__": asyncio.run(main())关键参数:raw_markdown是无过滤的完整转换结果,适合做存档和二次解析;fit_markdown是经过PruningContentFilter剪枝后的版本,threshold=0.4表示保留相关性得分前 40% 的文本块,这是喂给 LLM 的推荐粒度。过滤器会增加约 50ms 处理时间,换来的是 token 成本下降一个量级。
核心能力二:JS 渲染页面的脚本注入与等待怎么配
解决的问题:内容靠按钮点击或异步请求加载的页面,直接提取只能拿到骨架。
import asyncio from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode js_load_more = """ (async () => { const btn = [...document.querySelectorAll('button')] .find(b => b.textContent.includes('Load More')); if (btn) { btn.click(); } // 触发"加载更多"的异步加载 })(); """ async def main(): config = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, js_code=[js_load_more], # 页面加载完成后注入执行 wait_for=3000, # 给异步请求留足时间再提取 page_timeout=60000, ) async with AsyncWebCrawler(config=BrowserConfig(headless=False)) as crawler: result = await crawler.arun("https://example.com/feed", config=config) print(result.markdown.raw_markdown[:300]) if __name__ == "__main__": asyncio.run(main())js_code接收字符串列表,每条都会在页面就绪后执行;wait_for可以是超时毫秒数或 CSS 选择器,选择器形式会等到元素出现才提取,比死等更稳。本地调试时把headless=False打开,能直接看到脚本点击的过程,排查选择器问题时非常直观。
核心能力三:并发爬取的流式结果与信号量怎么调
解决的问题:要一次抓几十个页面,串行跑要等最慢的那个,还容易把浏览器内存撑爆。
import asyncio from crawl4ai import AsyncWebCrawler, CrawlerRunConfig, CacheMode urls = [ "https://example.com/page1", "https://example.com/page2", "https://example.com/page3", ] async def main(): config = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, stream=True, # 边完成边产出,不等全部结束 semaphore_count=8, # 最多同时 8 个页面在跑 ) async with AsyncWebCrawler() as crawler: async for result in await crawler.arun_many(urls, config=config): if result.success: print(f"[OK] {result.url},{len(result.markdown.raw_markdown)} 字符") else: print(f"[ERR] {result.url} => {result.error_message}") if __name__ == "__main__": asyncio.run(main())默认调度器会按系统可用内存自适应调节并发度;stream=True让结果在各自完成时立刻产出,适合接后续入库或 LLM 处理。单个 URL 失败不会中断整批,success标志位配合error_message足够你做重试逻辑。
组合拳——实际项目里怎么搭配用
场景 A:登录态下的多步操作。先访问登录页,再带着同一session_id抓后台页面,后续步骤用js_only=True避免重复导航,最后kill_session()清理:
result1 = await crawler.arun( url="https://example.com/login", session_id="my_session", # 建立会话,Cookie/Storage 都会保留 ) result2 = await crawler.arun( url="https://example.com/dashboard", session_id="my_session", # 复用同一浏览器上下文 js_only=True, # 只执行 JS,不重新加载页面 ) await crawler.kill_session("my_session") # 用完即释放适合电商后台、需要翻页点击的报表页等"一个页面状态要带到下一个动作"的流程。
场景 B:CSS 定位 + JSON 提取一次到位。用css_selector把抓取范围锁死在列表区,extraction_strategy直接吐出结构化数据,全程不花 LLM 的钱:
from crawl4ai import JsonCssExtractionStrategy schema = { "name": "Items", "baseSelector": "div.item", "fields": [ {"name": "title", "selector": "h2", "type": "text"}, {"name": "link", "selector": "a", "type": "attribute", "attribute": "href"}, ], } config = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, css_selector="div.list", # 只在这个容器里干活 extraction_strategy=JsonCssExtractionStrategy(schema), ) result = await crawler.arun(url="https://example.com/list", config=config) data = json.loads(result.extracted_content)适合列表结构稳定、需要批量入库的页面;换选择器、换 schema,别动流程。
场景 C:LLM 提取不规整页面。页面结构没法用选择器锁死时,用 Pydantic 模型定义你要的字段,让模型按 schema 解析,word_count_threshold=1把内容尽量保留给模型看:
from pydantic import BaseModel, Field from crawl4ai import LLMConfig, LLMExtractionStrategy class ProductFee(BaseModel): name: str = Field(..., description="产品或模型名称") input_fee: str = Field(..., description="输入费用") config = CrawlerRunConfig( cache_mode=CacheMode.BYPASS, word_count_threshold=1, extraction_strategy=LLMExtractionStrategy( llm_config=LLMConfig(provider="openai/gpt-4o", api_token="your-token"), schema=ProductFee.model_json_schema(), instruction="提取文中所有条目及其费用,不要遗漏", ), )适合定价页、财报这类"人能读懂但 DOM 很乱"的内容,输出同样落在extracted_content。
踩坑与避坑指南
现象:启动爬虫后长时间卡住或直接报错,页面一个都加载不了。原因:Playwright 的浏览器内核没装,crawl4ai-setup在部分环境静默失败。 解法:python -m playwright install --with-deps chromium,装完再跑crawl4ai-doctor确认。
现象:raw_markdown里混着导航、页脚、广告,token 烧得心疼。原因:默认只生成 raw 版本,fit_markdown需要显式挂内容过滤器才会产出。 解法:markdown_generator=DefaultMarkdownGenerator(content_filter=PruningContentFilter(threshold=0.4, threshold_type="fixed"))。
现象:动态页面抓回来是空壳,只有标题和骨架文本。原因:提取时机早于 JS 异步请求完成,wait_for没设或设小了。 解法:wait_for=5000,或者传一个内容容器的 CSS 选择器,等它出现再提取。
现象:并发量一上来,浏览器内存飙升、页面互相拖慢。原因:并发数超过内存调度器的舒适区,多个页面同时跑重资源请求。 解法:CrawlerRunConfig(semaphore_count=8)压低上限,浏览器侧BrowserConfig(memory_saving_mode=True)降低单页开销。
Crawl4AI 把渲染、清洗、提取压进了同一套异步 API,覆盖"能跑的页面"和"能解析的结构",代价是它的输出质量依赖你给对粒度参数,纯静态小批量场景用它属于杀鸡用牛刀。把第一段代码里的 URL 换成你自己的页面跑一遍,然后按能力二给你的动态页面加一个wait_for——这就是接入生产前的最小闭环。
【免费下载链接】crawl4ai🚀🤖 Crawl4AI: Open-source LLM Friendly Web Crawler & Scraper. Don't be shy, join here: https://discord.gg/jP8KfhDhyN项目地址: https://gitcode.com/GitHub_Trending/craw/crawl4ai
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考