源码教程:DOM、页面状态与元素读取
浏览器服务把 DOM 变为可供模型阅读的文本,同时保存索引与元素、frame 的对应关系。索引是一次快照的定位线索,不是网站自己的元素 ID。
1. DOM 采集链
DomService.buildExpression 读取 resources/dom/dom_tree/index.js 与 run.js,组合并缓存执行脚本。getFrameState 将脚本分别放到对应 Frame 执行,再把结果解析为 DOMState、DOMElementNode、DOMTextNode 和 FrameSnapshot。
Page 的 frame 列表
→ 每个 Frame 执行采集脚本
→ 可见性、交互性、实时表单属性、文本及定位信息
→ 分配不重叠的索引范围
→ Java DOM 模型和 frame 映射
→ 文本渲染、交互元素表、截图与快照存储
可见性、是否可交互以及可否接收点击是不同概念。采集需要考虑布局、视口及扩展范围;点击时仍需检查遮挡。密码属性输出为脱敏标记,但这不意味着截图、URL 和普通输入框都被脱敏。
get_browser_state 默认读取主文档及可处理的同源 frame,includeFrames:true 将跨域 frame 也纳入。各 frame 共享一个全局索引空间,按索引操作时通过映射回到正确 Frame;不能拿子 frame 内的 XPath 在主文档执行。
{"id":"1001","method":"get_browser_state","params":{"includeFrames":true,"highlight":false,"viewportExpansion":800,"includeElements":true,"maxElements":100}}
maxElements 控制响应内联元素数量,不应被当作整个 DOM 的元素总数。视口外找不到控件时,先检查滚动距离和视口扩展,再决定滚动或使用选择器。
2. 页面级读取各有用途
| 命令 | 实现与结果 |
|---|---|
get_browser_state | 生成 DOM 快照,保存索引,返回页面文本、页签和元素信息,并留档 |
list_frames | 列出当前页面 frame 的序号、地址等信息;refresh 控制相关刷新行为 |
get_page_snapshot | 汇总页面快照,可附控制台和请求信息,便于一次获取诊断上下文 |
diff_dom_text | 重新生成快照,与之前的文本按行比较;它会更新快照,不是单纯读取旧缓存 |
get_interactive_map | 返回当前可交互元素映射,适合检查完整定位信息 |
get_form_state | 遍历表单控件,读取实时 value、checked、disabled、readonly、错误提示等;可限制 selector、隐藏项和数量 |
extract_structured_data | 当前实现读取 document.body.innerText,按选项收集链接;query 被保留为结果信息,不在此命令中调用大模型抽取 |
旧的“把网页转 Markdown 再请求大模型”属于另一种设计,不是当前 extractStructuredData 的执行链。不要把命令名中的 structured 当成“已经按 query 生成业务 JSON”。如需特定结构,应在上层明确增加解析或模型步骤。
3. 元素读取共用 read 包装
多数读取方法调用 read(browserId, index, action, resultKey, reader):先找任务与索引,再执行读取函数,最后将结果写到指定 data 字段。下面是当前源码的调用形式:
return read(browserId, index, "get_element_value", "value",
locator -> locator.inputValue());
| 命令 | 底层读取 | 主要结果字段 |
|---|---|---|
get_element_text | Locator.innerText() | text |
get_element_html | Locator.innerHTML(),不是 outerHTML | html |
get_element_value | Locator.inputValue() | value |
get_element_attribute | Locator.getAttribute(name) | value |
is_visible | Locator.isVisible() | visible |
is_enabled | Locator.isEnabled() | enabled |
is_checked | Locator.isChecked() | checked |
get_element_count | 指定 Frame 内按 selector 统计 | 数量结果;无匹配与调用失败要区分 |
get_element_box | 获取元素 bounding box | 坐标与尺寸;无布局信息时不能虚构坐标 |
get_element_listeners | ListenerProbe 尝试协议探测和框架线索 | found、监听信息及检测说明;未知不等于没有监听 |
inputValue 与 HTML attribute 不同:用户修改输入框后,初始 value 属性可能没变。状态读取必须使用适合控件的实时属性。对非表单元素调用 inputValue、对非复选控件调用 isChecked,也可能因类型不匹配失败。
4. 索引生命周期与验证
在页面更新、导航或页签切换后重新取快照。即使新页面也有索引 8,它也不一定是上次的索引 8。实现中的旧快照提示和 frame 映射旨在帮助排障,不提供跨任意页面变更的稳定句柄。
本地 fixture 应包含输入框、复选框、隐藏字段、只读控件和 iframe。修改 property 后验证文本和 get_form_state 显示新值;验证密码脱敏、子 frame 索引不冲突,以及 get_element_html 只返回内部 HTML。另用两段等长文本测试差异检测,避免只比较字符串长度。
注册参数与 Java 入口
以下按 CommandTable 实际读取参数整理。* 表示注册层使用必填读取器;其余字段省略后由服务决定默认行为。带条件的入口仍需满足正文说明,例如上传文件来源、元素定位二选一。外层 id 不重复列出。
| 命令 | params 字段 | Java 入口 |
|---|---|---|
get_browser_state | highlight、viewportExpansion、includeElements、maxElements、includeFrames | getBrowserState |
list_frames | refresh | listFrames |
get_page_snapshot | includeConsole、includeRequests、requestFilter | getPageSnapshot |
diff_dom_text | highlight、viewportExpansion | diffDomText |
get_interactive_map | 无 | getInteractiveMap |
get_form_state | selector、includeHidden、max | getFormState |
extract_structured_data | query、extractLinks | extractStructuredData |
get_element_text | index* | getElementText |
get_element_html | index* | getElementHtml |
get_element_value | index* | getElementValue |
get_element_attribute | index、name | getElementAttribute |
get_element_listeners | index、selector、frame | getElementListeners |
get_element_count | selector*、frame | getElementCount |
get_element_box | index* | getElementBox |
is_visible | index* | isVisible |
is_enabled | index* | isEnabled |
is_checked | index* | isChecked |
