LLM 结构化提取(网页转 JSON)
LLM 结构化提取(网页转 JSON)
概述
LLM 结构化提取是将网页的非结构化 HTML 内容转换为结构化 JSON 数据的技术。传统爬虫依赖 XPath/CSS 选择器提取数据,对页面结构变化敏感且难以处理语义信息。LLM 结构化提取通过自然语言 Schema 定义目标数据结构,利用 LLM 的理解能力从 HTML 中提取匹配数据,对页面结构变化具有鲁棒性。
核心流程
1. HTML 清理
直接将原始 HTML 送入 LLM 会导致:
- Token 浪费:HTML 包含大量与提取无关的标签(
<script>、<style>、导航栏、页脚等) - 上下文窗口溢出:大型页面的 HTML 可能超出 LLM 的上下文限制
- 干扰信息:广告、Cookie 弹窗等噪音影响提取准确性
清理策略:
- 移除
<script>、<style>、<nav>、<footer>等非内容元素 - 提取主内容区域(main content area detection)
- 保留语义结构(标题层级、列表、表格)
2. Schema 定义
使用 Pydantic / Zod / JSON Schema 定义目标数据结构:
# Pydantic 示例class Founder(BaseModel): name: str = Field(description="创始人全名") role: str = Field(description="职位/角色")
class CompanyInfo(BaseModel): founders: list[Founder] = Field(description="创始人列表") pricing_plans: list[str] = Field(description="定价方案")Schema 中的 description 字段至关重要 —— 它是 LLM 理解每个字段语义的关键提示。
3. Prompt 组装
典型的结构化提取 Prompt 结构:
系统提示:你是一个数据提取助手。从给定的 HTML 中提取数据,匹配以下 JSON Schema。如果某个字段不存在,返回 null 而非猜测。
用户提示:HTML 内容:{cleaned_html}
JSON Schema:{schema_definition}4. LLM 调用与输出验证
关键决策点:
- JSON 解析失败:LLM 返回的不是合法 JSON,需要重试
- Schema 校验失败:JSON 合法但不匹配目标 Schema(缺少必填字段、类型不匹配等)
- 重试策略:通常 2-3 次重试,超过则返回错误
常见陷阱与对策
1. LLM 幻觉(Hallucination)
LLM 可能编造 HTML 中不存在的数据。
对策:
- 在系统提示中明确指令:“如果字段不存在,返回 null,不要猜测”
- 使用
strict模式(OpenAI 的 structured outputs 支持) - 对提取结果进行后验证(如检查 URL 是否真实存在)
2. Token 成本过高
大型页面的 HTML 可能消耗大量 Token。
对策:
- HTML 清理减少 60-80% 的 Token 用量
- 先提取精简的 DOM 树(只保留数据相关元素)
- 使用较小的模型处理简单 Schema
3. Schema 设计不当
过于宽泛的 Schema 导致提取结果不可用;过于严格的 Schema 导致频繁校验失败。
对策:
- 每个字段必须有清晰的
description - 使用
Optional/ 可空类型处理不一定存在的字段 - 避免在 Schema 中嵌套过深的结构
4. 多语言页面
页面内容可能混合多种语言,LLM 可能错误翻译或混淆。
对策:
- 在系统提示中指定目标语言
- 使用 LLM 的原生语言处理能力
设计优势
| 优势 | 说明 |
|---|---|
| 语义理解 | 不只是结构匹配,还能理解内容的语义关系 |
| 容错性 | 页面结构变化时,只要语义不变,提取结果仍正确 |
| 零模板 | 不需要为每个目标网站编写专用提取规则 |
| 自适应 | 同一 Schema 可用于不同网站 |
替代方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| XPath/CSS 选择器 | 精确、零成本、可预测 | 页面结构变化即失效,需维护大量规则 |
| 正则表达式 | 灵活、轻量 | 脆弱、难以维护、不适合复杂结构 |
| LLM 结构化提取 | 语义理解、容错、零模板 | 成本高、延迟大、可能幻觉 |
| 混合方案 | 先用规则提取简单字段,LLM 处理复杂语义 | 复杂度增加,但成本和延迟可控 |
补充:对话场景下的 LLM 记忆提取
LLM 结构化提取不仅适用于网页→JSON 场景,也广泛应用于对话→记忆的提取。两者的关键差异:
| 维度 | 网页提取 | 对话提取 |
|---|---|---|
| 输入 | 静态 HTML | 多轮对话消息(role + content) |
| 目标 | 预定义 Schema 的 JSON | 开放式的自然语言事实列表 |
| 去重 | 不需要(每次独立提取) | 需要(跨会话去重、语义去重) |
| 角色分离 | 不需要 | 需要区分用户/助手消息的提取来源 |
对话提取的挑战
- “First Topic Dominance” 问题:LLM 倾向于提取第一个主要话题,忽略后续话题。当对话覆盖多个主题时,需要提示词中明确要求”扫描整段对话的每一个话题”。
- 回显去重:助手消息经常重复用户已说的内容(“我帮你设置了每日检查,时间是 7:30 AM”),需要从助手消息中跳过已覆盖的信息。
- 时间锚定:对话中的相对时间引用(“昨天”、“上周”、“下个月”)必须基于**观察日期(Observation Date)而非当前日期(Current Date)**转换为绝对日期,否则数月后检索时时间信息失效。
- 多说话者场景:当对话中的”assistant”角色实际代表另一个人(如 “Maria: 我刚养了一只猫”),需要提取其个人事实,而非跳过所有 assistant 消息。
ADD-Only 提示词设计原则
ADD-Only 策略下的提取提示词(如 ADDITIVE_EXTRACTION_PROMPT)包含以下关键设计:
- 角色定义:“你是 Memory Extractor——精确、证据绑定的处理器”
- 提取范围:明确从用户消息和助手消息中分别提取什么
- 质量约束:15-80 字、自包含、时间锚定、数值精确
- 完整性约束:禁止泛化具体信息(“Ferrari 488 GTB” ≠ “跑车”)
- 完整性检查清单:“输出前,逐条扫描对话中的每个话题,确认是否已提取”
- Few-Shot 示例:覆盖多话题提取、助手推荐、空结果、去重、实体链接等场景
版本标注
本文档基于通用 LLM 提取模式编写,参考了 OpenAI Function Calling 和 JSON Schema 生成等相关技术文档。