Crawl4AI 0.4.0 版本解析:PruningContentFilter 无监督内容过滤与 User-Agent 生成器实战
【免费下载链接】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
Crawl4AI 0.4.0(2024 年 12 月 1 日发布)是内容抽取能力的一次重要升级:它引入了基于 HTML 节点评分的无监督过滤策略PruningContentFilter,增强了面向查询的有监督过滤器BM25ContentFilter,并新增了可定制、可随机的 User-Agent 生成器。读完本文,你可以掌握两种内容过滤器的适用场景与参数配置、PruningContentFilter的评分剪枝原理,以及通过BrowserConfig的user_agent_mode实现请求指纹多样化的方法。
一、0.4.0 版本总览
根据 0.4.0 发布说明,该版本的关键亮点包括:
- PruningContentFilter:新增的无监督内容过滤策略,基于文本密度、链接密度等指标对 HTML 节点进行评分和剪枝,自动保留页面中最有价值的内容部分;
- User-Agent 生成器:新增 User-Agent 生成工具,解决兼容性问题,支持可定制的 UA 字符串,默认对每个请求随机化 UA 以增加多样性;
- 线程安全增强:为多线程/多协程并行处理环境增加了更完善的线程锁机制,保证多任务运行的一致性和稳定性;
- 过滤策略矩阵完善:用户可同时使用
PruningContentFilter(无监督抽取)和BM25ContentFilter(基于用户查询的有监督过滤),且BM25ContentFilter增强了处理页面标题、meta 标签和描述的能力,使文本块分类与聚类更有效; - 文档与测试同步更新:示例和教程更新以推广两种过滤器的配合使用,并新增
PruningContentFilter单元测试、扩展BM25ContentFilter的边界用例覆盖。
发布说明同时指出:PruningContentFilter在当时仍处于实验性开发阶段,欢迎用户反馈以继续完善。
需要说明的是,当前仓库版本为 0.9.0(见 crawl4ai/version.py),以下所有实现细节均基于当前代码库核实,0.4.0 引入的核心类在当前版本中依然可用。
二、内容过滤架构:RelevantContentFilter 基类
在 crawl4ai/content_filter_strategy.py 中,三种过滤策略共享同一个抽象基类RelevantContentFilter,它定义了页面过滤的公共基础:
保留与排除标签集(基类__init__,第 50-111 行):
included_tags:article、main、section、div、列表结构(ul/ol/li)、文本内容(p/span/blockquote/pre/code)、标题(h1-h6)、表格、语义元素(figure、details)等;excluded_tags:nav、footer、header、aside、script、style、form、iframe、noscript——这些标签会被直接剔除;negative_patterns:正则nav|footer|header|sidebar|ads|comment|promo|advert|social|share,用于识别 class 或 id 中带有这些语义的容器(如class="sidebar"、id="comments")。
查询回退机制extract_page_query(第 125-159 行):如果调用方未提供user_query,过滤器会按顺序回退——先取<title>,再取第一个<h1>文本,然后读取meta[name=keywords]与meta[name=description];若仍为空,则取正文中第一个超过 50 字符的<p>段落的前 150 字符。这个机制正是 0.4.0 中"增强 BM25 对页面标题、meta 标签和描述的处理"的具体落地。
文本块提取extract_text_chunks(第 161-271 行):采用基于deque的手写深度优先遍历,将行内标签(a、span、em等)视为不切断文本流的节点,遇到块级元素边界时把累积文本刷成一个(序号, 文本, header/content 类型, 元素)四元组,并可按min_word_threshold过滤过短的块。
这套"提取 → 评分 → 过滤 → 清洗"的流水线是理解两个核心过滤器的共同前提。
三、PruningContentFilter:无监督评分剪枝
PruningContentFilter(crawl4ai/content_filter_strategy.py)不需要任何用户查询,适合"我只想要正文,不需要搜索意图"的场景。其构造函数参数如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
user_query | str | None | 保留参数(该过滤器不依赖查询) |
min_word_threshold | int | None | 节点文本单词数低于该值时强制剪除(构造内提前返回分数 -1.0) |
threshold_type | str | "fixed" | 阈值类型:fixed固定阈值 /dynamic动态阈值 |
threshold | float | 0.48 | 固定阈值或动态阈值的基础值 |
3.1 五维复合评分
_compute_composite_score(第 737-772 行)对每个节点计算加权复合分数,五个指标及权重(metric_weights)为:
- text_density(权重 0.4):
文本长度 / 标签总长度(encode_contents得到的 HTML 串长度)。文本占比越高,说明节点内容密度越大; - link_density(权重 0.2):
1 - 直接子级 <a> 链接文本长度 / 节点文本长度。链接占比越低的节点得分越高,专门压制导航、链接堆砌区; - tag_weight(权重 0.2):标签语义权重表
tag_weights(第 617-632 行),如article: 1.5、h1: 1.2、p: 1.0、div: 0.5、span: 0.3; - class_id_weight(权重 0.1):
_compute_class_id_weight(第 774-785 行)检查节点的 class 和 id,若命中negative_patterns(sidebar、ads、comments 等)则扣 0.5 分(最低截断为 0); - text_length(权重 0.1):
log(文本长度 + 1),对长文做对数平滑,避免超长节点主导评分。
最终分数为各启用指标的加权和除以权重之和,归一化到与阈值可直接比较的量纲。若设置了min_word_threshold且节点词数不足,直接返回 -1.0 保证被移除。
3.2 fixed 与 dynamic 两种阈值模式
_prune_tree(第 685-735 行)从<body>开始递归剪枝:
- fixed 模式:
score < threshold(默认 0.48)即node.decompose()整个子树移除; - dynamic 模式:在基础阈值上做三次微调——语义重要标签(
tag_importance表中article: 1.5、h1: 1.4等,权重 > 1)阈值乘以 0.8(更宽容);文本占比text_ratio > 0.4再乘以 0.9;链接占比link_ratio > 0.6则乘以 1.2(更严格)。也就是说,重要标签更难被剪、纯文本节点更被保留、链接密集区更容易被剪。
节点不达标时整棵子树被decompose(),达标则递归处理子节点;最终返回<body>下所有含文本的顶层子元素 HTML 串列表。过滤前先执行_remove_comments和_remove_unwanted_tags(按excluded_tags拆解nav/footer/script等)。
3.3 单元测试覆盖
tests/async/test_content_filter_prune.py 对该过滤器做了系统性验证:
test_basic_pruning:min_word_threshold=5下,高质量长段落保留,class="sidebar"与class="social-share"的内容被剪除;test_min_word_threshold:短摘要与短评论被过滤,长段落保留;test_threshold_types:fixed 与 dynamic 模式对同一 HTML 产出不同结果集;test_link_density_impact:dynamic模式下,含 4 个链接的<div class="links">区块被压制,链接密集区被剪枝;test_threshold_levels:参数化验证阈值单调性——阈值 0.3 时最多保留 4 个块,0.48(默认)时 2 个,0.7 时 1 个,可用于按页面噪声程度调参;- 还包含空输入、畸形 HTML(未闭合标签)、性能(< 0.1 秒)与输出一致性(多次运行结果相同)用例。
四、BM25ContentFilter:查询驱动的有监督过滤
BM25ContentFilter(crawl4ai/content_filter_strategy.py)与无监督的剪枝策略互补:它把"用户查询"当作相关性信号。构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
user_query | str | None | 查询词;缺省时走extract_page_query回退链(title → h1 → meta → 首段) |
bm25_threshold | float | 1.0 | 加权后 BM25 分数低于该值的块被丢弃 |
language | str | "english" | snowballstemmer 词干提取语言 |
use_stemming | bool | True | 是否对语料与查询做词干提取 |
核心流程(filter_content,第 440-538 行):
- 用 lxml 解析 HTML,缺失
<body>时自动包裹; - 提取查询(
user_query优先,否则页面元数据回退); - 复用基类的
extract_text_chunks得到有序文本块; - 分词 → 词干提取 →
clean_tokens去停用词,构建rank_bm25.BM25Okapi并计算每个块的分数; - 标签权重调整(第 425-437 行
priority_tags):h1: 5.0、title: 4.0、h2: 4.0、h3: 3.0、strong/blockquote/code: 2.0、b/em/pre/th: 1.5,即adjusted_score = score * tag_weight,让标题和强调内容在相关性接近时优先保留; - 按
bm25_threshold过滤、按原文档顺序排序(保持阅读顺序而非分数序)、按文本去重,最后用基类clean_element输出清洗后的 HTML 块。
0.4.0 中对页面标题、meta 标签、描述的增强正体现在第 125-159 行的extract_page_query回退链上:即使用户不传查询,过滤器也能用页面自身的语义信号完成"自我相关性"判断。
选型建议(与发布说明及 README.md 的示例一致):
- 无明确查询意图、只想拿到"干净正文"用于喂给 LLM →
PruningContentFilter(更快、零依赖查询); - 有明确信息需求(如"这个产品支持哪些支付方式")→
BM25ContentFilter(user_query=...)或进一步使用LLMContentFilter。
五、User-Agent 生成器
0.4.0 新增的 User-Agent 生成器实现于 crawl4ai/user_agent_generator.py,提供三类生成器:
5.1 ValidUAGenerator:基于流行度统计的合法 UA
ValidUAGenerator(第 68-87 行)基于fake_useragent库生成真实出现过的 UA 字符串,generate方法支持约束参数:
from crawl4ai.user_agent_generator import ValidUAGenerator gen = ValidUAGenerator() # 默认:Chrome/Firefox/Edge + Windows/Mac OS X + 桌面端,随机取一个 ua = gen.generate() # 定制:只取 Firefox,最低版本 110 ua = gen.generate(browsers=["Firefox"], os=["Windows"], min_version=110.0)参数还包括platforms(如["desktop"])与fallback(拉取数据失败时返回的兜底 UA,默认为一个 Linux Chrome 116 的 UA)。
5.2 OnlineUAGenerator:实时抓取最常见 UA
OnlineUAGenerator(第 89-139 行)在初始化时请求公开的用户代理统计站点,解析 JSON 得到带真实占比(pct)的 UA 列表,随后可按pct_threshold(占比阈值)、browsers、os、platforms过滤并返回首条记录({'ua': ..., 'pct': ...})。适合需要"与真实流量分布一致"的场景。注意该生成器依赖外部网络可用。
5.3 UserAgentGenerator:组件化拼装与 Client Hints
UserAgentGenerator(第 143-414 行)把 UA 拆成可约束的组件:桌面/移动平台字符串(Windows 10 64/32 位、macOS、Linux、Android 品牌机型、iOS 等)、浏览器组合栈(1-3 段浏览器/渲染引擎标识)、以及 Chrome/Edge/Safari/Firefox 的版本列表。generate方法接受:
from crawl4ai.user_agent_generator import UserAgentGenerator gen = UserAgentGenerator() ua = gen.generate(device_type="desktop", os_type="windows", browser_type="chrome", num_browsers=3) print(ua) # Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 # (KHTML, like Gecko) Chrome/118.0.5993.117 ...其中device_type("desktop"/"mobile")、os_type("windows"、"macos"、"linux"、"android"、"ios")、device_brand(如"iphone"、"pixel")、browser_type("chrome"/"edge"/"safari"/"firefox")、num_browsers(1-3,控制浏览器标识段数)。
generate_with_client_hints(第 344-348 行)会额外解析 UA 并生成与之匹配的Sec-CH-UA请求头值(如"Chromium";v="118", "Google Chrome";v="118", "Not_A Brand";v="8"),使 UA 与 Client Hints 保持一致,这对通过现代浏览指纹一致性检查尤为关键。
5.4 与 BrowserConfig 集成:每请求随机 UA
框架层在 crawl4ai/async_configs.py 中为BrowserConfig暴露了user_agent_mode与user_agent_generator_config两个参数(第 755-757 行的文档说明):user_agent_mode="random"时启用随机化,user_agent_generator_config用于透传给生成器的约束。默认行为即发布说明所述——"每次请求随机化 UA,增加多样性,同时允许用户定制"。实际调用链见 crawl4ai/async_crawler_strategy.py:
# 在每次运行中:若用户未显式传 user_agent, # 且 config.magic 或 user_agent_mode == "random" 时 user_agent = ValidUAGenerator().generate( **(config.user_agent_generator_config or {}) )命令行侧同样支持该能力,crawl4ai/cli.py 的示例即:
crwl https://example.com -b "headless=true,viewport_width=1280,user_agent_mode=random"六、多线程/并发环境下的锁机制
0.4.0 的另一项改进是"为并行处理环境增加更好的线程锁"。从源码结构看,这一能力体现在多个关键组件的锁设计上,例如 crawl4ai/browser_manager.py:
BrowserManager使用类级懒初始化锁_get_lock(第 596-605 行),并在锁上记录所属事件循环(_lock_loop),当检测到事件循环切换时重建锁,避免跨 loop 复用asyncio.Lock的经典陷阱;- 全局页面锁
_get_global_lock(第 691-698 行)保护页面级共享资源;实例级_contexts_lock、_page_lock、_pending_cleanup_lock(第 736-756 行)分别保护上下文池、页面获取与清理流程; - LRU 上下文淘汰方法
_evict_lru_context_locked(第 1435 行附近)明确标注"必须在持有_contexts_lock时调用",从源码结构看属于典型的锁内临界区约定。
此外 crawl4ai/async_database.py 中pool_lock/init_lock保护连接池获取与初始化,crawl4ai/async_webcrawler.py 第 157 行提供了thread_safe选项:self._lock = asyncio.Lock() if thread_safe else None,在共享爬虫实例时串行化arun调用。这些锁共同保证了多协程、多任务并行抓取时状态的一致性与稳定性。
七、实战示例:使用 PruningContentFilter 抽取干净正文
README.md 给出了 0.4.0 推广的两种过滤器用法,可直接运行(适用前提:已安装当前版本 Crawl4AI 并执行过crawl4ai-setup浏览器安装):
import asyncio from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode from crawl4ai.content_filter_strategy import PruningContentFilter, BM25ContentFilter from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator async def main(): browser_config = BrowserConfig( headless=True, verbose=True, user_agent_mode="random", # 0.4.0 起:每请求随机 UA user_agent_generator_config={}, # 可传 browsers/os/min_version 等约束 ) run_config = CrawlerRunConfig( cache_mode=CacheMode.ENABLED, markdown_generator=DefaultMarkdownGenerator( content_filter=PruningContentFilter( threshold=0.48, threshold_type="fixed", min_word_threshold=0, ) ), # 换成查询驱动过滤: # markdown_generator=DefaultMarkdownGenerator( # content_filter=BM25ContentFilter( # user_query="WHEN_WE_FOCUS_BASED_ON_A_USER_QUERY", # bm25_threshold=1.0, # ) # ), ) async with AsyncWebCrawler(config=browser_config) as crawler: result = await crawler.arun( url="https://docs.micronaut.io/4.9.9/guide/", config=run_config, ) print(len(result.markdown.raw_markdown)) print(len(result.markdown.fit_markdown)) # 过滤后的"干净"长度 if __name__ == "__main__": asyncio.run(main())CLI 同样内建了这两种过滤器:crawl4ai/cli.py 支持通过过滤配置文件或--output markdown-fit选择策略——type: bm25时构建BM25ContentFilter(可配query、threshold(默认 1.0)、use_stemming),type: pruning时构建PruningContentFilter(默认threshold=0.48),并在内部为markdown-fit输出默认采用 pruning 配置。
调参经验(源自单测中的阈值梯度验证):页面导航/广告噪声多时提高threshold(如 0.7 只保留最高价值块),正文稀疏或结构复杂时降低(如 0.3);短标签(按钮、时间戳)多时给min_word_threshold设 5~10 的地板值。
八、变更文件清单与实验性声明
按发布说明,0.4.0 的变更落点为:
- crawl4ai/content_filter_strategy.py:加入基于评分的剪枝算法(
PruningContentFilter); - README.md:补充
PruningContentFilter用法(当前仓库对应 README-first.md 中的示例保留同一写法); - tests/async/test_content_filter_prune.py:覆盖多种剪枝场景、阈值梯度、畸形 HTML 与性能断言;
- tests/async/test_content_filter_bm25.py:扩展 BM25 的边界用例(含畸形 HTML 输入)。
发布说明还保留了明确的边界提醒:PruningContentFilter当时处于实验性开发阶段。其默认阈值 0.48 与五维权重表都是启发式常量,面对非常规页面结构(如单列流式布局、大量嵌套span)时可能过度剪枝或保留噪声,建议按上文调参梯度在真实目标页面上验证后再固化配置。
九、小结
0.4.0 为 Crawl4AI 补齐了"无需查询意图的高质量正文抽取"(PruningContentFilter 的密度/链接/标签/语义四重评分)、"查询驱动的精准过滤"(BM25 + 页面元数据回退 + 标签权重)、"请求指纹多样化"(三种 UA 生成器 +user_agent_mode="random"+ Client Hints 一致性)与"并发稳定性"(浏览器管理器多级锁、thread_safe选项)四个能力维度。在当前 0.9.0 代码库中,这些类与参数均保持可用,本文给出的参数表、源码位置与测试路径可直接作为二次开发和调参的索引。
【免费下载链接】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),仅供参考