Playwright 和 Selenium 怎么选:先看你要不要 Grid

2026-08-18

先把这篇的依据交代清楚,免得你读到一半觉得少了半边:我手上只有 github.com/microsoft/playwright 这个仓库,所以关于 Selenium 的一切,只引用 Playwright 仓库里对它的描述与源码里对它的处理。Selenium 自身的定位器体系、等待模型、语言绑定这些,这个仓库里没有写,本文一律不比、不评。

也正因为这样,这篇的主线不是「两个框架谁更好」,而是一个更实际的岔口:你手上那套 Selenium Grid,到底是要接进来,还是绕开。 这个岔口决定了后面所有的技术细节。

岔口一:Grid 那条通路,仓库自己怎么定性

docs/src/selenium-grid.md 的标题逐字是 Selenium Grid (experimental)。正文写明 Playwright 可以连到运行 Selenium 4 的 Selenium Grid Hub,用上面的 Google ChromeMicrosoft Edge 代替本机浏览器,并且明说这个功能是 experimental、优先级也据此排定。

紧跟着是一段 warning,我把它的意思复述一下:这项集成将来存在被打断的风险,使用前请自行权衡风险与收益。展开的 details 里给了原因——Playwright 内部通过 Chrome DevTools Protocol 的 websocket 连接浏览器,Selenium 4 目前暴露了这个能力,但将来未必如此;如果 Selenium 不再暴露,Playwright 这条路就不再工作。

这段话的分量比「experimental」这个词本身重。它不是「功能还不完善」,而是「这条路依赖另一个项目继续暴露某个能力」。要不要接,先接受这个前提。

岔口二:你要覆盖哪些浏览器

文档里那句「只对 Google Chrome 和 Microsoft Edge 有效」,在源码里是硬拦截,不是文档上的软提示。

packages/playwright-core/src/server/browserType.tslaunch() 一进来就读环境变量:

const seleniumHubUrl = (options as any).__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL;
if (seleniumHubUrl)
  return this.launchWithSeleniumHub(progress, seleniumHubUrl, options);

而同一个文件里基类的 launchWithSeleniumHub 是这样的:

async launchWithSeleniumHub(progress: Progress, hubUrl: string, options: types.LaunchOptions): Promise<Browser> {
  throw new Error('Connecting to SELENIUM_REMOTE_URL is only supported by Chromium');
}

只有 packages/playwright-core/src/server/chromium/chromium.ts 里的 Chromium 覆写了它。把这两处放在一起就能得到一个很具体的结论:一旦进程里设了 SELENIUM_REMOTE_URL,Firefox 与 WebKit 的启动路径会走到基类那个方法上并抛错。所以这不是「Grid 上没有 Firefox 节点」的问题,而是这条通路本身只为 Chromium 系准备。

浏览器名的选择也在代码里写死了判断依据:options.channelmsedge 开头就用 MicrosoftEdge,否则用 chrome,对应的选项键相应是 ms:edgeOptionsgoog:chromeOptions

驱动模型差异:WebDriver 只负责把浏览器叫起来

这是两边最本质的差别,而且不用猜,launchWithSeleniumHub 的实现从头到尾摆在那里。按代码顺序走一遍:

  1. hubUrl 末尾不是 / 就补一个;
  2. 取默认启动参数,再追加 --remote-debugging-port=0
  3. 组出 desiredCapabilities:browserName 加上那组浏览器选项,选项里带着上一步的 args;
  4. 如果设了 SELENIUM_REMOTE_CAPABILITIES,解析后浅合并进 desiredCapabilities;如果设了 SELENIUM_REMOTE_HEADERS,解析成一组 headers;
  5. <hubUrl>session 发 POST,body 是 { capabilities: { alwaysMatch: desiredCapabilities } },请求头带 Content-Type: application/json; charset=utf-8 与上一步那组 headers;
  6. 从响应的 value.sessionId 拿到会话 id,并注册一个断开回调——它向 <hubUrl>session/<sessionId> 发 DELETE,同时被放进 gracefullyCloseSet
  7. 看返回的 capabilities 里有没有 se:cdp
  8. 最后调 _connectOverCDPInternal(progress, endpointURL, ...),并把那组 headers 一并传进去。

看到第 8 步就明白了:WebDriver 会话在这里只承担一件事——把浏览器在 Grid 节点上起起来、并交回一个 CDP 端点;此后所有页面操作都走 CDP,不走 WebDriver 协议。 这也解释了 headers 为什么要贯穿始终:建会话、删会话、以及后面的 CDP 连接都用同一组头,所以云端浏览器服务的鉴权信息才放在 SELENIUM_REMOTE_HEADERS 里。

第 7 步的分叉就是 Selenium 4 与 3 的分界:

  • se:cdp,日志打 <selenium> using selenium v4,直接拿它当端点;如果这个端点的 hostname 是 localhost127.0.0.1,代码会把它替换成 hubUrl 的 hostname。
  • 没有 se:cdp,日志打 <selenium> using selenium v3,改从 goog:chromeOptions 里的 debuggerAddress 取地址;如果取到的仍是本地地址,再去请求 <hub origin>/grid/api/testsession?session=<sessionId>,用返回里的 proxyId 的 hostname 顶上;这一步失败会记一句「无法解析端点 IP,是不是 standalone 模式」的日志。

文档对 Selenium 3 的定性也是一致的:Selenium 3 不暴露 CDP,所以只是 best-effort 支持——Playwright 会尝试直连 grid node,节点必须从运行 Playwright 的那台机器直接可达

顺带解释了分布式部署那条要求。文档说,跑分布式 Grid 时要让 selenium 节点以一个可访问的地址注册,办法是启动节点时设 SE_NODE_GRID_URL 指向 hub。上面第 7 步那些 hostname 替换逻辑,正是在补救地址不可达的情况。

Windows 侧:那些示例不能照抄

docs/src/selenium-grid.md 里的示例全是 bash 的前缀写法,各语言绑定各有一行,别串:

SELENIUM_REMOTE_URL=http://<selenium-hub-ip>:4444 npx playwright test

Python 绑定那一行是 pytest --browser chromium,Java 是 mvn test,C# 是 dotnet test——前缀部分相同,后面跑什么各自对应。文档里出现的 4444 是示例中的 hub 地址端口,不是配置项。

Windows 的 cmd 与 PowerShell 没有这种「环境变量前缀 + 命令」的语法。仓库在 docs/src/browsers.md 里讲另一个环境变量 PLAYWRIGHT_BROWSERS_PATH 时,给了三个平行的写法 tab:bash 用前缀,batch 用 set VAR=value 另起一行再跑命令,PowerShell 用 $Env:VAR="..."。把这两处摆在一起,Windows 上就该先设后跑:

$Env:SELENIUM_REMOTE_URL="http://<selenium-hub-ip>:4444"
npx playwright test

以上为按仓库文档中的参数语义组合的示例,未经实测,以仓库最新内容与 --help 的实际输出为准。

排查时把 DEBUG=pw:browser* 打开,文档明说这样能看到 Playwright 是怎么连 Grid 的,并且提 issue 时请附上这段日志。日志里会依次出现 <selenium> connecting to<selenium> connected to sessionId=<selenium> using selenium v4(或 v3)、<selenium> retrieved endpoint 这些行——这些字符串都在上面那段源码里,可以直接拿来对照。

岔口三:不接 Grid 的话,仓库自己给了什么

如果你的诉求其实是「把用例铺到更多机器上」,那 docs/src/test-sharding-js.md 才是对口的那页:传 --shard=x/y 把用例切成若干份,每份作为独立的 job 跑。而 Grid 那页的定位始终是「连到已有的 Hub,用它上面的浏览器代替本机浏览器」。两者解决的不是同一件事。

浏览器供给这边,docs/src/browsers.md 写明每个 Playwright 版本需要特定版本的浏览器二进制,用 CLI 的 install 安装;每次升级 Playwright 都可能要重跑一次。下载位置是 OS 相关的缓存目录,Windows 上是 %USERPROFILE%\AppData\Local\ms-playwright,macOS 是 ~/Library/Caches/ms-playwright,Linux 是 ~/.cache/ms-playwrightPLAYWRIGHT_BROWSERS_PATH 可以改这个位置,但文档单独用 note 提醒:它不会改变 Google Chrome 与 Microsoft Edge 的安装路径。

要用品牌浏览器就设 channel,可用值是 chromemsedge 以及各自的 beta / dev / canary,文档同时注明 Playwright 默认不安装这些浏览器。还有两条 warning 值得提前知道:企业浏览器策略可能影响 Playwright 启动与控制 Chrome / Edge,而这明确写作「超出 Playwright 项目范围」;另外品牌 Chrome / Edge 已换用新的 headless 实现,与 Playwright 默认使用的 chromium headless shell 行为有差异,文档说某些情况下要预期不同表现。

把岔口串成决策路径

从你的处境往回倒推,四问就够:

第一问:你要的是跨机器还是跨浏览器? 只是想跑得更分散,用仓库自己的 sharding;想复用已有的浏览器机房或云端浏览器服务,才轮到 Grid。

第二问:必须覆盖 Firefox 或 WebKit 吗? 必须的话,Grid 这条通路直接出局——不是配置问题,是基类方法就抛错。

第三问:你的 Grid 是 4 还是 3? 是 4,走 se:cdp,文档与代码都是主路径;是 3,属于 best-effort,且要求 grid node 从跑 Playwright 的机器直接可达,网络拓扑上先确认这一点再动手。

第四问:能不能接受 experimental 与那段 warning? 仓库把风险写在明面上了:这条路依赖 Selenium 继续暴露 CDP 能力。能接受就接,接受不了就走第一问的另一半。

没有依据的维度,直说不比

  • Selenium 自身的 API 设计、等待机制、报告生态:这一点我们在 Playwright 仓库里没有找到对应说明,不比。docs/src/protractor-js.md 里确实出现过一次 Selenium,但那是 Protractor 迁移原则里的一句话,语境是迁移清单,不能拿来当对 Selenium 的能力评价。
  • 运行速度、资源占用、稳定性:两侧我们都没有跑过,不写。
  • 具体云端浏览器服务的对接情况:文档只说 SELENIUM_REMOTE_CAPABILITIESSELENIUM_REMOTE_HEADERS 可以用于外部服务与鉴权(headers 的示例是 {'Authorization':'Basic b64enc'} 这种形式),具体到某家服务能不能通,仓库里没有写。

验证与反证

接完之后按这个顺序确认:文档要求先确保 Grid 本身能跑通 Selenium WebDriver 的示例并传入 SELENIUM_REMOTE_URL,跑不通就先看 hub / node / standalone 的输出;然后开 DEBUG=pw:browser*,看有没有 <selenium> connected to sessionId=;再看紧随其后是 using selenium v4 还是 v3,这决定了端点是来自 se:cdp 还是 debuggerAddress

反过来说,什么情况说明问题不在这条链路上:如果日志里压根没有 <selenium> 前缀的行,那说明进程根本没走进 launchWithSeleniumHub——因为那些日志全在这个函数体内,而进入它的唯一条件是 SELENIUM_REMOTE_URL 非空。这时候该查的是环境变量有没有传到跑测试的那个进程里(Windows 上尤其容易在这里翻车),而不是查 Grid。


本文依据 github.com/microsoft/playwright 仓库于 2026-08-18 的公开内容整理, 事实来自仓库内的文档与源码。我们没有对文中涉及的功能做过实测, 因此不涉及运行速度、稳定性与实际表现的任何描述。 该项目迭代频繁,文中涉及的 API 签名、配置项与默认值随版本变动,请以仓库最新内容为准。 本文不涉及浏览器版本矩阵与版本清单,相关信息请以官方发布说明为准。

本文关于 Selenium 的全部依据,均来自上述 Playwright 仓库内对它的描述与处理(docs/src/selenium-grid.md 与相关源码),并未引用 Selenium 官方仓库的内容。 本文只对照各方公开写明的机制,不推断未公开的实现,也不对项目做优劣排名

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。