playwright、puppeteer 和 selenium-webdriver 都是用于浏览器自动化的主流 npm 包,广泛应用于端到端测试、爬虫、截图生成等场景。它们都能控制真实浏览器执行用户操作,但底层架构、支持的浏览器范围、API 设计和维护策略存在显著差异。playwright 由 Microsoft 开发,支持 Chromium、WebKit 和 Firefox;puppeteer 由 Google 主导,主要聚焦于 Chromium;selenium-webdriver 是 Selenium 项目的一部分,通过 WebDriver 协议支持多语言和多浏览器,但在 Node.js 环境中使用时需额外配置浏览器驱动。
在构建健壮的端到端(E2E)测试套件或实现高级浏览器自动化任务时,playwright、puppeteer 和 selenium-webdriver 是三个主流选择。它们都能操控真实浏览器,但设计理念、功能覆盖和开发体验差异显著。本文从实际工程角度出发,通过代码示例对比关键能力。
puppeteer 仅支持 Chromium 系浏览器(Chrome、Edge 等)。虽然可通过实验性方式连接 Firefox,但官方不推荐用于生产。
// puppeteer: 启动 Chromium
const browser = await puppeteer.launch();
const page = await browser.newPage();
playwright 原生支持 Chromium、WebKit(Safari 内核)和 Firefox,三者 API 行为高度一致。
// playwright: 启动任意浏览器
const { chromium, webkit, firefox } = require('playwright');
const browser = await chromium.launch(); // 或 webkit.launch(), firefox.launch()
const page = await browser.newPage();
selenium-webdriver 理论上支持所有实现 WebDriver 协议 的浏览器(Chrome、Firefox、Safari、Edge),但需为每种浏览器单独安装对应驱动(如 chromedriver、geckodriver)。
// selenium-webdriver: 需显式指定驱动
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const driver = new Builder()
.forBrowser('chrome')
.setChromeOptions(new chrome.Options())
.build();
💡 关键区别:Playwright 内置所有浏览器二进制文件,开箱即用;Selenium 需手动管理驱动版本兼容性。
现代 Web 应用大量依赖异步加载,可靠的等待策略至关重要。
playwright 提供 自动等待:几乎所有操作(如 click()、fill())会自动等待元素可交互,无需额外代码。
// playwright: 自动等待按钮可点击
await page.click('#submit-button');
// 自动等待输入框就绪
await page.fill('#email', 'test@example.com');
puppeteer 需要 手动等待 元素状态,否则容易因竞态条件失败。
// puppeteer: 必须显式等待
await page.waitForSelector('#submit-button', { visible: true });
await page.click('#submit-button');
await page.waitForFunction(() => document.querySelector('#email').offsetParent !== null);
await page.type('#email', 'test@example.com');
selenium-webdriver 提供 显式等待(Explicit Waits),但需开发者自行编写条件逻辑。
// selenium-webdriver: 使用 WebDriverWait
const { until } = require('selenium-webdriver');
await driver.wait(until.elementLocated({ id: 'submit-button' }), 5000);
const button = await driver.findElement({ id: 'submit-button' });
await button.click();
💡 实践建议:Playwright 的自动等待大幅减少 flaky tests(不稳定测试),而 Puppeteer 和 Selenium 需要更多样板代码确保稳定性。
拦截请求、模拟响应对测试边界条件至关重要。
playwright 支持 路由(Routing),可拦截并修改任意请求。
// playwright: 拦截 API 请求并返回 mock 数据
await page.route('**/api/user', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ name: 'Mock User' })
});
});
await page.goto('/profile');
puppeteer 通过 .setRequestInterception 实现类似功能,但需手动处理请求继续或响应。
// puppeteer: 拦截请求
await page.setRequestInterception(true);
page.on('request', interceptedRequest => {
if (interceptedRequest.url().includes('/api/user')) {
interceptedRequest.respond({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ name: 'Mock User' })
});
} else {
interceptedRequest.continue();
}
});
await page.goto('/profile');
selenium-webdriver 原生不支持网络拦截。需借助外部代理(如 BrowserMob Proxy)或浏览器扩展实现,大幅增加复杂度。
// selenium-webdriver: 无法直接拦截,需额外工具链
// 通常需启动独立代理服务并配置浏览器使用该代理
💡 结论:Playwright 和 Puppeteer 在网络控制上远胜 Selenium,尤其适合需要模拟慢速网络、错误响应或认证令牌的场景。
playwright 内置 设备预设(如 iPhone 12、Pixel 5),一键模拟视口、UA 和触摸事件。
// playwright: 使用设备预设
const { devices } = require('playwright');
const iPhone = devices['iPhone 12'];
const context = await browser.newContext({ ...iPhone });
const page = await context.newPage();
puppeteer 需手动设置视口和 UA 字符串。
// puppeteer: 手动配置移动设备
await page.setViewport({ width: 390, height: 844, isMobile: true });
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) ...');
selenium-webdriver 可通过 Chrome DevTools Protocol (CDP) 或 Firefox 配置 模拟设备,但 API 繁琐且浏览器间不一致。
// selenium-webdriver: 通过 CDP 设置(仅限 Chrome)
await driver.executeCdpCommand('Emulation.setDeviceMetricsOverride', {
width: 390,
height: 844,
mobile: true,
deviceScaleFactor: 2
});
playwright 原生支持 测试隔离:每个测试可拥有独立的浏览器上下文(Context),共享浏览器进程但内存隔离,启动速度快。
// playwright: 每个测试新建上下文
const context = await browser.newContext();
const page = await context.newPage();
// 测试结束后关闭上下文,而非整个浏览器
await context.close();
puppeteer 通常为每个测试启动 全新浏览器实例,资源开销大、速度慢。
// puppeteer: 常见做法(低效)
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 测试结束需关闭整个浏览器
await browser.close();
selenium-webdriver 依赖外部 Grid 或本地驱动管理,无内置并行抽象,需自行设计资源池。
playwright:活跃维护,Microsoft 主导,持续增加新特性(如组件测试、录制器)。puppeteer:Google 维护,稳定但更新节奏放缓,重心转向 Chrome DevTools 集成。selenium-webdriver:Selenium 项目核心部分,长期支持,但 Node.js 版本 API 相对陈旧,缺乏现代 E2E 工具的便利性。| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 跨浏览器 E2E 测试(含 Safari/Firefox) | playwright | 唯一提供 WebKit 和 Firefox 一致支持的现代方案 |
| 仅 Chromium 环境的轻量自动化(如 PDF 生成) | puppeteer | 启动快、API 简洁、社区成熟 |
| 已有 Selenium 基础设施或多语言测试团队 | selenium-webdriver | 与现有 Grid/Hub 无缝集成,语言无关 |
| 需要网络拦截、设备模拟、自动等待等高级特性 | playwright | 开箱即用,减少样板代码 |
| 资源受限环境(如 CI)需快速并行测试 | playwright | 上下文隔离比新建浏览器实例更高效 |
playwright:它解决了 Puppeteer 和 Selenium 的多数痛点,提供更可靠、更简洁的自动化体验。puppeteer:如果你不需要跨浏览器,且已有 Puppeteer 脚本,迁移成本可能不值得。selenium-webdriver:除非你深度依赖 Selenium 生态(如企业级 Grid),否则其繁琐的配置和底层 API 会拖慢开发效率。无论选择哪个,都应结合具体需求权衡——但对大多数现代前端团队而言,Playwright 正迅速成为事实上的标准。
选择 selenium-webdriver 如果你已在使用 Selenium 生态(如 Java/Python 测试套件),或需要与现有 Selenium Grid 集成。它在多语言团队中具有优势,但在 Node.js 中使用时需手动下载和管理浏览器驱动,API 较为底层,缺乏现代 E2E 工具的便捷特性(如自动等待、网络 mocking)。
选择 playwright 如果你需要跨浏览器(包括 WebKit 和 Firefox)的一致性测试能力、内置等待机制、网络拦截、设备模拟以及开箱即用的并行测试支持。它特别适合现代 Web 应用的复杂 E2E 测试场景,且希望避免手动管理浏览器驱动或处理竞态条件问题。
选择 puppeteer 如果你的项目仅针对 Chromium 系浏览器(如 Chrome 或 Edge),且需要轻量级、高性能的自动化控制。它在 PDF 生成、页面截图、无头渲染等任务上表现优异,但不支持非 Chromium 内核浏览器,且对现代前端框架的异步等待支持不如 Playwright 完善。
JavaScript language bindings for Selenium WebDriver. Selenium automates browsers for testing and web-based task automation.
Requires Node.js >= 22.
npm install selenium-webdriver
const { Builder, Browser } = require('selenium-webdriver')
;(async function example() {
let driver = await new Builder().forBrowser(Browser.CHROME).build()
try {
await driver.get('https://www.selenium.dev')
console.log(await driver.getTitle())
} finally {
await driver.quit()
}
})()
Selenium Manager automatically handles browser driver installation — no manual driver setup required.
The Builder sets default options for all browsers in a single chain; options
for non-selected browsers are dropped at build() time. The target browser can
be swapped at runtime via the SELENIUM_BROWSER environment variable.
const { Builder, Browser } = require('selenium-webdriver')
const chrome = require('selenium-webdriver/chrome')
const firefox = require('selenium-webdriver/firefox')
let driver = new Builder()
.forBrowser(Browser.FIREFOX)
.setChromeOptions(new chrome.Options())
.setFirefoxOptions(new firefox.Options())
.build()
To run scripts against a Selenium Grid
or standalone server, point the Builder at the server URL, or set
SELENIUM_REMOTE_URL:
let driver = new Builder().forBrowser(Browser.CHROME).usingServer('http://localhost:4444').build()
SELENIUM_REMOTE_URL="http://localhost:4444" node script.js
selenium-webdriver supports the Node.js versions under active upstream
support. Each is supported until its
end-of-life date; after
that it is unsupported.
| Node.js | Support ends |
|---|---|
| 22 | 2027-04-30 |
| 24 | 2028-04-30 |
| 26 | 2029-04-30 |
CI tests the earliest and latest supported versions available in rules_nodejs; issues and pull requests are welcome for any supported version.
Contributions are welcome via GitHub pull requests. See the source code for this binding.
Licensed under the Apache License 2.0.