1. 从一次“抓不到数据”的崩溃说起:浏览器自动化到底难在哪
先说个真实场景。我让 OpenClaw 去抓一个用 Vue 写的商品列表页,它返回的 HTML 里只有一行<div id="app"></div>,数据全在 JavaScript 渲染之后才出现。换成无头浏览器,页面是出来了,但目标站点识别出 Headless 特征,直接弹了个滑块验证。那一刻我才意识到:浏览器自动化不是“装个 Playwright 就完事”,而是一整套从任务定义、浏览器控制、凭证管理到结果验证的链路工程。
OpenClaw 的自动化能力,本质上是在解决“让 AI 像人一样操作浏览器”这件事。它把浏览器自动化拆成了几个可组合的层级:L0 是纯搜索加抓取,不碰浏览器;L1 是无头浏览器,适合 JS 渲染页面;L2 是有头浏览器加 DOM 操作,能登录、填表单、点按钮;L3 是截图加视觉识别,兜底处理图片里的信息。这套分级思路很实用,因为日常任务里 80% 的信息获取其实 L0 就够了,没必要每次都启动一个完整浏览器。
而 agent-browser 是 OpenClaw 在 L2/L3 层的核心组件。它底层基于 CDP(Chrome DevTools Protocol)加 Playwright。Playwright 负责跨浏览器的自动化 API,CDP 负责跟 Chrome 实例通信。两者结合之后,OpenClaw 可以打开一个独立的 Chromium 实例,用独立的 profile 隔离,然后通过 DOM 快照理解页面结构,再执行点击、输入、滚动这些动作。
这篇文章要解决的核心问题是:怎么把这条链路搭成可复现的任务流。不是“演示一下能跑”,而是你照着配置片段和验证命令,在自己的环境里能稳定复现。同时我会说明怎么用统一的 Key/API 通道管理调用凭证,避免每个工具各配一套密钥、最后自己都记不清哪个 key 对应哪个服务。
适合谁看?如果你已经在用 OpenClaw,但浏览器任务总是时好时坏;或者你正准备把某个重复的网页操作交给 AI,却卡在环境配置和验证环节,那这篇就是写给你的。我会从任务定义开始,一步步走到执行验证,中间踩过的坑也会标出来。
2. TaoToken 前置:统一 Key/API 通道与 agent-browser 的凭证管理
在讲具体配置之前,得先把凭证这件事理清楚。浏览器自动化任务里,OpenClaw 需要调用的外部服务不止一个:搜索 API、网页读取 API、大模型推理 API,可能还有视觉识别 API。如果每个服务都单独申请 key、单独配置,很快就会变成一团乱麻。更麻烦的是,当你想换一个模型或者调整调用配额时,得翻好几个配置文件。
TaoToken 在这里的角色,是提供一个统一的 API 通道。你可以把它理解成一个“凭证中转站”:OpenClaw 和 agent-browser 只需要认一个 Base URL 和一个 Key,背后具体调用哪个模型、哪个搜索服务,由通道来路由。这样做的好处很直接——配置集中、切换成本低、排查问题时只需要看一个入口。
先明确几个地址,后面配置里会反复用到:
- 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end
- API 基础地址:https://taotoken.net/api
- 模型对话入口:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
- Coding Plan 入口:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite
- 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite
- API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
- Claude Code 接入:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite
注意,API 地址https://taotoken.net/api后面不加 UTM 参数,这是给程序调用的,加了反而可能影响请求解析。带 UTM 的那些链接是给人点的,用于归因统计。
现在说 agent-browser 这边怎么接。OpenClaw 的浏览器控制本身不直接依赖外部 API,它用的是本地 CDP 端口。但浏览器任务里经常需要“让模型理解页面快照”或者“让视觉模型识别截图”,这些推理请求就要走统一通道。所以配置分两块:一块是 OpenClaw 的模型 provider 配置,一块是 agent-browser 的浏览器 profile 配置。
模型 provider 这块,OpenClaw 支持自定义 OpenAI 兼容接口。你需要在配置文件里指定baseURL为https://taotoken.net/api,apiKey填你在控制台生成的 key,model填你要用的模型 ID。这样 OpenClaw 在需要推理时,请求会先到 TaoToken,再由它转发到实际模型。好处是你不用在 OpenClaw 里存多个厂商的 key,换模型也只改一个model字段。
浏览器 profile 这块,agent-browser 默认用openclaw这个隔离 profile,不会碰你日常用的 Chrome 数据。这个设计很关键,因为自动化任务经常要登录各种站点,如果跟你自己的浏览器混在一起,cookie 和会话会互相污染。独立 profile 意味着 OpenClaw 有自己的一套 cookie 和缓存,你手动登录一次之后,后续任务可以复用。
还有一个容易忽略的点:如果你用的是云主机跑 OpenClaw,默认没有桌面环境,agent-browser 启动有头浏览器时会失败。这时候要么装桌面环境加远程桌面,要么改用无头模式。这个后面排障章节会细说。
凭证管理上,我的建议是:所有需要 key 的地方,都指向 TaoToken 的同一个 key。包括 OpenClaw 的模型调用、搜索 skill 的 API 调用、视觉识别调用。这样你只需要在一个地方轮换 key,不用逐个服务去改。控制台的 API Keys 页面可以生成和管理这些 key,接入文档里有各语言的调用示例。
3. 可复制配置:agent-browser 与 Playwright 的任务流搭建
这一章是核心操作部分。我会给出完整的配置片段,包括 OpenClaw 的模型 provider 配置、agent-browser 的浏览器 profile 配置、以及一个可复现的浏览器任务定义。你照着改路径和 key 就能跑。
先看 OpenClaw 的主配置文件。通常位于~/.openclaw/openclaw.json,如果你用的是项目级配置,也可能在项目根目录的.openclaw/config.json。下面是一个最小可用的配置片段,重点是providers和browser两块:
{ "providers": { "default": { "type": "openai-compatible", "baseURL": "https://taotoken.net/api", "apiKey": "sk-your-taotoken-key", "model": "your-model-id", "timeout": 60000 } }, "browser": { "defaultProfile": "openclaw", "cdpPort": 18998, "headless": false, "profiles": { "openclaw": { "color": "#FF4500", "isolated": true, "userDataDir": "~/.openclaw/browser-profiles/openclaw" }, "work": { "color": "#3b82f6", "isolated": true, "userDataDir": "~/.openclaw/browser-profiles/work" } } }, "skills": { "scrapling-web-scraper": { "enabled": true, "path": "~/.openclaw/skills/scrapling-skill", "config": { "stealth_mode": true, "solve_cloudflare": true, "proxy_rotation": "auto" } } } }这里有几个参数需要解释。baseURL指向 TaoToken 的 API 地址,apiKey换成你在控制台生成的 key,model填你要用的模型 ID。browser.defaultProfile设为openclaw,表示默认用隔离 profile。cdpPort是 CDP 调试端口,agent-browser 通过这个端口跟 Chromium 实例通信。headless设为false表示有头模式,你能看到浏览器界面,方便调试;生产环境可以改true。
profiles里我配了两个:openclaw用于日常自动化任务,work用于需要单独登录态的任务。每个 profile 有独立的userDataDir,cookie 和缓存互不干扰。color是浏览器窗口的标识色,多 profile 同时开的时候方便区分。
接下来是 agent-browser 的启动和任务定义。OpenClaw 提供了 browser CLI,你可以先用命令行验证浏览器能不能正常启动:
# 查看浏览器状态 openclaw browser status # 启动默认 profile 的浏览器 openclaw browser start # 打开一个页面 openclaw browser open https://example.com # 获取页面快照,用于 AI 理解结构 openclaw browser snapshot --format aria # 截图 openclaw browser screenshotsnapshot --format aria这个命令很关键。它返回的是页面的无障碍树结构,比原始 HTML 干净得多,模型读起来更省 token,也更容易定位到可交互元素。agent-browser 在执行点击、输入之前,通常会先拿一次快照,确认目标元素存在,再执行动作。
如果你需要控制自己正在用的 Chrome 标签页,而不是独立实例,可以用userprofile 加 Chrome 扩展的方式。先安装扩展:
openclaw browser extension install openclaw browser extension path然后打开 Chrome,访问chrome://extensions,启用开发者模式,点“加载已解压的扩展程序”,选择上面命令打印的目录。加载后把扩展固定到工具栏。这样 OpenClaw 就能通过扩展中继控制你当前的标签页。不过这种方式适合过渡期使用,长期跑自动化任务还是建议用独立 profile,避免跟你自己的浏览行为冲突。
现在定义一个可复现的浏览器任务。假设我们要抓取一个需要登录才能看到的订单列表页,任务分三步:打开登录页、填入凭证、跳转到订单页并提取数据。在 OpenClaw 里可以用一个 skill 或者一段任务描述来定义。下面是一个任务定义的 JSON 片段:
{ "task": "fetch-order-list", "steps": [ { "action": "browser.open", "params": { "url": "https://example.com/login", "profile": "openclaw" } }, { "action": "browser.snapshot", "params": { "format": "aria" } }, { "action": "browser.type", "params": { "selector": "input[name='username']", "text": "${USERNAME}" } }, { "action": "browser.type", "params": { "selector": "input[name='password']", "text": "${PASSWORD}" } }, { "action": "browser.click", "params": { "selector": "button[type='submit']" } }, { "action": "browser.wait", "params": { "selector": ".order-list", "timeout": 15000 } }, { "action": "browser.extract", "params": { "selector": ".order-item", "fields": ["orderId", "amount", "status"] } } ] }这个任务流里,${USERNAME}和${PASSWORD}是环境变量占位符,实际执行时从环境变量注入,避免明文写在配置里。browser.wait等待订单列表容器出现,超时 15 秒,这是处理异步加载的关键步骤。browser.extract按选择器提取字段,返回结构化数据。
如果你用 Playwright 直接写脚本,而不是走 OpenClaw 的 skill 体系,也可以。Playwright 的 Python 版本大概是这样:
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch( headless=False, args=["--remote-debugging-port=18998"] ) context = browser.new_context( user_data_dir="~/.openclaw/browser-profiles/openclaw" ) page = context.new_page() page.goto("https://example.com/login") page.fill("input[name='username']", "your-username") page.fill("input[name='password']", "your-password") page.click("button[type='submit']") page.wait_for_selector(".order-list", timeout=15000) items = page.query_selector_all(".order-item") for item in items: print(item.inner_text()) browser.close()注意--remote-debugging-port=18998这个参数,它让 Playwright 启动的 Chromium 暴露 CDP 端口,这样 OpenClaw 的 agent-browser 也能连上同一个实例。user_data_dir指向跟 OpenClaw 配置里一致的 profile 目录,保证登录态复用。
配置写完之后,先别急着跑完整任务。用openclaw browser status确认浏览器状态,再用openclaw browser open打开目标页面,手动确认页面能正常加载。这一步能排除掉大部分网络和证书问题。
4. 验证请求与成功结果:从快照到数据落盘
配置写完只是第一步,真正重要的是验证。浏览器自动化最怕的是“看起来跑了,但结果是错的”。所以这一章讲怎么一步步验证,每一步都有明确的预期输出。
第一步,验证模型通道是否通。在 OpenClaw 里发一个最简单的推理请求,确认baseURL和apiKey配置正确:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-your-taotoken-key" \ -H "Content-Type: application/json" \ -d '{ "model": "your-model-id", "messages": [{"role": "user", "content": "reply with ok"}], "max_tokens": 10 }'预期返回是一个 JSON,choices[0].message.content里包含ok。如果返回 401,说明 key 不对;如果返回 404,说明model字段填的模型 ID 不存在;如果连接超时,检查网络能不能访问taotoken.net。这一步过了,说明凭证通道没问题。
第二步,验证浏览器能启动并暴露 CDP 端口。运行:
openclaw browser start openclaw browser status预期输出里running: true,cdpPort: 18998,cdpUrl: http://127.0.0.1:18998。如果running: false,看日志里有没有 Chromium 启动失败的报错。常见原因是userDataDir路径不存在或者没有写权限,手动mkdir -p一下对应目录就行。
第三步,验证页面快照能拿到。打开一个测试页面,拿一次 aria 快照:
openclaw browser open https://example.com openclaw browser snapshot --format aria预期输出是一段结构化的文本,包含页面的标题、链接、按钮等元素。如果输出为空,可能是页面还没加载完,加一个--wait-until networkidle参数,或者先openclaw browser wait --selector body再拿快照。
第四步,跑一个完整的提取任务,验证数据能落盘。用前面定义的fetch-order-list任务,执行后检查输出:
openclaw run fetch-order-list --env USERNAME=your-user --env PASSWORD=your-pass预期输出是一个 JSON 数组,每个元素包含orderId、amount、status三个字段。如果返回空数组,先确认.order-item这个选择器在快照里存在;如果报超时,把browser.wait的timeout调大,或者检查登录是否成功——登录失败的话,订单页会跳回登录页,自然等不到.order-list。
第五步,验证截图和视觉识别链路。有些信息只在图片里,比如验证码或者图表。用截图命令拿到图片,再走视觉模型识别:
openclaw browser screenshot --output /tmp/page.png然后调用视觉模型,把图片 base64 编码后发给 TaoToken 的 API:
base64 -w 0 /tmp/page.png > /tmp/page.b64 curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-your-taotoken-key" \ -H "Content-Type: application/json" \ -d '{ "model": "your-vision-model-id", "messages": [{ "role": "user", "content": [ {"type": "text", "text": "描述这张图片里的主要内容"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,'"$(cat /tmp/page.b64)"'"}} ] }] }'预期返回是对图片内容的文字描述。这一步验证的是 L3 兜底能力,速度比 DOM 操作慢,但能处理 DOM 拿不到的信息。
我实测下来,这套验证流程走一遍大概十分钟,但能省掉后面几个小时的瞎猜。关键是每一步都有明确的成功标准,哪一步挂了就查哪一步,不用从头怀疑。
还有一个验证技巧:把每次任务的快照和提取结果都存一份到本地,按时间戳命名。这样当结果不对时,可以回看当时的页面结构,判断是选择器变了还是页面逻辑变了。OpenClaw 的日志目录通常在~/.openclaw/logs,可以配合jq过滤出浏览器相关的条目。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一章列几个真实会遇到的报错,以及对应的排查路径。这些报错我在不同环境里都踩过,按这个顺序查基本能定位。
401 Unauthorized。这个最直接,就是 key 不对或者没带上。先确认openclaw.json里的apiKey跟控制台生成的一致,注意有没有多余空格。然后确认请求头是Authorization: Bearer sk-xxx格式,不是x-api-key或者其他。如果你用的是环境变量注入,检查变量名有没有拼错,echo $TAOTOKEN_API_KEY看一下值对不对。还有一种情况是 key 被轮换了但配置没更新,去控制台的 API Keys 页面确认当前有效的 key。
local proxy failed。这个报错通常出现在 agent-browser 启动浏览器的时候,提示本地代理连接失败。原因是 OpenClaw 配置里可能残留了代理设置,或者系统环境变量里有HTTP_PROXY、HTTPS_PROXY指向了一个不可用的地址。排查方法:先env | grep -i proxy看有没有代理变量,有的话临时unset掉再启动。然后检查openclaw.json里有没有proxy字段,有的话删掉或者改成正确的地址。注意,这里说的是本地网络配置问题,不是让你去用什么特殊网络工具,只是把错误的代理配置清理掉。
reading choices 报错。这个通常出现在模型返回格式不符合预期的时候,比如choices字段为空或者结构不对。先确认你调的模型 ID 是 chat 类型的,不是 embedding 或者别的类型。然后看返回的原始 JSON,curl命令加-v看完整响应。如果返回的是错误信息而不是choices,那错误信息里通常有具体原因,比如model not found或者insufficient quota。还有一种情况是流式返回没处理完就解析了,检查你的客户端有没有正确处理stream: true的响应。
OAuth 相关报错。如果你用 Claude Code 或者某些需要 OAuth 授权的工具接入,可能会遇到 token 过期或者 scope 不对的问题。Claude Code 接入 TaoToken 的配置方式跟普通 API key 不同,需要走https://taotoken.net/claude-code-anthropic这个入口的说明。常见错误是invalid_grant,表示授权码已经用过或者过期了,重新走一遍授权流程。如果是invalid_scope,检查你申请的权限范围跟实际调用的是否匹配。
浏览器启动失败但没明显报错。这种情况先看~/.openclaw/logs/browser.log,里面通常有 Chromium 的 stderr 输出。常见原因:userDataDir被另一个进程占用(比如你已经开了一个同 profile 的浏览器),杀掉进程或者换个 profile;磁盘空间不足,Chromium 启动需要写临时文件;缺少系统依赖库,Linux 上跑ldd检查一下 Chromium 二进制的依赖。
快照拿不到元素。页面明明有那个按钮,但snapshot里找不到。先确认页面是不是在 iframe 里,agent-browser 默认只拿主文档的快照,iframe 里的内容需要单独处理。然后确认元素是不是在 shadow DOM 里,aria 快照对 shadow DOM 的支持有限,可能需要用browser.evaluate执行 JS 直接查询。还有一种情况是元素动态加载,快照拿早了,加一个wait步骤。
提取结果字段缺失。browser.extract返回的对象里某些字段是 null。检查fields数组里的字段名跟页面上的实际属性是否一致,大小写敏感。如果页面用的是data-*属性,选择器要写成[data-order-id]这种形式。另外,如果字段值是异步填充的,提取之前先wait一下对应的元素。
排查的核心思路是:先确认凭证通道(401 类),再确认浏览器进程(proxy/启动类),再确认页面状态(快照/提取类),最后确认模型响应(choices/OAuth 类)。按这个顺序,大部分问题能在几分钟内定位。
6. 把任务流固化下来:从一次性脚本到可复用能力
走到这里,你已经有了一个能跑的浏览器任务流。但“能跑”和“可复现”之间还有一段距离。可复现意味着换一台机器、换一个时间点,同样的配置和命令能产出同样的结果。要做到这一点,需要把几个东西固化下来。
第一,把配置模板化。openclaw.json里的 key 不要写死,用环境变量占位。OpenClaw 支持${VAR}语法,启动时从环境注入。这样配置文件可以进版本控制,key 留在本地或者密钥管理服务里。TaoToken 的 key 在控制台可以生成多个,给不同环境用不同的 key,方便审计和轮换。
第二,把任务定义版本化。前面那个fetch-order-list的 JSON,存到项目的tasks/目录下,跟代码一起提交。每次修改任务步骤都留 commit 记录,出问题可以回滚。任务里的选择器尽量用稳定的属性,比如data-testid或者name,避免用会变的 class 名。
第三,把验证步骤自动化。前面手动跑的curl和openclaw browser status,可以写成一个verify.sh脚本,每次部署后跑一遍。脚本里检查关键返回值,不通过就退出非零码,接入 CI 流程。这样配置漂移能第一时间发现。
第四,把日志和快照归档。每次任务执行,把 aria 快照、截图、提取结果按任务名/时间戳/的目录结构存下来。数据量不大,但排查问题时非常有用。可以写一个清理脚本,只保留最近 30 天的记录。
第五,考虑用 Coding Plan 来管理长期运行的编码和 Agent 任务。如果你的浏览器自动化任务是持续跑的,比如每天定时抓取,那用 Coding Plan 的额度管理会比按次调用更划算。入口在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite,具体额度规则看页面说明。
最后说一个实用技巧:给每个浏览器任务加一个“健康检查”步骤。任务开始前,先打开一个已知的测试页面,拿一次快照,确认浏览器和模型通道都正常,再执行正式步骤。这样如果环境有问题,会在健康检查阶段就失败,不会跑到一半才报错,浪费时间和配额。
这套流程跑顺之后,你会发现浏览器自动化的瓶颈往往不在技术本身,而在任务定义的清晰度。把“让 AI 去抓一下那个页面”这种模糊指令,拆成明确的步骤、选择器、等待条件和预期输出,才是可复现的关键。配置和命令只是把这个定义落到实处的工具。