最近有几个做自动化测试的同行跟我吐槽,说规则脚本越写越累,目标网站一改版,CSS 选择器就全废了,又要花半天调 xpath。我特别理解这种状态,因为我自己也经历过类似周期:写脚本、上线、跑俩月、网站改版、脚本报废、再改。后来我开始关注一个叫 browser-use 的开源库,思路完全不一样——它让大模型自己“看”网页、自己决定下一步点哪里、填什么,而不是把每一步都写死在代码里。这篇文章就把我对这个库的理解、原理拆解、跑通 Demo 的经过,以及实测中踩过的坑和调优思路完整分享出来。如果你是做爬虫、做 RPA、做 AI Agent 的开发者,或者只是好奇“AI 操作浏览器到底是怎么实现的”,这篇内容应该能给你一个比较系统的参考。
1. browser-use 到底是什么:当自动化脚本不再“写死规则”
1.1 它解决的核心痛点:规则脚本的维护成本
传统浏览器自动化的典型做法是告诉程序“先找到 ID 为 email 的输入框,输入 A;再点击 class 为 btn-login 的按钮”。这种写法本身没毛病,问题在于页面一改版,ID 变成 name,class 变成>pip install browser-use playwright playwright install chromium
第一行是装库本身,第二行是给 Playwright 下载 Chromium 浏览器内核。很多人只执行第一步,运行时报错找不到浏览器才想起来第二步。
我建议用 Python 3.10 或 3.11 的虚拟环境来装。之前我在 3.13 的环境里装时遇到个别依赖编译失败,虽然最后也能装上,但浪费了不少时间。Windows 用户如果装完启动浏览器被杀毒软件拦截,记得把项目目录加白名单,这个库会临时创建浏览器实例,有些安全软件会误报。
3.2 最小可用示例
下面这个代码,是我跑通的最小可复现版本。功能非常简单:打开一个网页,把一个指定的页面内容读出来。
import asyncio from langchain_openai import ChatOpenAI from browser_use import Agent, Browser, BrowserConfig async def main(): browser = Browser(config=BrowserConfig(headless=True)) agent = Agent( task="打开 https://example.com,把页面上标题和第一段正文内容提取出来", llm=ChatOpenAI(model="your-model-id", temperature=0), browser=browser, max_steps=5, ) history = await agent.run() print(history.final_result()) await browser.close() asyncio.run(main())注意几点:
model="your-model-id"需要换成你自己有权限调用的模型 ID,这里我故意用占位符,避免给你一个过时的具体模型名。ChatOpenAI来自 LangChain 的模型封装,只要你的模型服务接口兼容 OpenAI 风格,换成本地部署的模型也可以传进来。headless=True表示无头模式,浏览器在后台跑,不弹窗。第一次调试建议设为False,你可以亲眼看模型每一步在干嘛。
3.3 第一次运行你会看到什么
跑起来之后,控制台会输出一大段“内心戏”,大致是模型的推理过程:它先分析当前页面有哪些可用元素,然后决定点击哪个、输入什么,执行之后又看到新状态,再继续决策。第一次看的同学可能会觉得信息量很大,但别慌,你只需要重点看两样东西:
- 它是否朝着目标前进(比如先找到搜索框,再输入关键词);
- 最后
final_result()返回的内容是不是你想要的。
如果你的模型服务配好了,但任务一直卡住不动,最常见的原因是模型输出格式和库不兼容,或者max_steps给太少,模型还没做完就被掐断。我一般查询类任务给 10,表单类给 20,足够它折腾。
4. 实战拆解:一个带登录、填表、数据提取的完整任务
4.1 任务描述怎么写才不容易翻车
普通的“打开网页读取标题”Demo 太温和了,我们上一个真实一点的任务:登录一个系统,进入数据页面,提取表格内容。
我一开始的任务描述是“登录后查看数据看板,把表格内容给我”,结果模型到处瞎逛,因为“表格内容”范围太大。后来我改成这样:
打开 https://example.com/login ,在用户名字段输入 demo_user,密码字段输入 demo_pass,点击登录按钮。登录后进入“数据总览”菜单,找到第一个表格,把表格最后一列所有数值按顺序提取出来,返回成列表。
这样模型每一步都清楚:去哪、做什么、什么时候停。在实际执行中,它可能会先找“用户名输入框”而不是直接找输入框,这是好事——说明它在通过语义理解来定位,而不是瞎猜顺序。
任务描述里如果包含账号密码,建议用变量从外部读取,而不是直接写死在源码里提交到仓库。我习惯用环境变量注入,这样就算任务日志外泄,也不会直接泄露凭据。
4.2 执行过程中的常见行为观察
跑这种登录类任务时,你会看到模型偶尔“犹豫”:它可能点了一个不是登录按钮的按钮,发现没反应,又看了一遍页面状态,重新找按钮。这种自我纠错能力是最能体现 Agent 价值的地方。
但也要注意,模型有时会陷入重复动作:同一类点击重复执行好几次,看起来像在循环。我遇到这种情况,通常调整max_actions_per_step或把任务重新拆细,让每一轮决策只做一件小而明确的事。
另外,登录后经常有跳转、加载动画、异步渲染,模型在页面还没稳定时就去点,容易失败。如果网络慢,可以考虑在关键节点给模型更多上下文,比如“点击登录后,如果显示登录成功跳转到看板页,再继续下一步”。这算是提示词工程层面的一个有效套路。
4.3 回放与取证:history、截图和录屏的价值
调试 Agent 类项目,最怕的就是“它不动了但我不知道为什么”。browser-use 提供了历史记录机制,agent.run()返回的 history 对象里记录了每一轮的思考、动作、执行结果。你可以把历史导出来看,定位它在哪一步出了问题。
还有两个非常实用的调试选项:截图和录屏。开启录屏后,它会保存整个浏览器操作过程的视频文件,我排查复杂问题基本都是开着录屏跑的,犯错之后直接回放视频,一眼就能看出模型当时把鼠标点到了哪个位置。这比看日志直观太多了。
我的建议是,任何需要上生产环境跑的任务,调试阶段必须留记录,否则出了问题你只能靠猜。这个习惯帮我省了大量排查时间。
5. 实测踩过的坑和调优思路
5.1 Token 消耗太猛怎么办
这类方案最大的隐性成本是模型 token。每轮循环都要把页面状态发给模型,页面稍复杂一点,一轮就是几万 token,一个任务跑十几轮,费用肉眼可见地上涨。我刚开始跑一个中等复杂度任务,用了几十个步骤,账单让我心疼。
优化方向主要有这几个:
| 优化手段 | 做法 | 效果 |
|---|---|---|
| 关闭视觉 | use_vision=False,不传截图只传文本树 | 省约一半 token,纯文本任务足够 |
| 限制步数 | 合理设置max_steps,不要无脑给 50 | 避免模型瞎逛浪费额度 |
| 拆分任务 | 一个大任务拆成几个小 Agent 串行执行 | 每个 Agent 上下文更短,失败重跑成本低 |
| 用便宜模型 | 简单任务用低成本模型,复杂任务才用强模型 | 成本可降低一个量级 |
我自己的原则是:能用文本树搞定的,绝不开启视觉;能拆的任务一定要拆。比如“搜索资料然后整理成报告”这种任务,我会拆成“搜集资料”和“报告撰写”两个 Agent,前者用便宜模型跑多步收集,后者用强模型一次性整理。
5.2 登录验证码、弹窗和 iframe 的边界处理
验证码是这个方案最大的痛,没有之一。模型确实可以“看”验证码图片,然后尝试猜,但成功率和稳定性都不够,尤其是复杂扭曲的图形验证码,猜错两次就可能触发账号保护机制。
我建议的稳妥做法是:登录这类敏感环节用传统方式解决,例如手动登录一次,导出 cookie 给 Agent 复用,或者先用普通脚本完成登录,再把登录后的会话交给 Agent 执行后续流程。这相当于把最不可控的部分剥离出去。
弹窗和 iframe 也会干扰模型。弹窗出现时页面可访问性树会被弹窗内容占据,模型有时会忽略背后的主页面。我遇到这种情况,会在任务描述里明确写“如果出现弹窗,先按 Esc 关闭再继续”。iframe 里的元素通常也能被读出来,但层级较复杂时模型容易定位到错误的同名元素,描述里加上 iframe 的上下文会好一些。
5.3 模型选型与参数配置的实战心得
用哪种模型,直接决定这个方案能不能落地。我个人的感觉是:
- 简单任务(查标题、翻页、点击几个链接)用廉价型号完全可以胜任;
- 中长链路任务(登录、填表、提取)用主流闭源大模型的中档版本比较稳,便宜型号容易在中间步骤忘掉任务目标;
- 需要理解复杂文案或者页面结构非常混乱的场景,才需要上更强的模型。
temperature一定要调低,最好接近 0,否则模型有时会“灵光一闪”做出不可复现的额外动作。我见过一次温度没改,模型顺手把页面上某个开关给打开了,排查了很久。
max_steps的把握需要一点经验。给太少,任务完不成;给太多,模型会在简单任务上反复横跳浪费钱。我的习惯是先给一个中间值,比如 15,跑一遍看历史记录里实际用了几步,再按实际值上下浮动调整。
5.4 混合模式:脚本和 Agent 分工协作最稳妥
用久了你会发现,browser-use 并不是用来替代 Playwright 的,它更适合用在“环境变化频繁”的决策节点上。我在生产项目里的做法是:固定不变的步骤全部用 Playwright 脚本,比如登录、打开页面、上传文件;变化较多的操作交给 Agent,比如页面元素的动态定位、内容理解、异常信息读取。
这样分工的好处是:
- 固定部分速度快、成本低、稳定性高;
- 动态部分利用模型的泛化能力,不会被网站改版轻易击穿;
- Agent 只处理少量关键节点,token 消耗也会小很多。
如果你想把这套东西真正用于实际业务,我非常推荐从这种混合模式起步,而不是上来就让 Agent 接管一切。
6. 适用边界与后续可以怎么扩展
6.1 什么场景我不建议直接用 browser-use
说实话,这种方案并不是银弹。我会避开以下几类场景:
第一,高频、低延迟的接口数据抓取。直接调 API 或用 requests 更高效,开一个浏览器让模型去读网页,属于杀鸡用牛刀。
第二,高并发批量任务。每个任务都启动浏览器实例,资源占用非常重,而且容易被目标站点的风控识别。我看到有人拿它跑几十个并发抓取,结果 IP 被限制得很快。
第三,对交互精度要求极高的操作。比如 Canvas 画布内拖拽、WebGL 场景操作,模型很难精确控制,不如写专用脚本。
第四,涉及高价值账号的关键操作。我不建议让模型直接操作支付、核心配置等场景,万一它在一个确认弹窗上点击了错误选项,后果不可控。
遇到这些场景,我更推荐前面说的混合模式,或者干脆用传统自动化方案。
6.2 几个值得尝试的扩展方向
如果你已经玩转了基本用法,下面这几个方向我觉得性价比很高。
让 Agent 生成 Playwright 代码而不是直接运行浏览器。思路是你让模型先分析页面、生成一段目标操作的 Python 代码,人工审查后再执行。这样既保留了模型的灵活性,又把最终执行控制权收回到确定性代码手里,安全性和可维护性都更高。
多 Agent 协作也可以尝试。一个 Agent 负责读取信息,另一个负责填写表单,它们通过消息队列传递结果,各自上下文更小、专注度更高。
本地小模型配合远程模型做两级调度,简单任务永远走小模型,遇到复杂页面再升级到更强模型,能在成本上实现比较好的平衡。
7. 关于方案落地,我最后想分享的个人体会
实测了一段时间之后,我最大的体会是:不要把 browser-use 当成一个全能的“AI 替身”,它更像是一个“具备了自我纠错能力的自动化框架”。它最适合的场景不是替代你解决所有问题,而是把规则脚本最脆弱的那部分——页面变化后的重新定位、动态内容的语义判断——接管过来,让整个自动化链条变得更有韧性。
调试阶段多花一点时间把任务描述打磨清楚,收益非常明显。我现在每次新建一个任务,都会先在录屏和历史记录的双重观察下跑一遍模型自己的“思路”,然后根据它的实际行为反向调整任务描述,直到它能在最少的步数内完成任务。这种调试方式,比一开始就追求复杂的代码逻辑有效得多。
最后再分享一个小技巧:如果你是在一个长期运行的服务里用 browser-use,建议给每个任务设置独立的会话、独立的浏览器实例,任务结束就及时关闭,避免浏览器进程残留导致的资源泄漏。我在早期跑批量任务时吃过这个亏,浏览器进程越堆越多,最后整个机器卡到无响应。把这些细节处理好,这套方案完全可以在实际项目中稳定承担一部分自动化工作。