playwright-browser
September 21, 2026 · View on GitHub
DSH (DeepSeek Harness) 浏览器自动化插件:给智能体提供一套 browser_* 模型工具,用 Playwright 驱动 Chromium 真实操作网页——打开页面、点击、填表、抓取 DOM、截图。
兼容性:对 dsh 0.1.2-alpha.5 实测通过。
- 宿主进程内直接
require('playwright-core'),自建模式下无需外部桥服务或端口 - 浏览器按需懒启动,插件卸载时自动关闭
- 可选挂载已经打开的浏览器(CDP attach):直接接管你正在用的 Chrome/Edge,登录态、Cookie、扩展、已开标签页全都在(见下文)
- 只依赖
playwright-core,无其他运行时依赖 - 工具描述/参数文档/输出文案支持中英双语(
PW_LANG=en切换,默认中文)— English README
功能一览
| 工具 | 作用 |
|---|---|
browser_open | 打开 URL,返回最终地址与页面标题 |
browser_status | 查询浏览器/当前页面状态(是否挂载、端点、URL、标题、标签页数) |
browser_attach | 运行时挂载/切换到一个已在运行的浏览器(无需重启 DSH) |
browser_tabs | 列出/切换/关闭已打开的标签页(挂载模式下的"接手"入口) |
browser_click | 点击元素(CSS 或 text= 选择器) |
browser_type | 逐字输入(可设 delay 模拟真人) |
browser_fill | 快速填充输入框 |
browser_press | 按键(Enter / Tab / Escape …) |
browser_wait | 等待若干毫秒 |
browser_extract | 抓取页面或指定元素的文本 |
browser_html | 抓取页面或指定元素的 HTML |
browser_eval | 在页面上下文执行 JS 表达式(诊断 DOM 等) |
browser_screenshot | 截图保存为 PNG,返回绝对路径 |
browser_close | 关闭浏览器释放资源(挂载模式下只断开连接) |
安装到 DSH profile
在任意目录执行一条命令(dsh 自行定位 profile 目录,首次使用会自动初始化):
# 示例:装进 web profile
dsh plugin --profile web add git+https://github.com/whklwhkl/dsh-playwright.git
dsh plugin 把 add 之后的参数原样转发给 profile 目录里的 pnpm,安装完成后自动把声明了 dsh.bundle 的依赖追加进 dsh.profile.bundles——无需手改 package.json。registry 包名、github:<user>/<repo>、本地路径等 pnpm 支持的安装源均可。
本地开发时指向 checkout 目录,加 link: 前缀以符号链接安装,改动后无需重新安装(重启 DSH 生效):
dsh plugin --profile web add link:/path/to/dsh-playwright
重启 DSH: bundle 列表在启动时读取,重启后 browser_* 工具对 profile 下所有会话自动可用。
旧版 dsh 没有
plugin子命令时:手动把playwright-browser加入 profilepackage.json的 dependencies,并追加进dsh.profile.bundles,再在 profile 目录执行pnpm install。
准备浏览器
playwright-core 不会自动下载浏览器,首次使用前需要准备 Chromium,二选一:
方式 A:让 playwright-core 自动下载(推荐,零配置)
# 任选其一(等价):
npx playwright-core install chromium
# 或安装完整版 playwright 借其下载器:
npm i -D playwright && npx playwright install chromium
下载的浏览器会进入系统标准缓存(macOS 为 ~/Library/Caches/ms-playwright,Linux 为 ~/.cache/ms-playwright),插件启动时自动发现。
方式 B:复用系统已有的 Chrome/Edge/Chromium(免下载)
给运行 DSH 的进程设置环境变量,指向任意现成浏览器可执行文件:
export PW_CHROMIUM_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
# Linux 示例:export PW_CHROMIUM_PATH="/usr/bin/google-chrome"
版本提示:自动发现依赖 playwright-core 与其期望的 Chromium build 号匹配(
npx playwright-core install chromium总是安装匹配版本)。用PW_CHROMIUM_PATH指向任意 Chromium 系浏览器则无版本要求。
方式 C:挂载已经打开的浏览器(复用登录态)
上面 A/B 两种方式都是让插件新起一个干净浏览器——没有登录态、没有扩展、没有你手动打开的标签页。若要让智能体直接在你正在使用的 Chrome 里干活,用挂载模式:
export PW_CDP_ENDPOINT=chrome # 真实默认 profile,推荐
# 或 export PW_CDP_ENDPOINT=http://127.0.0.1:9222 # 指定调试端口
# 或 export PW_CDP_ENDPOINT=auto # 先试 chrome,再试 9222
chrome 这个取法要求你在目标浏览器里开启一次远程调试开关(Chrome 136+ 的安全策略,默认关闭):
- 地址栏打开
chrome://inspect/#remote-debugging - 勾选 Allow remote debugging for this browser instance
之后插件即可挂载你的默认 profile:Cookie、登录态、扩展、已打开的标签页全都在。
想改用端口方式(http://127.0.0.1:9222)时注意:Chrome 136 起 --remote-debugging-port 对默认数据目录不再生效,必须同时指定一个专属目录,并且该 Chrome 需先完全退出:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-debug"
两种取法不能混用(实测):用
chrome://inspect开关启动的浏览器只提供 WebSocket 端点,不响应 HTTP 发现接口(http://127.0.0.1:9222/json/version返回 404),所以对这类实例只能写 channel 名chrome;反过来,用--remote-debugging-port起的实例两种写法都行。
不想重启 DSH 也能挂载: 直接让智能体调用 browser_attach(也可以自己说"挂上我的 Chrome")。它接受与 PW_CDP_ENDPOINT 相同的写法,并且:
| 调用 | 作用 |
|---|---|
browser_attach | 缺省端点:已挂载就保持当前端点,否则用 PW_CDP_ENDPOINT(未设置则 auto) |
browser_attach({ endpoint: "chrome" }) | 挂到该端点(channel 名 / http://… / ws://… / auto / 逗号分隔回退) |
browser_attach({ page: "portal" }) | 不换浏览器,只按 URL/标题子串换要接手的标签页;命中不到会报错 |
browser_attach({ endpoint: "launch" }) | 不再挂载,切回插件自建的干净浏览器(忽略环境变量) |
挂载模式的行为约定:
browser_status会顺带建立连接,先告诉你连上了哪个端点、哪个页面、共几个标签页;browser_tabs列出全部标签页(*= 当前操作页),传index或url子串即可切换(会置前),加close:true关闭该标签页;browser_open在当前跟踪的标签页里导航——别拿它去覆盖你不想丢的页面,可以先browser_tabs选一个;browser_close只断开连接,绝不关闭你的浏览器(自建实例模式下才是真的关闭);PW_CDP_PAGE可按 URL/标题子串固定要接手的标签页;- 端点连不上时会在
PW_CDP_TIMEOUT(默认 15 秒)内快速失败并列出试过的端点,不会把一次工具调用挂死; - 该模式下
PW_HEADLESS、PW_CHROMIUM_PATH无效(浏览器是你自己起的)。
与 webclaw3 的区别:webclaw3 走 Chrome 扩展桥接 + 本地服务,本插件走 Playwright 原生 CDP 挂载,不需要装扩展;代价是 CDP 看不到
chrome://等特权页面(扩展桥接可以)。
配置(环境变量)
| 变量 | 默认 | 说明 |
|---|---|---|
PW_LANG | zh | 设为 en 切换工具描述与输出为英文 |
PW_CHROMIUM_PATH | 自动发现 | 复用指定浏览器可执行文件 |
PW_HEADLESS | true | 设为 false 弹出可见窗口(挂载模式下无效) |
PW_SHOT_DIR | 插件目录下 shots/ | 截图保存目录 |
PW_CDP_ENDPOINT | 未设置 | 设置后进入挂载模式:chrome / msedge 等 channel 名、http://127.0.0.1:9222、ws://…,逗号分隔可做回退,auto = 先 chrome 再 9222 |
PW_CDP_PAGE | 未设置 | 挂载时按 URL 或标题子串挑选要接手的标签页 |
PW_CDP_TIMEOUT | 15000 | 挂载连接超时毫秒;端点写错时据此快速失败 |
内置 skill:playwright-browser-tips
bundle 同时携带一个 playwright-browser-tips skill,正文是一张站点地图:各站点在自动化下的实测行为与对策(搜索类任务默认 Bing),加上通用恢复手法。中英双语跟随 PW_LANG;模型在 browser_* 工具失败或自动化搜索/登录流程时按需加载,用户也可以直接输入 /playwright-browser-tips 调用。
地图欢迎共建——人和 agent 都可以按 SITE-MAP-SPEC.md 的规范提交条目(只改 assets/site-map.json),提交前运行 node scripts/validate-site-map.js 并把输出贴进 PR。框架文本(含反自动化边界:验证码一律由用户人工完成)由代码持有,不随地图数据变化。
skill 需要带 skill 注册表的 profile——web、headless、acp、sdk-app 等基于 dsh-base 的 profile 均满足。同名项目或用户目录 skill 优先级更高,可本地覆盖插件内置版本。
使用示例(对智能体说的话)
- "用浏览器打开 https://example.com,抓取正文给我"
- "打开百度,搜索「playwright」,把第一条结果标题告诉我"
- "打开这个页面 https://…,点击「登录」,截个图"
- "挂上我的 Chrome,把当前标签页的正文抓下来"(智能体调用
browser_attach,或设PW_CDP_ENDPOINT) - "看看我浏览器里都开了哪些标签页,切到那个 XXX 页面然后点登录"(同上)
故障排查
| 现象 | 处理 |
|---|---|
Executable doesn't exist ... ms-playwright | 浏览器未下载,执行 npx playwright-core install chromium |
| 下载 Chromium 时连接中断/超时(代理环境常见) | 大文件经代理易被中断;改用方式 B 的 PW_CHROMIUM_PATH 指向系统 Chrome,免下载 |
Could not connect to chrome: DevToolsActivePort file not found | 目标浏览器没开远程调试:去 chrome://inspect/#remote-debugging 勾选允许,或改用 PW_CDP_ENDPOINT=http://127.0.0.1:9222 并以专属 --user-data-dir 启动 Chrome |
http://127.0.0.1:9222 报 Unexpected status 404 ... /json/version/ | 该端口上的浏览器是用 chrome://inspect 开关开的:它不提供 HTTP 发现接口,改用 channel 名 chrome(或 browser_attach({endpoint:"chrome"})) |
connect ECONNREFUSED 127.0.0.1:9222 | 目标浏览器没起来或端口不对:确认它带 --remote-debugging-port=9222 启动,且 curl http://127.0.0.1:9222/json/version 有返回 |
CDP 挂载失败(已尝试 N 个端点) | 端点不对或浏览器没开调试;报错里会逐个列出每个端点失败的原因,15 秒内返回,不会挂住 |
挂载模式下 browser_open 覆盖了我正在看的页面 | 正常现象——它导航的是"当前跟踪的标签页";先 browser_tabs 切到目标页,或用 PW_CDP_PAGE 固定 |
net::ERR_CONNECTION_CLOSED | 目标站点网络问题或反爬,换个站点/稍后重试 |
| 站点弹验证码(如百度滑块)、headless 下输入框不可见 | 反自动化机制,非插件问题;实测 Bing 全流程可用,可优先换 Bing,或设 PW_HEADLESS=false 用有头模式 |
| 元素"not visible" | 页面改版或选择器过时,用 browser_eval 检查 DOM 再选选择器 |
| 当前模型看不了截图 | browser_screenshot 只保存文件;需要支持图片输入的视觉模型才能"看"图 |
本地开发 / 快速自测
# 不启动 DSH,直接验证模块加载与工具注册:
node --input-type=module -e "
import { apply } from './lib/index.js'
const tools = []
apply({ tools: { register: (d) => tools.push(d) }, effect: () => () => {} })
console.log(tools.map((t) => t.name).join('\n'))
"
# 挂载模式冒烟测试(另开一个终端,先按"方式 C"起好可调试的 Chrome):
# 不设 PW_CDP_ENDPOINT 也行——browser_attach 可以运行时指定端点
node --input-type=module -e "
const { apply } = await import('./lib/index.js')
const m = new Map()
apply({ tools: { register: (d) => m.set(d.name, d) }, effect: () => () => {} })
const call = (n, a = {}) => m.get('browser_' + n).execute(a)
console.log(await call('attach', { endpoint: 'http://127.0.0.1:9222' })) // 挂载
console.log(await call('tabs')) // 列出现有标签页
console.log(await call('attach', { endpoint: 'launch' })) // 切回自建实例
console.log(await call('open', { url: 'https://example.com' }))
console.log(await call('close')) // 关闭自建实例
"
社区与支持
本插件是 DeepSeek Harness(DSH)生态插件,按官方社区支持指南添加了 dsh-plugin topic 以便被发现。
- 插件问题、功能建议:在本仓库提 Issues 或 Discussions
- DSH 框架问题与反馈:提交到 DeepSeek Harness Discussions
- 加入 DeepSeek Harness Discord 社区(见官方 README)
License
MIT