跳转到内容

DOM 提取与无障碍树 — LLM 可读的页面表示

学习目标

  • 理解 Browser Use 的 DOM 处理流程
  • 掌握 EnhancedDOMTreeNode 的设计
  • 了解 LLM 文本表示生成和页面统计

项目实践

DOM 处理流程

CDP DOMSnapshot 解析

通过 DOMSnapshot.captureSnapshot 获取的数据是扁平化的数组格式:

{
documents: [{
domNodes: {
nodeId: [...],
nodeName: [...], // 标签名
nodeValue: [...], // 文本节点值
nodeType: [...], // 节点类型
parentId: [...], // 父节点索引
childNodeIndexes: [...], // 子节点列表
attributes: [...], // 属性列表
// ... 更多字段
},
layout: { ... }, // 布局信息(可见性、尺寸)
accessibility: { ... } // 无障碍信息
}]
}

Browser Use 将其转换为结构化的树:

  1. 遍历 domNodes 数组,为每个节点创建 SimplifiedNode
  2. 根据 parentIdchildNodeIndexes 构建父子关系
  3. 根据 layout 信息标记可见性和位置
  4. 根据 attributes 解析 CSS 类、ID、role 等

EnhancedDOMTreeNode

SimplifiedNode 基础上增强:

class EnhancedDOMTreeNode:
node_id: int
backend_node_id: int
node_type: str
node_name: str
attributes: dict
# 交互相关
is_interactive: bool # 是否可交互
is_actually_scrollable: bool # 是否可滚动
is_visible: bool # 是否可见
is_shadow_host: bool # 是否是 Shadow DOM 宿主
absolute_position: DOMRect # 绝对位置(像素坐标)
# 树关系
parent_node: Optional[EnhancedDOMTreeNode]
children_nodes: list[EnhancedDOMTreeNode]
# CDP 引用
session_id: SessionID
target_id: TargetID
frame_id: str

交互元素判定

  • 标签名在白名单中(a, button, input, select, textarea, 等)
  • onclickrole 属性
  • 可滚动容器

LLM 文本表示生成

dom_state.llm_representation() 方法生成 LLM 可读的文本:

[1] <a>Google Search</a>
[2] <button>Sign in</button>
[3] <input type="text" placeholder="Search...">
...

关键特性

  • 每个交互元素分配唯一索引
  • 层级缩进表示 DOM 结构
  • 包含元素的关键属性(placeholder、value、href)
  • 自动截断超出 max_clickable_elements_length 的内容
  • [Start of page] / [End of page] 标记边界

Paint Order Filtering

按视觉渲染顺序而非 DOM 顺序排序元素:

  • 通过 layout.paint_order 字段获取元素的渲染优先级
  • 过滤隐藏的元素(display: none, visibility: hidden
  • 结果更贴近用户视角,而非源码结构

Shadow DOM 支持

Browser Use 是少数支持 Shadow DOM 的浏览器自动化框架:

  • 通过 CDP DOMSnapshot 获取 Shadow Root 信息
  • shadow_root_type 区分 open / closed
  • 递归处理嵌套 Shadow DOM
  • 在页面统计中分别计数

页面统计提取

AgentMessagePrompt._extract_page_statistics() 遍历 DOM 树提取:

stats = {
'links': 0, # <a> 数量
'iframes': 0, # iframe 数量
'images': 0, # <img> 数量
'interactive_elements': 0, # 可交互元素
'total_elements': 0, # 总元素
'text_chars': 0, # 文本字符数
'shadow_open': 0, # open shadow DOM
'shadow_closed': 0, # closed shadow DOM
'scroll_containers': 0, # 可滚动容器
}

用于生成 <page_stats> 注入到 Agent 上下文中。


问题与规避

大页面性能

问题:复杂页面(如电商首页)可能有数千个 DOM 节点。

对策

  • CDP DOMSnapshot 是批量获取,性能优于逐个节点遍历
  • max_clickable_elements_length 限制输出大小
  • 只显示交互元素,忽略纯装饰性 DOM

iframe 递归深度

问题:嵌套 iframe 可能导致无限递归。

对策

  • max_iframesmax_iframe_depth 限制递归深度
  • 跨域 iframe 通过 CDP 可能需要特殊权限

动态 DOM 与快照的时序问题

问题:SPA 页面可能在快照获取过程中动态变化。

对策

  • 快照是原子操作,获取的是某一时刻的完整 DOM 状态
  • 每次 step 重新获取快照,不依赖旧数据
  • wait_for_network_idle_page_load_time 等待页面加载完成后再获取快照

设计取舍

文本表示 vs 视觉表示

方案优势代价
文本表示(当前)Token 效率高、所有模型通用丢失视觉布局信息
截图 + 坐标保留视觉信息需要视觉模型、Token 消耗大

Browser Use 两者兼用:文本表示作为主要交互方式,截图作为辅助视觉输入。


参考来源