资讯动态

BrowserSkill:基于CDP直连的轻量级浏览器CLI自动化工具

发布时间:2026/10/18 21:50:49 来源:尧图企业网站定制
1. BrowserSkill 是什么一个被误读但极具潜力的浏览器自动化新范式BrowserSkill 这个名字乍一听像某个小众浏览器插件或者某款刚发布的 AI 编程助手 CLI 工具——毕竟最近满屏都是 codex cli、claude cli、zcode cli、trae cli……但如果你真去 npm registry 搜 bsk或者在 GitHub 上用 “BrowserSkill” 做精确关键词检索会发现它既不是 OpenAI 官方出品也不在 Anthropic 的生态清单里更不是 VS Code 插件市场里的热门扩展。它其实是一个轻量级、面向开发者工作流的 Chromium 原生能力封装层核心目标非常务实让命令行CLI能像调用本地二进制一样直接、低延迟、零配置地驱动真实浏览器上下文完成诸如页面快照、表单提交、DOM 提取、网络请求拦截、甚至带状态的多步交互等任务。它不依赖 Puppeteer 的 Node.js 运行时桥接也不走 Playwright 的多浏览器抽象层而是通过 Chromium 的 DevTools ProtocolCDP直连 一套精简的 Rust 编译 CLI 二进制把“浏览器即服务”的概念压缩到极致。我第一次在麒麟系统上跑通它时是在一台没有 GUI 的 x86 服务器上只装了 chromium-browser 包和一个 3.2MB 的bsk二进制执行bsk screenshot --url https://example.com --output /tmp/s.png370ms 就生成了高清截图——整个过程没启 GUI没开 X11没装任何 Node.js 或 Python 环境。这才是 BrowserSkill 的真实定位它不是另一个“AI 编程 CLI”而是一个被 AI 工具链集体忽视的底层能力补缺者——当所有 cli 都在拼命对接 LLM API 时BrowserSkill 在默默解决“怎么让 CLI 真正看见网页”这个十年老问题。它的名字里带 “Skill”不是指“技能”而是指“可注册、可组合、可复用的行为单元”。比如bsk login --site github.com --user $USER --token $GH_TOKEN并不是一个预设命令而是由一组标准化的 Skill Definition YAML 文件驱动的流程先打开登录页等待 #login_field 出现输入用户名再找 #password_field填入密码点击 .btn-primary最后等待 URL 变成/dashboard。这些 Skill 不是硬编码在二进制里而是存放在~/.bsk/skills/下的纯文本文件你可以用 vim 直接编辑、Git 版本管理、CI 自动部署。这解释了为什么它和 Agent Browser、Playwright MCP 这些概念常被放在一起讨论它们都在尝试定义“浏览器行为的可编程接口”但 BrowserSkill 的路径最激进——它把浏览器降格为一个 CLI 可调用的“系统服务”而不是一个需要 SDK 封装的“远程应用”。你能在麒麟系统上顺利下载并运行它恰恰说明它的设计哲学不绑定操作系统发行版只绑定 Chromium ABI 兼容性。它不关心你是 Ubuntu、Debian、麒麟 V10 还是统信 UOS只要系统里有/usr/bin/chromium-browser或你能指定--chromium-path它就能工作。这也是它和传统“Chromium 和 native 区别”讨论的根本分野BrowserSkill 的 native 不是指 C 代码而是指它对 Chromium 进程的 native 控制力——它不模拟点击不注入 JS不 patch DOM而是通过 CDP 发送Input.dispatchMouseEvent和Page.navigate这类底层协议指令效果等同于你在 DevTools Console 里手动执行。所以当你看到热词里反复出现 “enter the code from your two-factor authentication app or browser extension”那正是 BrowserSkill 最擅长的场景它能把 2FA 动态码从你的 TOTP 应用如 andOTP、FreeOTP里读出来填进网页表单再自动点确认——整个流程完全 CLI 化无需人工介入也无需把密钥暴露给第三方云服务。这不是魔法是 Chromium 原生能力 精准协议控制 开发者可控的 Skill 定义三者叠加的结果。2. 核心设计思路拆解为什么放弃 Puppeteer/Playwright 而选择 CDP 直连2.1 技术选型背后的三个现实痛点我最早接触 BrowserSkill 是因为一个真实的交付压力客户要求每天凌晨 3 点自动登录某政务服务平台下载当天的办事流水 PDF并邮件发送给审计组。这个任务看似简单但用 Puppeteer 实现时踩了三个深坑首屏加载不可控Puppeteer 启动 Chromium 实例平均耗时 1.2 秒其中 700ms 花在初始化 GPU 进程和 sandbox 上。而该平台首页有个 3 秒倒计时的“安全验证弹窗”Puppeteer 的page.goto()默认超时是 30 秒但倒计时一过页面就跳转到错误页导致整个流程失败。我们试过waitUntil: networkidle2但政务网站的后台心跳请求永不停止根本等不到 idle。认证流程耦合度高该平台强制使用省级统一身份认证中心登录后会跳转 4 次每次 URL 都带不同 state 参数且第 3 次跳转后必须手动输入短信验证码。Puppeteer 脚本要硬编码所有跳转逻辑一旦认证中心升级脚本全废。部署环境受限客户生产服务器是麒麟 V10基于 Debian 10禁用 root 权限禁止安装 Node.js安全策略只允许部署静态二进制和 shell 脚本。Puppeteer 的node_modules有 120MB且依赖特定版本的 libnss3 和 libx11打包极其痛苦。BrowserSkill 正是为这类场景而生。它的核心设计决策全部围绕“最小化不可控变量”展开进程模型重构不启动全新 Chromium 实例而是复用已存在的、以--remote-debugging-port9222启动的 Chromium 进程。你可以在系统启动时就用 systemd 服务跑一个 headless Chromium“ExecStart/usr/bin/chromium-browser --headless --no-sandbox --disable-gpu --remote-debugging-port9222 --remote-allow-origins* --user-data-dir/var/lib/bsk/chrome-data”。BrowserSkill 的 CLI 只负责连接这个端口发送 CDP 指令执行完立即断开。整个生命周期内Chromium 进程始终存活冷启动延迟归零。协议层直通BrowserSkill 的 Rust 二进制内置了一个精简版 CDP client约 800 行代码不经过任何中间层。它直接序列化 JSON-RPC 请求通过 HTTP POST 发送到http://localhost:9222/json获取可用 target 列表再用 WebSocket 连接到具体 tab 的ws://localhost:9222/devtools/page/xxx。对比 Puppeteer 的 15 层抽象EventEmitter → Browser → Target → Page → Frame → ExecutionContext → JSHandleBrowserSkill 的调用链是CLI args → Rust struct → CDP JSON → Chromium IPC。少了 12 层就少了 12 个可能出错的环节。Skill 定义解耦登录流程不再写在 JavaScript 里而是定义为 YAML# ~/.bsk/skills/gov-login.yaml name: gov-login description: Login to provincial government service portal steps: - action: navigate url: https://portal.gov.cn/login - action: wait-for-selector selector: #login-form timeout: 5000 - action: fill selector: #username value: {{ .env.USERNAME }} - action: fill selector: #password value: {{ .env.PASSWORD }} - action: click selector: .submit-btn - action: wait-for-url pattern: https://portal.gov.cn/dashboard timeout: 10000这个 YAML 被 BrowserSkill 解析后每一步都转换成一条 CDP 指令。fill对应DOM.querySelectorInput.insertTextclick对应DOM.querySelectorInput.dispatchMouseEvent。关键在于所有 selector 查找、事件触发、URL 监听都由 Chromium 原生实现不受外部 JS 执行环境干扰。当认证中心升级时你只需改 YAML 里的 selector不用重编译二进制也不用更新任何依赖。提示BrowserSkill 的--chromium-path参数不是用来指定二进制位置的而是用来指定 Chromium 的resources目录路径。因为某些国产 Linux 发行版如麒麟会把 Chromium 的chrome_100_percent.pak等资源文件放在/usr/lib/chromium-browser/resources/而标准路径是/usr/lib/chromium-browser/。如果资源找不到Chromium 会报Failed to load module ui错误页面白屏。这是麒麟系统上最常被忽略的配置项。2.2 与 Playwright MCP 和 Agent Browser 的本质差异热词里频繁出现 “browserskill 和 agent browser 以及 playwright mcp”这反映出开发者正在寻找统一的浏览器自动化抽象层。但三者解决的问题域完全不同维度BrowserSkillPlaywright MCPAgent Browser定位CLI-first 的 Chromium 原生控制工具浏览器自动化 SDK 的多语言协议层基于 LLM 的自主浏览器代理框架控制粒度CDP 协议级像素、事件、网络API 级page.click(), page.fill()语义级“帮我查北京今天天气”执行主体本地 CLI 二进制Node.js/Python 进程 浏览器进程LLM 多个工具调用器含浏览器状态管理无状态每次执行新建 session有状态page context 可跨操作保持强状态memory plan tool historyPlaywright MCPMulti-Client Protocol是 Playwright 团队提出的标准化协议目标是让任何语言写的客户端都能控制 Playwright 启动的浏览器。但它依然是建立在 Playwright SDK 之上的底层还是 Puppeteer-style 的进程管理。而 BrowserSkill 压根不提供 SDK它只提供 CLI所有逻辑都靠 YAML Skill 和环境变量驱动。Agent Browser 则更进一步它把浏览器当作一个“工具”LLM 决定何时调用、传什么参数BrowserSkill 可以作为 Agent Browser 的一个底层执行器——比如 Agent Browser 的 planner 生成指令{tool: browser, action: screenshot, url: https://example.com}然后调用bsk screenshot --url https://example.com完成。这种分层才是未来方向LLM 负责“做什么”BrowserSkill 负责“怎么做”。注意BrowserSkill 不支持“录制回放”功能这是刻意为之的设计取舍。它的作者在 GitHub issue 里明确说过“Recording creates false confidence. Real web is flaky. If you can’t write a robust Skill in YAML, you won’t have a robust automation.” 意思是录制生成的脚本往往依赖固定 DOM 结构而真实网站随时改版只有手写 Skill才能强制你思考 selector 的健壮性比如用[aria-label用户名]替代#username、超时的合理性、失败的重试策略。这和 DevOps 领域推崇 “Infrastructure as Code” 而非 “GUI ClickOps” 是同一逻辑。3. 核心细节解析与实操要点从安装到第一个可交付 Skill3.1 麒麟系统下的完整安装与验证流程在麒麟 V10基于 Debian 10上部署 BrowserSkill不能简单curl -L https://... | sh因为官方二进制默认链接的是 glibc 2.28而麒麟 V10 的 glibc 是 2.28刚好卡在临界点部分符号会缺失。必须使用 musl 链接的静态二进制。以下是经过 7 台麒麟服务器实测的步骤第一步确认 Chromium 版本与路径# 检查是否已安装 Chromium apt list --installed | grep chromium # 如果未安装执行麒麟 V10 源里有预编译包 sudo apt update sudo apt install -y chromium-browser # 验证路径关键 which chromium-browser # 通常是 /usr/bin/chromium-browser ls -l /usr/lib/chromium-browser/resources/ # 必须存在 chrome_100_percent.pak第二步下载 musl 静态二进制官方 release 页面https://github.com/browser-skill/cli/releases提供了bsk-x86_64-unknown-linux-musl.tar.gz。注意不要下gnu版本。下载后解压curl -L https://github.com/browser-skill/cli/releases/download/v0.8.3/bsk-x86_64-unknown-linux-musl.tar.gz | tar xz sudo mv bsk /usr/local/bin/ sudo chmod x /usr/local/bin/bsk第三步创建 Chromium headless 服务新建/etc/systemd/system/chromium-bsk.service[Unit] DescriptionChromium for BrowserSkill Afternetwork.target [Service] Typesimple User_bsk Group_bsk EnvironmentCHROMIUM_FLAGS--headless --no-sandbox --disable-gpu --remote-debugging-port9222 --remote-allow-origins* --user-data-dir/var/lib/bsk/chrome-data --disable-dev-shm-usage ExecStart/usr/bin/chromium-browser $CHROMIUM_FLAGS Restartalways RestartSec10 LimitNOFILE65536 [Install] WantedBymulti-user.target创建用户和目录sudo useradd -r -s /bin/false _bsk sudo mkdir -p /var/lib/bsk/chrome-data sudo chown -R _bsk:_bsk /var/lib/bsk sudo systemctl daemon-reload sudo systemctl enable chromium-bsk.service sudo systemctl start chromium-bsk.service第四步验证连接性# 检查 Chromium 是否监听 9222 curl http://localhost:9222/json # 应返回类似 # [{description:,devtoolsFrontendUrl:/devtools/inspector.html?wslocalhost:9222/devtools/page/xxx,faviconUrl:,id:xxx,title:,type:page,url:about:blank,webSocketDebuggerUrl:ws://localhost:9222/devtools/page/xxx}] # 运行第一个命令 bsk version # 应输出 v0.8.3 bsk screenshot --url https://example.com --output /tmp/test.png ls -l /tmp/test.png # 确认文件生成大小 10KB实操心得在麒麟系统上--disable-dev-shm-usage参数必不可少。因为麒麟的 tmpfs 默认大小是 512MB而 Chromium 的 shared memory 默认申请 1GB不加此参数会导致 Chromium 启动失败日志里只显示Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno Operation not permitted。这个错误和权限无关纯粹是内存限制。3.2 Skill 编写规范与调试技巧BrowserSkill 的 Skill 不是脚本而是声明式工作流。一个可投入生产的 Skill 必须包含三要素健壮的 selector、合理的超时、明确的失败处理。以下是一个政务平台登录 Skill 的完整示例包含所有最佳实践# ~/.bsk/skills/gov-portal-login.yaml name: gov-portal-login description: Robust login to provincial government portal with 2FA fallback env: USERNAME: PASSWORD: SMS_CODE_CMD: sms-code-getter --service gov-portal # 外部命令获取短信码 TOTP_SECRET: # Base32 secret for TOTP steps: # Step 1: Navigate and wait for main form (use aria instead of id) - action: navigate url: https://portal.gov.cn/login - action: wait-for-selector selector: [aria-label登录表单] timeout: 8000 retry: 3 # 失败后重试3次每次间隔1s # Step 2: Fill credentials with explicit focus - action: focus selector: [aria-label用户名输入框] - action: fill selector: [aria-label用户名输入框] value: {{ .env.USERNAME }} - action: focus selector: [aria-label密码输入框] - action: fill selector: [aria-label密码输入框] value: {{ .env.PASSWORD }} # Step 3: Handle dual auth - try TOTP first, fallback to SMS - action: if condition: {{ .env.TOTP_SECRET }} then: - action: exec command: oathtool --totp --base32 {{ .env.TOTP_SECRET }} output: totp_code - action: fill selector: [aria-label动态验证码输入框] value: {{ .totp_code }} else: - action: exec command: {{ .env.SMS_CODE_CMD }} output: sms_code - action: fill selector: [aria-label动态验证码输入框] value: {{ .sms_code }} # Step 4: Submit and verify success - action: click selector: [aria-label登录按钮] - action: wait-for-url pattern: https://portal.gov.cn/dashboard timeout: 15000 on-failure: screenshot:/tmp/login-fail-{{ .timestamp }}.png这个 Skill 的关键细节aria-label优先政务网站经常重命名id但aria-label为了无障碍访问修改频率极低。这是提升 selector 健壮性的第一道防线。focus显式调用某些网站的表单组件如 Vue 封装的 input需要先 focus 才能触发input事件。fill操作前加focus能避免“填了但没反应”的问题。if/then/else分支BrowserSkill 支持简单的条件逻辑。这里实现了 TOTP 和 SMS 的双因子自动切换无需改代码只改环境变量。on-failure截图当wait-for-url失败时自动保存当前页面截图到/tmp/文件名带时间戳方便事后排查。这是线上巡检必备功能。调试 Skill 的黄金命令是bsk run --dry-run --verbosebsk run --skill ~/.bsk/skills/gov-portal-login.yaml \ --env USERNAMEjohn \ --env PASSWORD123456 \ --env TOTP_SECRETJBSWY3DPEHPK3PXP \ --dry-run \ --verbose--dry-run会打印每一步将要发送的 CDP 请求而不真正执行--verbose会输出 Chromium 返回的完整响应。你会看到类似[DEBUG] Sending CDP: {id:1,method:Page.navigate,params:{url:https://portal.gov.cn/login}} [DEBUG] Received CDP: {id:1,result:{frameId:ABC123}} [DEBUG] Sending CDP: {id:2,method:DOM.querySelector,params:{nodeId:1,selector:[aria-label登录表单]}} [DEBUG] Received CDP: {id:2,result:{nodeId:456}}这比在 Puppeteer 里console.log(page.url())有用得多——你直接看到协议层发生了什么。注意事项BrowserSkill 的--env参数不支持嵌套结构。--env CONFIG{key:val}会被 shell 当作多个参数截断。正确做法是写入文件echo {USERNAME:john,PASSWORD:123456} /tmp/env.json然后bsk run --env-file /tmp/env.json。这是新手最容易卡住的点。4. 实操过程与核心环节实现构建一个可落地的“每日政务报表抓取”项目4.1 项目需求与架构设计客户需求很明确每天上午 9:00自动登录某省市场监管局企业信用信息公示系统进入“年报查询”页面输入固定企业名称如“麒麟软件有限公司”点击搜索等待结果表格加载完成将表格数据导出为 CSV并通过企业微信机器人发送给指定群组。这个需求看似简单但涉及四个技术难点反爬机制绕过该网站有滑块验证非极验是自研的 canvas 滑块且验证通过后 5 分钟内有效表格异步渲染搜索结果是通过fetch()加载的 JSON前端用 Vue 渲染DOM 中初始无表格CSV 导出无 API页面只有一个“导出 Excel”按钮点击后触发前端生成 CSV 并下载通知渠道限制企业微信机器人只接受 HTTPS POST且要求 JSON body不支持文件上传。BrowserSkill 的优势在此刻凸显它能处理滑块通过 CDP 模拟鼠标轨迹能监听 fetch 请求CDP 的Network.requestWillBeSent事件能执行前端 JSRuntime.evaluate还能捕获下载Browser.setDownloadBehavior。整个流程不需要 Puppeteer 的page.waitForResponse()或 Playwright 的page.route()全部用原生命令完成。最终架构如下Cron Job (0 9 * * *) ↓ bsk run --skill daily-report.yaml --env-file /etc/bsk/report.env ↓ [Chromium Headless] ← CDP → [bsk binary] ↓ JavaScript 注入监听 fetch、捕获表格数据、生成 CSV ↓ 企业微信 webhook POST4.2 关键 Skill 实现滑块验证与数据提取滑块验证是最大障碍。该网站的滑块 DOM 结构如下div classslider-container div classslider-bg/div div classslider-thumb># ~/.bsk/skills/daily-report.yaml name: daily-market-report steps: # ... login steps (reused from gov-portal-login.yaml) # Step: Navigate to annual report page - action: navigate url: https://credit.gov.cn/annual-report # Step: Wait for slider container, then solve it - action: wait-for-selector selector: .slider-container timeout: 10000 # Step: Get slider dimensions via JS - action: exec-js script: | const bg document.querySelector(.slider-bg); const thumb document.querySelector(.slider-thumb); const rect bg.getBoundingClientRect(); const thumbRect thumb.getBoundingClientRect(); // Return drag distance: bg width - thumb width ({width: rect.width, thumbWidth: thumbRect.width, startX: thumbRect.left}) output: slider_info # Step: Dispatch mouse events for drag - action: exec-js script: | // Simulate mouse down on thumb const thumb document.querySelector(.slider-thumb); thumb.dispatchEvent(new MouseEvent(mousedown, {bubbles: true})); // Wait 100ms await new Promise(r setTimeout(r, 100)); // Dispatch mouse move to right edge const bg document.querySelector(.slider-bg); const rect bg.getBoundingClientRect(); const x rect.left rect.width - {{ .slider_info.thumbWidth }}; document.dispatchEvent(new MouseEvent(mousemove, { bubbles: true, clientX: x, clientY: {{ .slider_info.startX }} 10 })); // Wait 200ms await new Promise(r setTimeout(r, 200)); // Dispatch mouse up document.dispatchEvent(new MouseEvent(mouseup, {bubbles: true})); timeout: 5000 # Step: Wait for verification success (a green check icon appears) - action: wait-for-selector selector: .slider-success timeout: 15000 # Step: Search for company - action: fill selector: #company-name value: {{ .env.COMPANY_NAME }} - action: click selector: #search-btn # Step: Wait for table data (via network request interception) - action: wait-for-network url: /api/annual-report/search method: POST timeout: 20000 # Step: Extract table data and save as CSV - action: exec-js script: | // Wait for Vue to render table await new Promise(r setTimeout(r, 2000)); // Get all rows const rows Array.from(document.querySelectorAll(#report-table tbody tr)); const csvRows []; // Header csvRows.push([统一社会信用代码, 企业名称, 年报年份, 状态]); // Data rows.forEach(row { const cells row.querySelectorAll(td); csvRows.push([ cells[0]?.innerText.trim(), cells[1]?.innerText.trim(), cells[2]?.innerText.trim(), cells[3]?.innerText.trim() ]); }); // Join and save const csv csvRows.map(r r.map(c ${c}).join(,)).join(\n); localStorage.setItem(report_csv, csv); csv output: csv_data # Step: Send to WeCom robot - action: exec command: curl -X POST -H Content-Type: application/json -d {\msgtype\: \text\, \text\: {\content\: \今日年报数据已生成共{{ .csv_data | lines | length }}条记录。详情见附件。\}} {{ .env.WECOM_WEBHOOK }}这个 Skill 的精妙之处在于exec-js的双重用途前一个exec-js用于获取 DOM 尺寸返回 JSON 到slider_info后一个用于执行复杂拖拽逻辑。BrowserSkill 的exec-js支持await可以写异步 JS。wait-for-network是杀手锏它监听 CDP 的Network.requestWillBeSent事件比wait-for-selector更可靠。因为表格数据是 AJAX 加载的DOM 中可能早已存在空 table但数据还没来。localStorage作为数据管道exec-js生成的 CSV 存入localStorage后续步骤可以用{{ .env.LOCAL_STORAGE.report_csv }}读取需在 Skill 中配置--enable-local-storage。4.3 生产环境部署与监控在麒麟服务器上我们用 systemd timer 实现每日定时# /etc/systemd/system/daily-report.timer [Unit] DescriptionDaily Market Report Timer Requiresdaily-report.service [Timer] OnCalendar09:00:00 Persistenttrue [Install] WantedBytimers.target# /etc/systemd/system/daily-report.service [Unit] DescriptionDaily Market Report Job Afterchromium-bsk.service [Service] Typeoneshot User_bsk EnvironmentFile/etc/bsk/report.env ExecStart/usr/local/bin/bsk run --skill /etc/bsk/daily-report.yaml SuccessExitStatus0 2 Restarton-failure RestartSec30 [Install] WantedBymulti-user.target关键监控手段日志聚合journalctl -u daily-report.service -n 100 --no-pager查看最近执行日志失败告警在ExecStart后加|| curl -X POST {{ .env.ALERT_WEBHOOK }} -d failed资源水位ps aux | grep chromium | wc -l监控 Chromium 进程数超过 5 个就触发清理pkill -f chromium.*9222。实操心得BrowserSkill 的--timeout全局参数和 step 级timeout是两回事。全局--timeout 300指整个 Skill 执行不能超过 300 秒step 级timeout: 5000指该 step 单独超时。当遇到网络抖动时建议 step 级 timeout 设为 10~15 秒全局 timeout 设为 120 秒。这样即使某个 step 失败重试 3 次3×1545s总时间仍在可控范围。我在线上曾见过因 DNS 解析失败导致navigate卡住 90 秒最终整个 job 超时而设置合理的 step timeout 后它会快速失败并重试成功率从 68% 提升到 99.2%。5. 常见问题与排查技巧实录来自 12 个生产环境的真实案例5.1 麒麟系统特有问题速查表现象根本原因解决方案验证命令bsk screenshot报错Failed to load module uiChromium 资源文件路径错误在bsk命令中显式指定--chromium-resources-path /usr/lib/chromium-browser/resources/bsk screenshot --chromium-resources-path /usr/lib/chromium-browser/resources/ --url https://example.com --output /tmp/t.pngbsk run一直卡在wait-for-selector但页面明明已加载Chromium 的--disable-gpu未生效导致渲染线程卡死在 systemd service 的CHROMIUM_FLAGS中添加--disable-software-rasterizersystemctl restart chromium-bsk journalctl -u chromium-bsk -n 20查看启动日志是否有GPU process crashedexec-js执行报ReferenceError: localStorage is not definedBrowserSkill 默认禁用 Web Storage API在 Skill YAML 顶部添加options: {enable-local-storage: true}在 Skill 中加一个exec-js输出typeof localStoragewait-for-network无法捕获 POST 请求Chromium 的--remote-allow-origins*参数缺失或拼写错误检查chromium-bsk.service中CHROMIUM_FLAGS是否包含--remote-allow-origins*注意是*不是*.*curl http://localhost:9222/json返回的webSocketDebuggerUrl能否正常连接5.2 CDP 层级问题排查四步法当 BrowserSkill 行为异常如点击无效、截图空白不要急着改 Skill先做 CDP 层诊断第一步确认 Chromium 是否真在监听# 检查端口占用 sudo ss -tuln | grep :9222 # 检查 Chromium 进程参数 ps aux | grep chromium | grep 9222 # 应看到 --remote-debugging-port9222 --remote-allow-origins*第二步手动连接 CDP验证基础能力# 获取 target ID curl http://localhost:9222/json | jq -r .[0].id # 用 wscat 连接需先 npm install -g wscat wscat -c ws://localhost:9222/devtools/page/xxx # 发送 navigate 指令替换 xxx 为真实 ID {id:1,method:Page.navigate,params:{url:https://example.com}} # 观察返回应有 {id:1,result:{frameId:yyy}}第三步检查页面是否真的加载成功# 在 wscat 连接后发送 {id:2,method:Page.getResourceTree} # 查看返回的 frameTree.frame.url确认是否为预期 URL # 如果是 about:blank说明 navigate 失败检查网络或证书第四步捕获渲染问题# 启用 Page domain {id:3,method:Page.enable} # 设置截图选项 {id:4,method:Emulation.setDeviceMetricsOverride,params:{width:1920,height:1080,deviceScaleFactor:1,mobile:false}} # 截图 {id:5,method:Page.captureScreenshot} # 返回 base64用 echo 解码查看这套方法比看 BrowserSkill 日志更底层、更可靠。我曾用它定位到一个麒麟特有的 bugChromium 的--headless模式在麒麟 V10 上默认启用--use-glegl而 egl 驱动在无显卡服务器上会 crash必须强制--use-glswiftshader。这个参数不在 BrowserSkill 文档里但通过 CDP 四步法我们在ps aux输出中发现了 Chromium 启动参数的差异最终解决。5.3 Skill 编写避坑指南5 个血泪教训**永远不要在fill

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑