Install
openclaw skills install @yicko/playwright-browser-use浏览器自动化(Playwright 版):纯 Node.js + Playwright 实现,完全独立运行。支持打开网页、截图、点击、填表、翻页等全部浏览器自动化能力。
openclaw skills install @yicko/playwright-browser-usepw-browser)
pw-browser是基于 Playwright 的浏览器自动化 CLI。无需 DuMate,仅依赖 Node.js 和 Playwright(已全局安装)。
本 skill 是 dumate-browser-use 的完全替代品,实现相同的语义能力,但不依赖 DuMate 环境:
| 能力 | dumate-browser-use | playwright-browser-use(本 skill) |
|---|---|---|
| 浏览器引擎 | DuMate Extension / headed 浏览器 | Playwright Chromium(headless=false) |
| 依赖 | DuMate 宿主 + Extension | Node.js + Playwright(npx playwright) |
| init | 需要 DuMate agent 进程 | 启动本地 daemon |
| snap | YAML 可访问性快照 | YAML 可访问性快照 |
| click/fill/hover 等 | ✅ | ✅ |
| run-code / eval | ✅ | ✅ |
| 翻页策略 | ✅ | ✅ |
| RTE/SPA 编辑 | ✅ | ✅ |
进入 Skill 目录,安装依赖:
cd <skill-directory>
npm install
无需单独下载浏览器 —— daemon 启动时自动检测并使用系统的 Chrome 或 Edge(通过 Playwright 的
channel机制)。
Skill 目录路径取决于安装方式(通过 WorkBuddy 设置中的 "导入 Skill" 安装)。安装后,<skill-directory> 即为目标目录。
pw-browser 采用 daemon + client 架构:
┌──────────────┐ HTTP (localhost:19223) ┌──────────────┐
│ pw-browser │ ──────────────────────────────→│ Daemon │
│ (CLI 客户端) │ │ (浏览器进程) │
└──────────────┘ └──────┬───────┘
│
├─ Playwright
├─ Chromium 浏览器
└─ 页面状态持久化
daemon 启动后持续运行,浏览器和页面状态跨命令保持。CLI 每次通过 HTTP 调用 daemon。
每次会话开始前,在后台启动 daemon:
SKILL_DIR="<skill-directory>"
NODE_PATH="${SKILL_DIR}/node_modules" node "${SKILL_DIR}/pw-browser.js" daemon &
sleep 4
daemon 在
127.0.0.1:19223监听,首次启动会用 Playwright 的channel: 'chrome'自动连接系统 Chrome(如已安装 Edge 也会尝试)。无需下载额外的 Chromium。
验证 daemon 可用:
SKILL_DIR="<skill-directory>"
NODE_PATH="${SKILL_DIR}/node_modules" node "${SKILL_DIR}/pw-browser.js" init
关闭 daemon:
pw-browser close --all
注意:所有
pw-browser命令都需要设置NODE_PATH。Agent 执行时应使用:bash SKILL_DIR="<skill-directory>" NODE_PATH="${SKILL_DIR}/node_modules" node "${SKILL_DIR}/pw-browser.js" <cmd> [args] [--json]为简洁起见,下文示例省略前缀,用
pw-browser表示。
# 1. 启动 daemon(会话开始一次)
pw-browser daemon &
# 2. 打开页面
pw-browser open https://www.baidu.com
# 3. 获取页面快照(必须!每次交互前都要 snap)
pw-browser snap
# 4. 交互 — 基于快照中的 e0, e1, e2... ref 引用
pw-browser click e8 # 点击 ref=e8 的元素
pw-browser fill e5 "hello" # 在 ref=e5 的输入框填入文本
pw-browser press Enter # 键盘按键
# 5. 等待
pw-browser wait-for "text=加载完成" --timeout 8000
pw-browser wait-for "url:https://example.com/*"
pw-browser wait-for "state:networkidle"
# 6. Tab 管理
pw-browser tab list
pw-browser tab select 1
pw-browser tab close 0
# 7. 关闭
pw-browser close # 关闭当前页面
pw-browser close --all # 关闭浏览器 + daemon
CLI 不会在 open/click 后自动获取快照。每次交互前,Agent 必须主动执行 pw-browser snap,基于最新快照选择 ref。
正确: pw-browser open URL → pw-browser snap → pw-browser click e5
错误: pw-browser open URL → pw-browser click e5(缺少 snap)
点击可能触发导航的链接(<a> 标签、按钮等)后:
pw-browser snap — 检查页面是否已变化pw-browser tab list → pw-browser tab select <idx>pw-browser snap — 获取新页面内容如果快照中元素不全(列表不完整等):
pw-browser mousewheel 0 500 滚动pw-browser snap从 snap 判断页面需要登录时:
pw-browser snap 继续与 dumate 不同,pw-browser 没有 wait-login 阻断机制。利用 Playwright 的非 headless 模式,用户可以直观操作浏览器。
点击导致新 tab 时用 tab list/select/close 处理。没有 tab new 命令。
涉及翻页、统计、收集、遍历时,参考下面的"分页策略"章节。
快照通过 pw-browser snap 命令获取,不要直接读 ~/.pw-browser/snap.yml。
遇到知识库、文档系统、CMS 等 SPA 页面,参考下面的"SPA 与富文本编辑器"章节。
如果 CLI 返回连接错误:
# 杀掉旧 daemon(端口 19223)
powershell -Command "Get-NetTCPConnection -LocalPort 19223 -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id \$_.OwningProcess -Force }" 2>/dev/null
fuser -k 19223/tcp 2>/dev/null
rm -rf ~/.pw-browser/daemon.json
# 重新启动
SKILL_DIR="<skill-directory>"
NODE_PATH="${SKILL_DIR}/node_modules" node "${SKILL_DIR}/pw-browser.js" daemon &
sleep 4
| 命令 | 说明 |
|---|---|
pw-browser init | 连接 daemon,确认浏览器可用 |
pw-browser open <url> | 导航到 URL |
pw-browser close | 关闭当前页面 |
pw-browser close --all | 关闭浏览器 + daemon |
pw-browser recover | 重启浏览器连接 |
| 命令 | 说明 |
|---|---|
pw-browser snap | 获取页面可访问性快照(YAML 格式,含 ref 引用) |
pw-browser wait-for <target> [--timeout ms] | 等待条件满足 |
| 命令 | 说明 |
|---|---|
pw-browser click <ref> | 点击元素 |
pw-browser fill <ref> "text" | 填入文本 |
pw-browser type "text" | 键盘输入 |
pw-browser press <key> | 按下按键(Enter, Escape, Tab 等) |
pw-browser hover <ref> | 悬停 |
pw-browser select <ref> <option> | 选择下拉选项 |
pw-browser check <ref> | 勾选复选框 |
pw-browser uncheck <ref> | 取消勾选 |
| 命令 | 说明 |
|---|---|
pw-browser goto <url> | 同 open |
pw-browser go-back | 后退 |
pw-browser go-forward | 前进 |
pw-browser reload | 刷新 |
| 命令 | 说明 |
|---|---|
pw-browser screenshot [ref] [--path file] | 截图 |
pw-browser mousewheel <dx> <dy> | 滚动 |
pw-browser eval "<expr>" [ref] | 执行 JS 表达式 |
pw-browser run-code "<code>" | 执行任意 Playwright 代码 |
pw-browser dialog-accept [text] | 确认对话框 |
pw-browser dialog-dismiss | 取消对话框 |
| 命令 | 说明 |
|---|---|
pw-browser tab list | 列出所有 tab |
pw-browser tab select <idx> | 切换到指定 tab (0-based) |
pw-browser tab close <idx> | 关闭指定 tab |
| 命令 | 说明 |
|---|---|
pw-browser sleep <seconds> | 等待 N 秒 |
wait-for 支持多种目标格式:
# 等待 URL 匹配
pw-browser wait-for "url:**/dashboard"
# 等待文本出现
pw-browser wait-for "text=加载完成"
# 等待页面加载状态(load / domcontentloaded / networkidle)
pw-browser wait-for "state:networkidle"
# 等待 CSS 选择器
pw-browser wait-for ".result-list" --timeout 15000
run-code)当内置命令不够用时,用 run-code 执行任意 Playwright 代码:
# 获取页面标题
pw-browser run-code "return await page.title();"
# 获取页面 HTML
pw-browser run-code "return await page.content();"
# 在页面中执行 JS
pw-browser run-code "return await page.evaluate(() => document.title);"
# 等待网络空闲
pw-browser run-code "await page.waitForLoadState('networkidle');"
# 复杂场景:提取列表数据
pw-browser run-code "
const items = await page.locator('.product-item').all();
const results = [];
for (const item of items) {
results.push({
title: await item.locator('.title').textContent(),
price: await item.locator('.price').textContent()
});
}
return JSON.stringify(results);
"
注意:
run-code 中直接使用 Playwright Page APIpage 对象已注入从 snap 判断:
| 类型 | 识别信号 | 翻页方式 |
|---|---|---|
| 页码分页 | 底部有 1/2/3...页码、"下一页"/"Next"/">" | 点击页码或"下一页" |
| 无限滚动 | 底部无分页控件,内容随滚动增加 | mousewheel 滚动 |
| 加载更多 | 底部有"加载更多"/"查看更多" | 点击该按钮 |
页码分页:
pw-browser snap # 找到"下一页"按钮的 ref
pw-browser click e42 # 点击
pw-browser sleep 2 && pw-browser snap # 验证
无限滚动:
pw-browser mousewheel 0 800
pw-browser sleep 2 && pw-browser snap
加载更多按钮:
pw-browser click <ref>
pw-browser sleep 2 && pw-browser snap
| 方式 | 成功信号 | 失败/结束信号 |
|---|---|---|
| 页码 | 内容更新,URL 变化 | "下一页"按钮 disabled 或消失 |
| 滚动 | 内容增加,新元素出现 | 内容不变,"没有更多了" |
| 按钮 | 新内容加载,按钮仍可点击 | "已加载全部",按钮消失 |
处理知识库、文档系统、CMS 等 SPA 页面的编辑操作:
contenteditable、编辑器 toolbarinput/textarea,而是复杂编辑器pw-browser click <编辑按钮的ref>pw-browser snap — 检查是否出现"更新"/"保存"按钮pw-browser run-code "
const editor = page.locator('[contenteditable=\"true\"]').first();
await editor.click();
await page.keyboard.press('Control+A');
await page.keyboard.type('要写入的内容');
await page.waitForTimeout(500);
"
pw-browser run-code "
await page.evaluate(() => {
const btn = Array.from(document.querySelectorAll('button'))
.find(b => ['更新','保存','发布'].includes(b.textContent.trim()));
btn?.click();
});
await page.waitForTimeout(3000);
"
pw-browser snap — 确认保存成功、内容正确不要直接用
innerText/textContent修改 RTE 内容。Playwright 的keyboard.type和fill是正确方式。
所有命令在 daemon 端返回 JSON:
{"ok": true, "data": {...}, "elapsedMs": 123}
{"ok": false, "error": {"kind": "ElementNotFound", "message": "..."}, "elapsedMs": 50}
CLI 客户端默认以人类可读格式输出;加 --json 标志输出原始 JSON。
| 错误类型 | 原因 | 处理 |
|---|---|---|
ElementNotFound | snap 后 ref 已失效 | 重新 snap 获取新 ref |
NavigationTimeout | 页面加载超时 | 先 snap 检查实际状态 |
| 连接拒绝 | daemon 未运行 | 重新启动 daemon |
| 空快照 | 页面未加载完成 | wait-for state:load 后重新 snap |
SKILL_DIR="<skill-directory>"
export NODE_PATH="${SKILL_DIR}/node_modules"
# 启动 daemon(首次)
node "${SKILL_DIR}/pw-browser.js" daemon &
sleep 4
# 打开百度
node "${SKILL_DIR}/pw-browser.js" open https://www.baidu.com
# 快照 → 找到搜索框和按钮的 ref
node "${SKILL_DIR}/pw-browser.js" snap
# 例如:e12 = textarea(搜索框),e13 = button(百度一下)
# 填搜索关键词
node "${SKILL_DIR}/pw-browser.js" fill e12 "天气预报"
# 点击搜索
node "${SKILL_DIR}/pw-browser.js" click e13
sleep 2
# 检查搜索结果
node "${SKILL_DIR}/pw-browser.js" snap | head -30
# 清理
node "${SKILL_DIR}/pw-browser.js" close --all
| 场景 | 文件 |
|---|---|
| 翻页策略详解 | references/pagination.md |
| 富文本编辑器策略 | references/rich-text-editor.md |
| 运行自定义代码 | references/running-code.md |