跨节点给远程 Mac 装 Playwright 实战:全局模块解析与超时中断的坑
这篇文章记录了我在自己的分布式小环境里,为一个远程 macOS 节点安装 Playwright(浏览器自动化框架)的完整过程,以及过程中踩到的两个真实坑:全局 npm 模块无法解析,以及节点反复上下线导致长命令被网关超时打断。最后通过「后台脱离安装 + 日志轮询」的方式一次装成。
背景:为什么要给 Mac 装 Playwright
我的主控环境是一台 VPS(OpenClaw 网关),通过 Tailscale 组网,另外挂了一台 MacBook。VPS 上本来就有 Playwright,用来做网页抓取、截图和 WordPress 自动化。但 VPS 是数据中心 IP,很多站点对数据中心出口有风控。
MacBook 走的是东京家宽出口 IP,更「干净」。所以我想把浏览器自动化能力放到 Mac 上,用家宽 IP 跑抓取/截图,绕过部分对机房 IP 的封锁。于是就有了这次安装。
环境
- 主控:VPS(OpenClaw 网关,通过 Tailscale 组网)
- 节点:MacBook Pro(macOS 14.6.1,Intel x86_64)
- Node.js:v24.21.0(nvm 管理,路径
/Users/binli/.nvm/versions/node/v24.21.0/bin) - Node 节点能力:browser / local-inference / mcp / system(支持
system.run)
第一步:确认节点在线且可执行命令
先确认节点连接状态和支持的命令能力:
openclaw nodes status
openclaw nodes describe --node MacBook-Pro
Mac 节点报告 paired · connected · approved,能力包含 system.run,说明可以远程执行命令。这里要特别注意:从 VPS 执行远程命令时,tools.exec.pathPrepend 对 host=node 不生效,必须在命令里显式导出节点上的 PATH。
坑一:全局 npm 模块无法被 require
Playwright 用 npm install -g 全局安装后,直接在脚本里 require('playwright') 会报 MODULE_NOT_FOUND。原因是 Node.js 默认的模块解析路径(node_modules 向上查找)不包含全局目录。
解决办法是显式指定 NODE_PATH 指向全局模块根目录:
NODE_PATH="$(npm root -g)" node script.js
在本机就是 /Users/binli/.nvm/versions/node/v24.21.0/lib/node_modules。加上这一条后,脚本立刻就能正常 require 了。
坑二:节点反复上下线,长命令被网关超时打断
第一次尝试直接前台运行安装命令,结果网关返回 gateway request timeout for node.invoke——Playwright 要下载约 190MB 的 Chromium,耗时远超 node.invoke 的默认超时。更麻烦的是,这台 Mac 的 Tailscale 连接不稳定(relay “tok”),节点会周期性掉线又重连。
如果前台跑,一旦超时或掉线,安装就被中断,还得从头再来。所以我改用后台脱离(detached)+ 日志轮询:安装进程用 nohup ... & 独立跑,把输出重定向到日志文件,然后分多次调用去轮询日志,避免被单次超时切断。
nohup bash -lc 'export PATH="/Users/binli/.nvm/versions/node/v24.21.0/bin:$PATH";
echo "START $(date)";
npm install -g playwright@latest;
echo "NPM_EXIT=$?";
npx --yes playwright install chromium;
echo "BROWSER_EXIT=$?";
echo "DONE $(date)"' > /tmp/pw-install.log 2>&1 &
之后每次等节点在线时,cat /tmp/pw-install.log 看进度即可。日志里能看到 Chromium 和 Headless Shell 的下载进度条一路走到 100%。
安装结果
- Playwright:v1.63.0(npm 全局)
- Chromium:153.0.8010.12(
chromium-1243,约 192MB) - Chromium Headless Shell:153(约 99MB)
- 浏览器缓存目录:
~/Library/Caches/ms-playwright/
功能验证:真的能跑起来
装完不算数,要实际启动一次 headless Chromium、渲染页面并截图:
const { chromium } = require('playwright');
(async () => {
const b = await chromium.launch({ headless: true });
const p = await b.newPage();
await p.setContent('<h1>Playwright OK on Mac</h1>');
await p.screenshot({ path: '/tmp/pw_mac_test.png' });
await b.close();
console.log('PW_MAC_FUNCTIONAL_OK');
})();
用 NODE_PATH="$(npm root -g)" node test.js 运行,输出 PW_MAC_FUNCTIONAL_OK,并成功生成截图文件——说明整套能力真正可用。
经验总结
- 跨节点执行要显式导出 PATH:
pathPrepend对host=node无效。 - 全局 npm 模块记得带
NODE_PATH,否则脚本无法 require。 - 耗时的安装/下载一定要后台脱离 + 日志轮询:不要和
node.invoke的超时硬碰硬,尤其当节点连接不稳定时。 - 装完必须做功能验证:版本号对不代表能启动浏览器,实际跑一遍才算数。
- 家宽节点做浏览器自动化的价值:出口 IP 更干净,适合抓取和对 IP 敏感的任务。
本文记录于 2026-10-04。环境:OpenClaw 网关(VPS)+ Tailscale + macBook Pro 节点。
