跨节点给远程 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,并成功生成截图文件——说明整套能力真正可用。

经验总结

  1. 跨节点执行要显式导出 PATH:pathPrepend 对 host=node 无效。
  2. 全局 npm 模块记得带 NODE_PATH,否则脚本无法 require。
  3. 耗时的安装/下载一定要后台脱离 + 日志轮询:不要和 node.invoke 的超时硬碰硬,尤其当节点连接不稳定时。
  4. 装完必须做功能验证:版本号对不代表能启动浏览器,实际跑一遍才算数。
  5. 家宽节点做浏览器自动化的价值:出口 IP 更干净,适合抓取和对 IP 敏感的任务。

本文记录于 2026-10-04。环境:OpenClaw 网关(VPS)+ Tailscale + macBook Pro 节点。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注