【Agent】浏览器自动化最佳实战
让 Agent 操作网页,第一反应通常是”让它像人一样点”。截图、识别按钮、移动鼠标、输入文字,看起来很自然,也很符合我们对”电脑自动化”的直觉。
但实际做过几次就会发现:能点,不等于好用。一次临时演示可能没问题,真正要把它变成稳定、可重复、以后一句话就能执行的能力,难点完全不同。
这篇文章记录我目前认为更适合个人 Agent 的浏览器自动化方案:复用用户已经登录的 Chrome,通过 Apple Events 向当前页面注入 JavaScript,直接读取和操作 DOM,并把最终流程沉淀成 Skill。
文中不会讨论某个网站的具体按钮、接口或业务细节,只讲通用方法。
候选方案
选择浏览器自动化方案时,先看 Agent 要控制的是哪一层:浏览器页面、整个桌面、独立自动化浏览器、现有 Chrome、还是网站接口。下面六种方案各自控制不同的对象,不应混为一谈。
方案一:使用 Hermes Agent 内置的浏览器工具
Hermes Agent 内置了 browser_navigate、browser_snapshot、browser_click、browser_type、browser_press、browser_scroll、browser_back、browser_get_images、browser_vision 和 browser_console 等工具。它们通常通过 CDP 控制一个由 Agent 管理的 Chromium 浏览器。
CDP 是 Chrome DevTools Protocol(Chrome 开发者工具协议)的缩写。它允许程序直接与 Chrome 或 Chromium 通信,可以理解为”代码版的 Chrome 开发者工具”。其中,browser_navigate 用来打开网页,browser_snapshot 读取页面文本、控件和元素编号,browser_click、browser_type、browser_press 和 browser_scroll 负责交互,browser_vision 用于截图识别,browser_console 则可以查看控制台错误或执行页面 JavaScript。
这套工具可以完成登录后的网页操作、填写表单、下载文件、测试 Web 应用和提取动态页面内容。它控制的是 Agent 自己的浏览器会话,不是用户桌面上正在使用的 Chrome。如果任务依赖用户日常 Chrome 中已有的登录状态,通常仍要在这个独立会话中重新登录。
方案二:使用 Hermes Agent 的 Computer Use Skill
Hermes Agent 还提供了基于 cua-driver 的 Computer Use Skill。CUA 通常指 Computer Use Agent,cua-driver 是 Hermes 用来驱动整个电脑桌面的底层程序。它通过 macOS Accessibility、屏幕捕获和输入事件等系统能力,控制 Chrome、Finder、系统设置及其他原生应用。
这套方案支持截图、点击、输入、拖动、快捷键和切换应用,也可以读取辅助功能树后按元素操作。设计上会尽量在后台发送输入,不抢用户真实的鼠标指针和键盘焦点。它的控制对象是整个桌面,而不是网页 DOM。
它适合跨应用流程,以及 Finder、系统设置等无法使用浏览器工具的场景。用于复杂网页时,辅助功能信息可能不完整,坐标也会受窗口位置和页面布局影响。因此它是通用的桌面方案,也是 DOM 自动化失效后的重要兜底,但不是批量网页操作的首选。实际使用前还要确认当前 Hermes 会话是否注册了 computer_use 工具;只有 Skill 文档而没有对应工具时,Agent 无法真正执行桌面控制。
方案三:编写 Playwright 或 Puppeteer 脚本并启动独立浏览器
这一方案由开发者自行编写自动化程序,并启动独立的 Chromium 或专用 Chrome Profile。脚本可以精确读取 DOM、等待元素、监听页面请求、保存截图,还能实现重试、日志和测试。
它适合 CI、自动化测试、专用账号或长期运行的无人值守任务。边界也很清楚:脚本管理自己的浏览器进程和会话,不依赖用户当前打开的 Chrome。
个人 Agent 场景里的主要问题是登录态。新浏览器通常没有用户日常 Chrome 的 Cookie,也没有已经完成的企业认证。复制 Profile 还可能遇到 macOS 钥匙串加密、浏览器锁文件和安全策略。自动化能力很完整,但需要额外维护一套浏览器身份。
方案四:通过 CDP 接管开启了远程调试的现有 Chrome
这一方案不再启动独立浏览器,而是让 Playwright、Puppeteer 或其他程序通过 CDP 连接现有 Chrome。这样既能读取 DOM,也能复用浏览器中的登录态。
它与方案一的区别是:方案一使用 Agent 管理的浏览器工具和会话;方案四连接用户指定的 Chrome 实例。与方案三的区别是:方案三由脚本创建浏览器,方案四只接管已经启动的浏览器。
Chrome 通常需要带远程调试参数启动,并开放本地调试端口。已经运行的日常 Chrome 往往需要重启才能启用,长期开放调试端口也需要注意本机安全。它适合愿意维护专用 Chrome 启动方式的用户。
方案五:绕过页面,直接调用网站 API
如果网站提供公开 API,Agent 可以直接发送 HTTP 请求,不必操作浏览器。这是最干净、最快也最容易验证的方案,应该优先考虑。
有些网站没有公开接口,也可以从开发者工具里观察前端请求,再尝试复用内部 API。但这已经是另一种风险更高的做法:接口路径和请求体可能随前端版本改变,调用还可能依赖 Cookie、CSRF Token、签名或设备信息。它也绕过了页面原本的交互和状态检查。
所以这一方案的控制对象是服务端接口,不是浏览器。公开 API 值得优先使用;未公开接口则要评估维护成本和误操作风险。
最终采用的方案六:通过 Apple Events 操作日常 Chrome 的 DOM
这里先解释一下 DOM。DOM 是 Document Object Model(文档对象模型)的缩写。浏览器加载网页后,会把 HTML 解析成一棵由标题、按钮、输入框、表格等节点组成的树。JavaScript 可以沿着这棵树读取页面内容、定位元素、触发点击,也能检查操作后的状态。对 Agent 来说,读取 DOM 相当于直接理解网页结构,而不是只看一张截图猜测哪里能点。
在 macOS 上,Chrome 支持通过 Apple Events 在当前标签页执行 JavaScript。它不需要启动第二个浏览器,也不要求 Chrome 预先开启远程调试端口。Agent 通过 AppleScript 或 osascript 找到用户当前的 Chrome 标签页,再把 JavaScript 送进页面执行。
1 | Agent |
AppleScript 只负责导航标签页和传递 JavaScript。页面识别、元素定位、点击以及结果验证都由 JavaScript 完成。
例如,通用执行器可以很短:
1 | on run argv |
这套方案的控制对象是用户日常 Chrome 中的真实页面。它既复用了已有的登录、企业认证、Cookie 和权限,又保留了 DOM 自动化的精确性。对于 macOS 上需要用户身份、没有公开 API、而且会重复执行的网页任务,这是我最终采用的方案。
为什么这个方案更适合个人 Agent
直接复用真实登录态
不复制 Cookie,不启动第二个 Profile,也不要求用户重新登录。Agent 操作的就是用户正在使用的 Chrome。
这对企业后台尤其重要。很多系统除了账号密码,还包含扫码、设备验证、企业切换或单点登录。复用现有会话省掉了最麻烦的一段。
定位依据是 DOM,不是像素
脚本可以按应用名、按钮文字、ARIA role、稳定 class 和元素关系定位目标。例如:
1 | const row = [...document.querySelectorAll("tr")] |
如果一行里有多个按钮,还可以限定在该行内部继续查找。页面滚动、窗口尺寸和屏幕分辨率不再影响定位。
能拿到业务标识
批量任务不能只看显示名称。DOM 中常常包含列表行的 data-row-key、详情链接或资源 ID。脚本可以在点击前记录这些标识,并在操作后进行核对。
这比”点击第六行的配置按钮”可靠得多。
可以验证结果,而不是只验证点击
成熟的自动化流程不应该把 button.click() 当成成功。正确的完成条件应该来自页面最终状态:
- 开关文字是否发生变化
- 弹窗是否消失
- URL 是否返回列表页
- 目标行是否从列表中消失
- 再次遍历分页时是否仍能找到目标
点击只是动作,状态变化才是结果。
开发速度快
第一次探索页面时,可以先运行只读 JavaScript,输出按钮、表格行和局部 outerHTML。确认结构后,再写操作逻辑。
调试过程不需要搭建完整的浏览器工程,也不需要处理新的登录会话。一个 Python 脚本、一个 AppleScript 执行器,加上若干 DOM 查询就能工作。
容易沉淀为 Skill
页面选择器、状态判断、风险边界和验证方式都可以写进 Skill。以后 Agent 再遇到同类任务,不需要重新从截图和坐标开始摸索。
实现步骤
第零步:先由人完整演示一遍
在写自动化脚本之前,最好先让熟悉业务的人从头到尾实际操作一次,并同步讲清楚自己正在做什么。描述越具体越好:从哪个入口进入、使用了哪些筛选条件、为什么选择这个对象、点击后出现了什么提示、如何判断当前步骤成功,以及完成后怎样回到列表继续处理。容易被忽略的翻页、等待、二次确认和异常分支也应记录下来。
这次演示的重点不是教 Agent 模仿鼠标轨迹,而是把人脑中的隐性流程变成可分析的操作说明。Agent 可以据此梳理页面入口、步骤依赖、目标匹配规则和完成条件,再去寻找对应的 DOM 元素。
如果边操作边打字太麻烦,可以直接录音讲解。飞书妙记很适合这个场景:人一边操作一边口述,结束后把妙记生成的完整文字稿交给 Agent。尽量提供逐字稿,而不是只给 AI 总结,因为确认弹窗、返回路径和状态变化等细节很容易在摘要中被省略。Agent 拿到文字稿后,应先整理成结构化步骤,并对照实际页面补齐可验证的状态,再开始设计自动化。
第一步:确认任务边界
自动化之前先明确四件事:
- 目标资源如何精确匹配
- 哪些页面是权威入口
- 操作顺序是否有前置依赖
- 什么状态才算真正完成
批量删除尤其不能用模糊包含匹配。显示名称相同的对象,还要记录各自的唯一 ID。
第二步:验证 JavaScript 注入
先执行只读探针:
1 | JSON.stringify({ |
这一步用于确认:
- 当前标签页确实是目标页面
- 登录态有效
- DOM 已经可读
- Chrome 允许 Apple Events 执行 JavaScript
Chrome 需要开启”视图 → 开发者 → 允许 Apple 事件中的 JavaScript”。这是一次性设置。
第三步:只读分析 DOM
不要一上来就点击。先找出页面中稳定的语义锚点:
- 表格行
- 名称字段
- 状态字段
- 操作按钮
- 分页控件
- 确认弹窗
- 唯一资源 ID
可以输出局部结构:
1 | const rows = [...document.querySelectorAll("table tbody tr")]; |
稳定 class 可以使用,但不要只依赖一串自动生成的 class。更稳妥的做法是将 class、文字、父子关系和业务 ID 组合起来。
第四步:把流程写成状态机
批量网页操作最好按状态推进,而不是写成长串固定点击:
1 | 扫描列表 |
每处理一个对象就重新扫描,虽然比一次性缓存所有行稍慢,却能避免分页、排序和列表刷新导致的旧元素引用失效。
第五步:处理异步加载
网页组件经常先渲染外壳,再加载业务数据。固定 sleep(3) 可以用于实验,但最终脚本应尽量等待条件:
1 | def wait_for(expression, timeout=15): |
等待条件可以是:
- 某个元素出现
- 某段状态文字变化
- URL 进入详情页
- 弹窗出现或消失
- 列表重新加载完成
第六步:写入操作要有双重验证
删除类操作至少做两次核对:
- 列表页通过名称和唯一 ID 选中目标
- 详情页再次确认名称、状态和 ID
操作后也要验证两次:
- 当前页面显示成功后的状态
- 重新进入列表,完整遍历分页,确认目标不存在
这比增加更多”你确定吗”的人工弹窗更适合无人值守任务。安全性来自程序的精确边界和验证,不是让用户重复确认同一件事。
第七步:保留降级路径
DOM 自动化并不能覆盖所有情况。合理的降级顺序是:
- DOM JavaScript
- 浏览器可访问性元素
- 像素点击
- 用户处理登录、验证码或权限弹窗
如果页面变成 Canvas、跨域 iframe 或封闭的 Shadow DOM,DOM 路径可能受限。此时再使用 Computer Use,而不是从一开始就依赖坐标。
如何沉淀为 Skill
一次脚本能跑通还不够。Skill 要记录的不是某次成功,而是下次如何稳定地再次成功。
一个实用的目录可以这样组织:
1 | browser-task-skill/ |
SKILL.md 写什么
主文件只保留每次都需要的内容:
- 触发条件
- 前置环境
- 权威页面入口
- 命令参数
- 操作顺序
- 安全边界
- 完成标准
- 常见失败分支
不要把所有 DOM 片段和调试记录都塞进主文件。页面结构细节更适合放到 references。
脚本如何设计
脚本最好把”只读观察”和”实际执行”分开。首次运行或页面改版后,先做只读扫描,确认目标数量、页面结构和权限状态;正式执行时再进入完整流程,并在结束后重新扫描。
目标名称、租户 URL 等会变化的信息应该作为参数传入,避免把个人环境硬编码进逻辑。具体采用命令行参数、配置文件还是环境变量,可以根据任务复杂度决定。博客只需要说明这个设计原则,不必把脚本接口写成使用手册。
references 写什么
参考文件适合保存:
- 当前验证过的选择器
- 页面改版后的排查方法
- 常见错误和修复方式
- 哪些元素容易异步加载
- 降级到 Computer Use 的条件
Skill 的完成标准
Skill 创建后至少做三类验证:
- 静态检查:脚本能编译,Front Matter 合法
- 只读测试:
scan能在真实页面返回结果 - 实战验证:写操作执行后,最终状态满足验收条件
实战验证很重要。没有真实运行过的自动化说明,只是一份猜测。
这个方案的限制
它依赖 macOS 和 Google Chrome,也依赖用户允许 Apple Events 执行 JavaScript。脚本默认操作当前活动标签页,因此运行期间不适合让用户同时在同一个 Chrome 窗口里频繁切换标签。
页面改版后,DOM 选择器仍可能需要更新。好在更新过程是可诊断的:先运行只读扫描,查看新的结构,再修正选择器。相比坐标漂移,这种故障更容易定位,也更容易写进 Skill。
如果网站提供稳定、权限清晰的公开 API,应该优先使用 API。DOM 自动化适合填补”没有公开接口,但用户在网页里可以完成操作”的空白。
结语
个人 Agent 的浏览器自动化,重点不是模拟人类鼠标,而是复用人类已经建立好的浏览器会话,然后以机器更擅长的方式理解页面。
在 macOS 上,现有 Chrome、Apple Events 和 DOM JavaScript 的组合很好地平衡了登录态、开发成本、执行速度与可验证性。视觉点击仍然有价值,但更适合作为最后的兜底。
真正值得沉淀的也不是某段选择器,而是一套纪律:先只读观察,精确匹配目标,按状态推进,每一步都验证,最后重新扫描,并把这些经验写进 Skill。下一次再遇到类似网页,Agent 就不必重新学着”点鼠标”了。

