Browser Agent、DOM 与视觉定位
正在读取学习记录…
要让 Agent 查询订单,第一件事是找到页面里的“查询订单”按钮,再确认结果区出现了“已发货”。Browser Agent 就是在浏览器里反复做观察、选择动作、执行、读回结果的程序。网页的 DOM 是浏览器维护的节点树:按钮、输入框、结果区各是一个节点,节点带着名称、角色和状态。像查办公室座位表一样,先按姓名找人,比记住“第三排第二个”更能适应座位变化;这里姓名对应按钮可见名称,座位对应屏幕坐标。
正常的一次查询是:观察到一个名称为“查询订单”的 button,按钮可用,点击后读到订单 A17 已发货。视觉定位适合画布、远程桌面等无法取得语义节点的界面;DOM 定位适合能提供角色和名称的普通网页。两种方法最后都要读回新状态,点击返回成功不等于订单已经查到。
从输入到可核对结果
我们的材料是一棵人工简化的 XML 受控页面节点树,点击事件由 Python 函数更新 output 节点。代码让定位与状态读回真实发生,便于看清定位前后需要的检查顺序。
夹具明确写出 role="button" 和 disabled="true" 便于程序比较;真实 HTML 的 button 自带隐式按钮角色,可访问名称可能来自 aria-label 或按钮的子节点文字。disabled 是布尔属性,出现就代表禁用,disabled="false" 也会禁用。真实浏览器应使用计算后的角色、可访问名称与可用状态;XML 字符串判断不能直接替代浏览器的定位与可操作性检查。
matches 收集角色为 button 且文字等于 TARGET 的节点。长度为 1 才能继续:0 个代表没找到,2 个代表名称有歧义。随后检查 disabled;最后更新 output 并读回文字。网页实际还会经历网络请求、脚本渲染和按钮动画,真实浏览器工具通常在动作前等待元素可用。Playwright 定位器解释了角色、名称和重新定位;可操作性检查列出可见、稳定、接收事件、可用等条件。
| 页面情况 | 定位依据 | 动作后的核对 |
|---|---|---|
| 按钮顺序变化 | role 与名称 | 读回订单结果 |
| 两个查询按钮 | 进一步限定订单区域 | 确认区域与订单ID |
| 画布没有语义按钮 | 截图中的目标坐标 | 重新截图确认状态 |
| 按钮暂时禁用 | disabled 状态 | 等待可用后再操作 |
保存并运行这两个文件
在 PowerShell 创建练习目录:
New-Item -ItemType Directory -Force l9-browser
cd l9-browsermacOS 终端:
mkdir -p l9-browser
cd l9-browser把下面第一段完整代码保存为 dom_agent.py,第二段保存为 test_dom_agent.py,两份文件放在这个目录。
from xml.etree import ElementTree as ET
TARGET = "查询订单"
PAGE = '<main><button id="query" role="button">查询订单</button><output>待查询</output></main>'
def act(markup, name):
# 人工XML夹具显式写role与disabled=true;真实浏览器读取计算后的可访问性属性。
root = ET.fromstring(markup)
matches = [node for node in root.iter("button")
if node.get("role") == "button" and node.text == name]
if len(matches) != 1:
raise ValueError("定位结果必须唯一")
if matches[0].get("disabled") == "true":
raise ValueError("按钮尚未可用")
# 受控页面事件:点击查询后更新结果节点,再从节点读回。
root.find("output").text = "订单 A17:已发货"
return ET.tostring(root, encoding="unicode"), root.find("output").text
if __name__ == "__main__":
updated, result = act(PAGE, TARGET)
print("定位:role=button,name=" + TARGET)
print("读回:" + result)
print("结果节点:" + updated.split("<output>")[1].split("</output>")[0])import unittest
import dom_agent as lab
class MechanismTests(unittest.TestCase):
# 正常、边界和失败输入都由这里提供,直接运行即可。
def test_normal(self):
markup, result = lab.act(lab.PAGE, "查询订单")
self.assertIn("已发货", markup)
self.assertEqual(result, "订单 A17:已发货")
def test_boundary_reorder(self):
page = lab.PAGE.replace("<main>", '<main><button role="button">取消</button>')
self.assertEqual(lab.act(page, "查询订单")[1], "订单 A17:已发货")
def test_failure_ambiguous_or_disabled(self):
for page in [lab.PAGE.replace("</main>", '<button role="button">查询订单</button></main>'),
lab.PAGE.replace('id="query"', 'id="query" disabled="true"')]:
with self.assertRaises(ValueError):
lab.act(page, "查询订单")
if __name__ == "__main__":
unittest.main()打开 PowerShell,逐行输入下列命令。cd 是进入文件夹,dir 是列出文件;确认列表里有刚保存的两个 .py 文件,再运行 Python。
# 在刚才创建的练习目录检查文件,再运行程序。
ls
python -X utf8 dom_agent.py
python -X utf8 -m unittest -v test_dom_agent.py程序输出:
定位:role=button,name=查询订单
读回:订单 A17:已发货
结果节点:订单 A17:已发货测试末尾应显示 Ran 3 tests 与 OK。若找不到文件,检查当前目录与扩展名;若导入失败,确认测试和主程序在同一文件夹。测试描述的三组材料就在上方代码,不需要另外准备输入。
改一处,再看结果
程序连续输出定位名、业务结果和结果节点内容。后两行相同,是因为我们从更新后的节点树取值,读回没有借用动作前的旧文字。测试在前面插入一个“取消”按钮,查询仍成功;这说明按名称找节点不受位置顺序影响。
改动主文件里的 TARGET = "查询订单" 为 TARGET = "取消",保存后运行同一条命令。页面中没有这个按钮,将出现 ValueError: 定位结果必须唯一,退出码为 1。然后把 TARGET 恢复成“查询订单”并再次运行。遇到同名按钮时,应缩小到订单卡片区域再定位;真实浏览器里不要为了让动作通过就选第一个匹配节点。
改动后仍在刚才的 PowerShell 目录运行:
python -X utf8 dom_agent.py确认上述变化后,把参数恢复成原值,保存并再次执行这条命令。
把机制接回 Agent
浏览器工具接入时,观察返回页面角色与名称,Agent 提议点击,工具定位并执行,再读取订单结果。定位器需要在每次动作前重新查询当前页面;视觉坐标也需要绑定最新截图。更复杂的网页可沿 BrowserGym 的任务与环境实现阅读页面观察、动作空间和完成条件。
课后练习展开习题收起习题
正在读取本地记录…
不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。
选择题 · 先判断边界
关于“Browser Agent、DOM 与视觉定位”,哪种做法更稳妥?
对应知识点:L9-browser
简答题 · 解释失败
写出一个失败输入并说明记录字段。
对应知识点:L9-browser
代码题 · 边界测试
补充正常、边界、失败三组输入和预期结果。
对应知识点:L9-browser
场景题 · 结果未知
证据不足时如何设计状态和人工接管?
对应知识点:L9-browser
描述会发送给本站维护者,用于修订文章,保存90天。请只填写文章问题,省略个人信息、密钥和内部资料。