Crawl4AI LLMExtractionStrategy 实战:用任意 LLM 从网页提取结构化 JSON
【免费下载链接】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
当你需要从网页中提取复杂、非结构化的信息——例如自然语言描述、分类标签、摘要或知识图谱——简单的 CSS/XPath 选择器往往无能为力。Crawl4AI 为此提供了 LLM 提取策略LLMExtractionStrategy:通过 LiteLLM 抽象层接入任意大语言模型(OpenAI、Ollama、Claude、Gemini 等),自动分块应对 token 限制,并支持基于 Pydantic 的 schema 约束提取。读完本文,你将掌握 LLM 提取的完整配置参数、分块机制的源码级原理、token 用量监控方法,以及可直接运行的完整示例(含知识图谱构建)。
需要先明确一点:LLM 提取比基于 schema 的确定式方案更慢、成本更高。如果你的页面结构规整,应优先使用JsonCssExtractionStrategy或JsonXPathExtractionStrategy(参见 无 LLM 提取策略);只有当数据需要 AI 理解、解释或重组时,本文的方案才是合适选择。
1. 为什么选择 LLM 提取
- 复杂推理:站点数据非结构化、分散在多处、充满自然语言上下文时,规则匹配无法覆盖;
- 语义提取:摘要、知识图谱、关系型数据等需要"理解"而非"匹配"的任务;
- 灵活性:可以通过
instruction参数向模型下达任意转换、分类指令,无需为每种新任务改写选择器。
2. 供应商无关:通过 LLMConfig 接入任意模型
Crawl4AI 使用 LiteLLM 作为底层调用抽象,通过"provider 字符串"(如"openai/gpt-4o"、"ollama/llama3"、"gemini/gemini-2.0-flash")来标识模型。LiteLLM 支持的任何模型都可以直接使用。核心配置对象是LLMConfig(定义于 async_configs.py),可以方便地创建多组配置、实验不同模型以找到最优解(更多参数说明见 LLMConfig 参数参考):
llm_config = LLMConfig(provider="openai/gpt-4o-mini", api_token=os.getenv("OPENAI_API_KEY"))LLMConfig的完整参数(源码确认):
| 参数 | 类型 | 说明 |
|---|---|---|
provider | str | <provider>/<model_name>标识,默认"openai/gpt-4o"(DEFAULT_PROVIDER,见 config.py) |
api_token | str | API 密钥;支持"env:VAR_NAME"前缀从环境变量解析;本地模型(如 Ollama)可省略 |
base_url | str | 自定义 API 端点(如自建 OpenAI 兼容网关) |
temperature/top_p/max_tokens | — | 采样与长度控制(也可通过策略的extra_args传入) |
frequency_penalty/presence_penalty/stop/n | — | 其余 OpenAI 风格采样参数 |
backoff_base_delay | int | 失败重试基础等待秒数,默认 2 |
backoff_max_attempts | int | 最大重试次数,默认 3 |
backoff_exponential_factor | int | 指数退避因子,默认 2 |
两个值得注意的源码细节:
- API key 自动解析:如果你不显式传
api_token,LLMConfig会按 provider 前缀查找PROVIDER_MODELS_PREFIXES表(config.py 中维护了openai、groq、anthropic、gemini、deepseek、bedrock等前缀与对应环境变量的映射)。例如provider="openai/gpt-4o"会自动读取OPENAI_API_KEY;ollama/*则被标记为no-token-needed。 - 重试与退避:每次 LLM 调用都通过
perform_completion_with_backoff(同步)/aperform_completion_with_backoff(异步,见 utils.py)发起,网络抖动或限流会按backoff_base_delay * backoff_exponential_factor^n的策略自动重试,最多backoff_max_attempts次。
这意味着你不会被锁定在单一 LLM 供应商:更换provider即可切换模型、对比效果与成本。
3. LLM 提取的完整工作流程
3.1 端到端流程
LLMExtractionStrategy的提取流程(实现位于 extraction_strategy.py 的LLMExtractionStrategy类):
- 内容选择与分块:爬虫主流程根据
input_format选取内容,先用 chunking 策略切成 sections;策略内部再按chunk_token_threshold与overlap合并/切分为 LLM 分块; - 提示词构造:为每个分块填充模板变量
{URL}、{HTML}、{REQUEST}(instruction)、{SCHEMA},生成最终 prompt; - LLM 推理:异步路径用
asyncio.gather对全部分块并行请求(arun);同步路径用ThreadPoolExecutor(max_workers=4)并行(run),但对groq/前缀的 provider 采用串行 + 每块 0.5 秒间隔,以规避速率限制; - 结果解析与合并:从
<blocks>标签(或force_json_response模式下的纯 JSON)中解析出 block 列表,追加错误标记后合并为最终 JSON。
3.2 提示词模板的四种模式
aextract/extract方法会根据参数组合选择 prompts.py 中的不同模板:
| 条件 | 使用的模板 | 行为 |
|---|---|---|
| 无 instruction、无 schema | PROMPT_EXTRACT_BLOCKS | 让模型将页面拆成语义块并打标签(block模式) |
| 有 instruction、无 schema | PROMPT_EXTRACT_BLOCKS_WITH_INSTRUCTION | 按用户指令拆分语义块 |
extraction_type="schema"且有schema | PROMPT_EXTRACT_SCHEMA_WITH_INSTRUCTION | 将 JSON Schema 嵌入 prompt,要求模型按 schema 提取,并附带"质量反思 + 1~5 分自评分"的元认知约束,显著降低格式错误率 |
extraction_type="schema"但未提供schema | PROMPT_EXTRACT_INFERRED_SCHEMA | 让模型自行推断最合理的 JSON 结构(日期用 ISO 格式、价格不带货币符号等规范写在模板里) |
3.3extraction_type参数
"schema"(默认值):模型返回符合你 Pydantic schema 的 JSON。你传入schema=YourPydanticModel.model_json_schema()即可;"block":模型返回自由文本块或小的 JSON 结构,由库收集。
注意一个源码细节:__init__中有一行if schema: self.extract_type = "schema"——只要提供了 schema,即使你显式写了extraction_type="block",也会被强制切回"schema"模式。因此对结构化数据,推荐始终使用"schema"并传入model_json_schema()。
3.4 调用链与input_format的选择
在爬虫主流程 async_webcrawler.py 中,提取发生在 markdown 生成之后。主流程从config.extraction_strategy.input_format读取格式,并在以下候选内容中取值:
content = { "markdown": markdown_result.raw_markdown, "html": html, "fit_html": fit_html, "cleaned_html": cleaned_html, "fit_markdown": markdown_result.fit_markdown, }.get(content_format, markdown_result.raw_markdown)"markdown"(默认):markdown_generator输出的原始 markdown;"fit_markdown":内容过滤器(如PruningContentFilter)产出的精简版 markdown。若过滤器没有产出(页面未触发过滤),源码会自动回退到"markdown"而不是报错。如果你信任过滤器,这可以大幅减少喂给 LLM 的 token 数;"html":清洗后的 HTML 交给模型。若你的 instruction 依赖 HTML 标签结构,选它。
另一个细节:当content_format是 HTML 类格式时,主流程使用IdentityChunking()(即整页作为单段),由策略内部的merge_chunks统一负责切块;而 markdown 类格式会先走config.chunking_strategy预切分,再进入策略内的合并切块。
4. 关键参数详解
以下参数在LLMExtractionStrategy(...)中设置,然后把策略挂到CrawlerRunConfig(..., extraction_strategy=...)上。默认值均以源码(config.py 常量与 extraction_strategy.py 构造签名)为准:
llm_config(LLMConfig):模型与密钥配置,如LLMConfig(provider="openai/gpt-4", api_token=...)。若不传,默认回落到openai/gpt-4o+ 环境变量OPENAI_API_KEY;schema(dict):描述目标字段的 JSON Schema,通常由YourModel.model_json_schema()生成;extraction_type(str):"schema"或"block",默认"schema";instruction(str):写给模型的提取指令,如 "Extract these fields as a JSON array";chunk_token_threshold(int):每个 LLM 分块的目标 token 上限,默认2**11=2048;overlap_rate(float):相邻分块的重叠比例,默认0.1,即每块尾部约 10% 的内容会复制到下一块开头,防止目标信息被切在边界上;word_token_rate(float):词→token 换算系数,默认1.3(源码常量WORD_TOKEN_RATE,注意这与部分早期文档提到的 0.75 不同,以当前源码为准)。merge_chunks用字数 × 该系数估算 token 数;apply_chunking(bool):默认True。设为False时源码会把chunk_token_threshold置为1e9,等效于整页单次请求;input_format(str):见上文 3.4,"markdown"(默认)/"fit_markdown"/"html"(及fit_html/cleaned_html);force_json_response(bool):默认False。设为True时通过 LiteLLM 请求结构化输出并直接json.loads(剥离 markdown 围栏后),跳过<blocks>标签解析;extra_args(dict,经**kwargs传入):透传给 LLM 的额外参数,如temperature、max_tokens、top_p;verbose(bool):打印每次 LLM 调用的分块索引与解析块数日志;show_usage()(方法):打印 token 用量汇总与逐请求历史。
弃用警告:
provider、api_token、base_url、api_base这四个旧参数已被标记为弃用(源码中_UNWANTED_PROPS会在你试图设置它们时抛出AttributeError并提示改用llm_config=LLMConfig(...)),请不要再在新代码中使用。
完整参数示例:
extraction_strategy = LLMExtractionStrategy( llm_config=LLMConfig(provider="openai/gpt-4", api_token="YOUR_OPENAI_KEY"), schema=MyModel.model_json_schema(), extraction_type="schema", instruction="Extract a list of items from the text with 'name' and 'price' fields.", chunk_token_threshold=1200, overlap_rate=0.1, apply_chunking=True, input_format="html", extra_args={"temperature": 0.1, "max_tokens": 1000}, verbose=True )5. 完整示例:把策略挂进 CrawlerRunConfig
重要:在 Crawl4AI 中,所有策略定义都应放进CrawlerRunConfig,而不是直接作为arun()的参数。完整可运行示例:
import os import asyncio import json from pydantic import BaseModel, Field from typing import List from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode, LLMConfig from crawl4ai import LLMExtractionStrategy class Product(BaseModel): name: str price: str async def main(): # 1. 定义 LLM 提取策略 llm_strategy = LLMExtractionStrategy( llm_config=LLMConfig(provider="openai/gpt-4o-mini", api_token=os.getenv('OPENAI_API_KEY')), schema=Product.model_json_schema(), # 由 Pydantic 模型生成 JSON Schema extraction_type="schema", instruction="Extract all product objects with 'name' and 'price' from the content.", chunk_token_threshold=1000, overlap_rate=0.0, apply_chunking=True, input_format="markdown", # 或 "html"、"fit_markdown" extra_args={"temperature": 0.0, "max_tokens": 800} ) # 2. 构建爬虫运行配置 crawl_config = CrawlerRunConfig( extraction_strategy=llm_strategy, cache_mode=CacheMode.BYPASS ) # 3. 按需创建浏览器配置 browser_cfg = BrowserConfig(headless=True) async with AsyncWebCrawler(config=browser_cfg) as crawler: # 4. 爬取单个页面 result = await crawler.arun( url="https://example.com/products", config=crawl_config ) if result.success: # 5. 提取结果是 JSON 字符串 data = json.loads(result.extracted_content) print("Extracted items:", data) # 6. 打印 token 用量 llm_strategy.show_usage() else: print("Error:", result.error_message) if __name__ == "__main__": asyncio.run(main())执行链路为:crawler.arun()→ 主流程按input_format选内容 →strategy.arun(url, sections)→ 每块并行调用 LLM → block 列表json.dumps后写入result.extracted_content。
6. 分块机制源码解析
6.1chunk_token_threshold如何生效
策略内部的_merge方法调用 utils.py 中的merge_chunks,将 sections 合并为不超过target_size的分块。其核心逻辑:
- 对每个 section 按空白分词(
str.split),token 数估算为词数 × word_token_rate(默认 1.3); - 预分配
ceil(total_tokens / target_size)个分桶,将词依次装入当前桶; - 当当前桶达到
target_size且overlap > 0时,把上一桶末尾的overlap个词复制到新桶开头——这就是overlap_rate的实现方式(overlap = int(chunk_token_threshold × overlap_rate))。
所以调小chunk_token_threshold会得到更多分块、更高并行度与更大重叠开销;估算偏保守(1 词 ≈ 1.3 token)意味着实际分块往往略短于上下文窗口,这是有意为之的安全边际。
6.2overlap_rate的作用
overlap_rate=0.1表示每个后续分块包含前一分块尾部约 10% 的文本。当目标信息可能横跨分块边界时(例如一条产品描述被切开),重叠能避免漏提取;代价是重复 token 的额外成本。如果你的页面结构块与分块边界天然对齐,可设为0.0。
6.3 并行与串行
从源码看,arun(异步)用asyncio.gather并发处理所有分块;同步run用 4 线程的线程池并行,唯独groq/*provider 被特殊处理为串行 + 500ms 间隔(规避其速率限制)。分块并行确实能显著缩短大页面总耗时,但要注意各供应商的并发/速率限制——重试退避(backoff_*参数)会兜底,但高并发下仍可能触顶。
6.4 响应解析与错误容错
解析逻辑(extract/aextract)分三条路径:
force_json_response=True:json.loads(_strip_markdown_fences(content))。若结果是 dict:单键且值为 list 时解包为该 list(如{"news": [...]}→[...]);否则包装成[dict];- 默认路径:
extract_xml_data(["blocks"], content)从<blocks>...</blocks>标签中提取 JSON 数组——这也是为什么 schema 模板要求模型把结果包在<blocks>标签里; - 兜底:任一步解析失败时,用
split_and_parse_json_objects做尽力解析,无法解析的残留文本会被追加为一个带error: True标记的块,而不是抛出异常中断整个爬取。
因此在CrawlResult.extracted_content中偶尔看到{"error": true, "tags": ["error"], ...}条目,属于正常的部分失败信号,生产代码应过滤这类块。
7. Token 用量监控:show_usage()
每次 LLM 调用返回后,策略都会记录一条TokenUsage(completion_tokens、prompt_tokens、total_tokens及明细),追加到self.usages并累加进self.total_usage。show_usage()会打印如下报告(Completion/Prompt/Total 三项合计 + 逐请求历史表):
llm_strategy = LLMExtractionStrategy(...) # ... 爬取完成后 ... llm_strategy.show_usage()如果你的模型供应商不返回 usage 字段,这些数值可能部分缺失或为零。用量数据可用于成本核算与瓶颈定位:若prompt_tokens远大于completion_tokens,说明分块过大或input_format选了冗余格式,应调低chunk_token_threshold或改用fit_markdown。
提示:
JsonCssExtractionStrategy.generate_schema()也支持通过可选usage参数跟踪 token 用量,参见 无 LLM 策略文档。
8. 实战示例:构建知识图谱
下面展示用嵌套 Pydantic schema+ LLM 提取从新闻页面构建知识图谱的完整片段。注意instruction如何引导模型解析实体与关系:
import os import json import asyncio from typing import List from pydantic import BaseModel, Field from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode, LLMConfig from crawl4ai import LLMExtractionStrategy class Entity(BaseModel): name: str description: str class Relationship(BaseModel): entity1: Entity entity2: Entity description: str relation_type: str class KnowledgeGraph(BaseModel): entities: List[Entity] relationships: List[Relationship] async def main(): # LLM 提取策略 llm_strat = LLMExtractionStrategy( llm_config=LLMConfig(provider="openai/gpt-4", api_token=os.getenv('OPENAI_API_KEY')), schema=KnowledgeGraph.model_json_schema(), extraction_type="schema", instruction="Extract entities and relationships from the content. Return valid JSON.", chunk_token_threshold=1400, apply_chunking=True, input_format="html", extra_args={"temperature": 0.1, "max_tokens": 1500} ) crawl_config = CrawlerRunConfig( extraction_strategy=llm_strat, cache_mode=CacheMode.BYPASS ) async with AsyncWebCrawler(config=BrowserConfig(headless=True)) as crawler: # 示例页面 url = "https://www.nbcnews.com/business" result = await crawler.arun(url=url, config=crawl_config) print("--- LLM RAW RESPONSE ---") print(result.extracted_content) print("--- END LLM RAW RESPONSE ---") if result.success: with open("kb_result.json", "w", encoding="utf-8") as f: f.write(result.extracted_content) llm_strat.show_usage() else: print("Crawl failed:", result.error_message) if __name__ == "__main__": asyncio.run(main())关键观察:
extraction_type="schema"保证输出是符合KnowledgeGraph的 JSON;input_format="html"意味着模型看到的是 HTML(关系型信息往往藏在链接结构中);instruction引导模型输出结构化知识图谱;- 嵌套模型(
Relationship引用Entity)通过model_json_schema()自动展开为完整 JSON Schema。
9. 最佳实践与注意事项
- 成本与延迟:LLM 调用可能慢且贵。如果只需要部分数据,考虑分块或缩小覆盖范围;
- 模型上下文限制:页面 + instruction 超出上下文窗口时,
apply_chunking=True是必需的,并应根据模型窗口调整chunk_token_threshold; - 指令工程:精心编写的
instruction能显著提升输出可靠性。schema 模板自带"质量反思 + 自评分"约束,但你的指令越具体(字段语义、取值格式、空值处理),结果越稳定; - Schema 严格性:
"schema"模式会把模型输出解析为 JSON。模型返回非法 JSON 时,解析器会尽力兜底,未解析部分以error块形式出现——不要假设extracted_content总能json.loads出干净结构; - 并行与速率限制:异步路径全分块并发,注意供应商限流。
groq/*已被源码特殊处理为串行,其他供应商如遇限流可调整backoff_*参数; - 输出后校验:LLM 可能遗漏字段或夹带多余文本。建议用 Pydantic 模型对每个 block 做二次校验(
Model.model_validate),并妥善处理解析错误。
10. 总结与后续步骤
Crawl4AI 的LLM 提取是供应商无关的:通过 LiteLLM 可从数百个模型中挑选,非常适合语义复杂的任务(摘要、分类、知识图谱)。代价是更慢、更贵。记住四个要点:
- 把 LLM 策略放进
CrawlerRunConfig; - 用
input_format决定 LLM 看到 markdown、HTML 还是精简 markdown; - 调
chunk_token_threshold、overlap_rate、apply_chunking处理大内容; - 用
show_usage()监控 token 消耗。
后续方向:
- 实验不同供应商:切换
provider("ollama/llama3"、"openai/gpt-4o"等)对比速度、准确率与成本;用extra_args微调temperature、top_p、max_tokens; - 性能调优:大页面场景下调节分块参数优化吞吐;用
show_usage()定位 token 瓶颈; - 输出校验:
extraction_type="schema"时用 Pydantic 模型做最终校验,优雅处理偶发的畸形 JSON; - 组合 Hooks 与自动化:将 LLM 提取与 hooks 机制 结合做复杂前后处理,构建"爬取 → 过滤 → LLM 提取 → 存储/索引"的多步流水线。
如果你的站点数据规整、重复性强,先用JsonCssExtractionStrategy追求速度与确定性;当你需要AI 驱动的理解与重组时,LLMExtractionStrategy提供了灵活的多供应商结构化 JSON 提取能力。
【免费下载链接】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),仅供参考