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 将其转换为结构化的树:
- 遍历
domNodes数组,为每个节点创建SimplifiedNode - 根据
parentId和childNodeIndexes构建父子关系 - 根据
layout信息标记可见性和位置 - 根据
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, 等) - 有
onclick或role属性 - 可滚动容器
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_iframes和max_iframe_depth限制递归深度- 跨域 iframe 通过 CDP 可能需要特殊权限
动态 DOM 与快照的时序问题
问题:SPA 页面可能在快照获取过程中动态变化。
对策:
- 快照是原子操作,获取的是某一时刻的完整 DOM 状态
- 每次 step 重新获取快照,不依赖旧数据
wait_for_network_idle_page_load_time等待页面加载完成后再获取快照
设计取舍
文本表示 vs 视觉表示
| 方案 | 优势 | 代价 |
|---|---|---|
| 文本表示(当前) | Token 效率高、所有模型通用 | 丢失视觉布局信息 |
| 截图 + 坐标 | 保留视觉信息 | 需要视觉模型、Token 消耗大 |
Browser Use 两者兼用:文本表示作为主要交互方式,截图作为辅助视觉输入。