大家好,我是木木。
今天给大家分享一个利落的 Python 库,crawl4ai。
crawl4ai
做网页内容抽取时,最费劲的地方往往不是“能不能请求到页面”,而是如何把页面里的正文、链接、结构化字段和动态渲染结果整理成后续系统真正好用的数据。crawl4ai 的定位很直接:把网页抓取成适合 LLM、RAG、Agent 和自动化流水线使用的 Markdown 或 JSON。
项目地址:https://github.com/unclecode/crawl4ai
官方文档:https://docs.crawl4ai.com
三大特点
Markdown 友好
它会把网页内容整理成更适合 RAG、摘要和 Agent 输入的 Markdown,减少自己写清洗规则的工作量。
抽取可控
除了整页转 Markdown,也能用 CSS schema 抽结构化字段,适合商品卡片、列表页、文档页这类稳定结构。
浏览器就绪
底层支持浏览器渲染、缓存、内容过滤和多种运行配置,遇到动态页面时比单纯 requests 更从容。
最佳实践
安装方式:python -m pip install crawl4ai==0.8.6
如果是第一次在机器上跑动态页面,建议再执行一次 crawl4ai-setup 准备浏览器环境;如果只验证本地 HTML,也可以先用下面这种 raw: 模式把核心流程跑通。
功能一:把页面整理成 Markdown
这段代码解决什么问题:先不碰外部网站,直接用一段 HTML 验证 crawl4ai 的 Markdown 输出。这个步骤适合接入前做冒烟测试,确认抓取结果、正文和链接都能进入统一的数据格式。
importasyncioimportwarningswarnings.filterwarnings("ignore")fromcrawl4aiimportAsyncWebCrawler,BrowserConfig,CrawlerRunConfig,CacheModehtml="""raw:<html><body><main><h1>订单日报</h1><p>今日新增订单 42 单。</p><a href="https://example.com/report">查看报表</a></main></body></html>"""asyncdefmain():browser=BrowserConfig(headless=True,verbose=False)config=CrawlerRunConfig(cache_mode=CacheMode.BYPASS,verbose=False)asyncwithAsyncWebCrawler(config=browser)ascrawler:result=awaitcrawler.arun(url=html,config=config)markdown=str(result.markdown).strip().replace("\n"," | ")print("抓取成功:",result.success)print("Markdown:",markdown)print("内部链接:",len(result.links.get("internal",[])))asyncio.run(main())
这里的关键是把抓取配置和浏览器配置分开。后面接真实站点时,可以继续在 CrawlerRunConfig 里加缓存、过滤、抽取策略,而不是把所有逻辑散落在业务代码里。
功能二:用 CSS 规则抽结构化数据
这段代码解决什么问题:有些页面不需要整篇 Markdown,而是要列表里的字段,比如标题、价格和详情链接。JsonCssExtractionStrategy 可以把这些字段声明成 schema,让输出更接近业务系统需要的 JSON。
importasyncioimportjsonimportwarningswarnings.filterwarnings("ignore")fromcrawl4aiimportAsyncWebCrawler,BrowserConfig,CrawlerRunConfig,CacheModefromcrawl4aiimportJsonCssExtractionStrategyhtml="""raw:<html><body><section id="products"><article class="product"><h2>Pro 套餐</h2><span class="price">¥299/月</span><a href="/pro">详情</a></article><article class="product"><h2>Team 套餐</h2><span class="price">¥699/月</span><a href="/team">详情</a></article></section></body></html>"""schema={"name":"ProductCards","baseSelector":"article.product","fields":[{"name":"name","selector":"h2","type":"text"},{"name":"price","selector":".price","type":"text"},{"name":"href","selector":"a","type":"attribute","attribute":"href"},],}asyncdefmain():browser=BrowserConfig(headless=True,verbose=False)strategy=JsonCssExtractionStrategy(schema,verbose=False)config=CrawlerRunConfig(cache_mode=CacheMode.BYPASS,extraction_strategy=strategy,verbose=False)asyncwithAsyncWebCrawler(config=browser)ascrawler:result=awaitcrawler.arun(url=html,config=config)items=json.loads(result.extracted_content)print("抽取数量:",len(items))foriteminitems:print(f"{item['name']} | {item['price']} | {item['href']}")asyncio.run(main())

这个模式很适合和数据库、表格、消息队列对接。我的建议是把 schema 放进配置文件,并给每个页面类型准备一组小样本测试,防止页面改版后静默抽错字段。
环境与版本信息
- Demo 环境:Windows 11,Python 3.11
- 关键依赖版本:
playwright 1.58.0、pydantic 2.12.5、aiohttp 3.13.3 - 解析相关依赖:
beautifulsoup4 4.12.3、lxml 5.4.0、httpx 0.28.1 - GitHub 最近一次推送时间:
2026-04-24T16:53:57Z
高级功能
这段代码解决什么问题:真实网页通常有导航、订阅栏、页脚和广告块。如果这些噪声直接进入 RAG 或摘要任务,后面的模型会更容易跑偏。内容过滤器可以先把正文压出来,再交给下游系统。
importasyncioimportwarningswarnings.filterwarnings("ignore")fromcrawl4aiimportAsyncWebCrawler,BrowserConfig,CrawlerRunConfig,CacheModefromcrawl4ai.content_filter_strategyimportPruningContentFilterfromcrawl4ai.markdown_generation_strategyimportDefaultMarkdownGeneratorhtml="""raw:<html><body><nav>Home Pricing Docs Login Register</nav><aside>Subscribe newsletter and follow our social channels.</aside><main><h1>Release Notes</h1><p>Crawl4AI converts web pages into clean Markdown for downstream automation.</p><p>The crawler keeps the main content and drops noisy navigation blocks.</p></main><footer>Copyright links and legal text.</footer></body></html>"""asyncdefmain():browser=BrowserConfig(headless=True,verbose=False)generator=DefaultMarkdownGenerator(content_filter=PruningContentFilter(threshold=0.35,threshold_type="fixed",min_word_threshold=0))config=CrawlerRunConfig(cache_mode=CacheMode.BYPASS,markdown_generator=generator,verbose=False)asyncwithAsyncWebCrawler(config=browser)ascrawler:result=awaitcrawler.arun(url=html,config=config)filtered=result.markdown.fit_markdownor""print("原始长度:",len(result.markdown.raw_markdown))print("过滤后长度:",len(filtered))print("保留正文:",filtered.strip().replace("\n"," | "))asyncio.run(main())

这类过滤不是银弹,阈值需要用真实页面校准。上线前最好把过滤前后的 Markdown 都留一份样本,方便排查“正文被删多了”或“噪声没删干净”的问题。
适用场景
- 你要把网页整理成 Markdown,交给 RAG、摘要、问答或 Agent 使用
- 页面需要浏览器渲染,普通 HTTP 请求拿不到完整内容
不适用场景
- 目标服务已经提供稳定 API,直接调用 API 会更准确、更省资源
- 任务要求金融、医疗、法务级别的确定性,不能接受页面结构变化带来的波动
上线检查
- 先确认 robots、服务条款、访问频率和数据使用边界,不要把爬虫当成默认许可。
- 给每类页面准备样本测试,校验 Markdown、JSON 字段和空结果处理。
- 配好缓存、重试、并发、超时和日志,避免一次页面波动拖垮整条数据流水线。
总结
crawl4ai 适合把网页从“浏览器里能看”推进到“系统里能用”。如果你的下游是 RAG、Agent 或内容自动化,它能少掉很多抓取、清洗和结构化抽取的胶水代码;但真正上线时,边界、授权和样本校验依然要认真做。