nightmare、playwright、puppeteer 和 selenium-webdriver 都是用于控制浏览器进行自动化测试、爬虫或生成截图的 Node.js 库。selenium-webdriver 是历史最悠久的标准方案,支持多语言和多浏览器;puppeteer 由 Google 维护,专注于 Chromium 生态;playwright 由 Microsoft 开发,支持多浏览器并提供了现代化的自动等待机制;而 nightmare 基于 Electron,目前已不再维护。这些工具虽然目标相似,但在架构设计、浏览器支持范围和长期维护性上存在显著差异。
在 Node.js 生态中,控制浏览器进行自动化测试或数据抓取是一项常见需求。nightmare、playwright、puppeteer 和 selenium-webdriver 是四个最具代表性的解决方案。虽然它们都能完成“打开浏览器、点击按钮、获取内容”这类任务,但在底层实现、维护状态和开发体验上有着本质区别。作为架构师,我们需要从工程落地的角度深入分析它们的差异。
这是选型时最关键的决策点。一个不再维护的库会带来安全隐患和技术债务。
nightmare 已经停止维护。
// nightmare: 已废弃,仅作历史参考
const Nightmare = require('nightmare');
const nightmare = Nightmare({ show: false });
nightmare
.goto('https://example.com')
.click('button')
.end()
.then(function(result) {
console.log(result);
});
playwright 处于活跃维护中。
// playwright: 现代且活跃
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
puppeteer 处于活跃维护中。
// puppeteer: 现代且活跃
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
selenium-webdriver 处于活跃维护中。
// selenium-webdriver: 标准且稳定
const { Builder, By } = require('selenium-webdriver');
(async function example() {
let driver = await new Builder().forBrowser('chrome').build();
await driver.get('https://example.com');
await driver.quit();
})();
不同的业务场景需要不同的浏览器内核支持。
playwright 支持三大内核。
// playwright: 多浏览器支持
const { chromium, firefox, webkit } = require('playwright');
async function testAll() {
for (const browserType of [chromium, firefox, webkit]) {
const browser = await browserType.launch();
// 运行测试逻辑...
await browser.close();
}
}
puppeteer 主要支持 Chromium。
// puppeteer: 专注 Chromium
const puppeteer = require('puppeteer');
(async () => {
// 默认启动 Chromium
const browser = await puppeteer.launch({ product: 'chrome' });
// Firefox 支持有限且配置复杂
})();
selenium-webdriver 支持所有主流浏览器。
// selenium-webdriver: 广泛支持
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const firefox = require('selenium-webdriver/firefox');
// 启动 Chrome
let driverChrome = await new Builder().forBrowser('chrome').build();
// 启动 Firefox
let driverFirefox = await new Builder().forBrowser('firefox').build();
nightmare 仅支持 Electron。
// nightmare: 仅 Electron
const Nightmare = require('nightmare');
// 无法切换浏览器内核,仅限于 Electron 环境
const nightmare = Nightmare({ show: false });
现代 Web 应用大量使用异步加载,如何处理元素等待是测试稳定性的核心。
playwright 内置智能自动等待。
waitFor 代码。// playwright: 自动等待
const page = await browser.newPage();
await page.goto('https://example.com');
// 自动等待按钮可见且可点击
await page.click('#submit-button');
puppeteer 需要部分手动等待。
waitForSelector,但点击操作本身不总是隐含等待。// puppeteer: 需显式等待
const page = await browser.newPage();
await page.goto('https://example.com');
// 通常需要先等待元素出现
await page.waitForSelector('#submit-button');
await page.click('#submit-button');
selenium-webdriver 需要显式等待配置。
WebDriverWait 来保证稳定性。// selenium-webdriver: 显式等待
const { until } = require('selenium-webdriver');
await driver.get('https://example.com');
// 必须显式定义等待条件
let element = await driver.wait(until.elementLocated(By.id('submit-button')));
await element.click();
nightmare 链式调用隐含等待。
// nightmare: 链式隐含等待
nightmare
.goto('https://example.com')
.wait('#submit-button') // 需显式调用 wait
.click('#submit-button');
这些功能常用于生成报告或爬虫存档。
playwright 支持全功能截图与 PDF。
// playwright: 截图与 PDF
await page.screenshot({ path: 'example.png', fullPage: true });
await page.pdf({ path: 'report.pdf', format: 'A4' });
puppeteer 截图与 PDF 是其强项。
// puppeteer: 截图与 PDF
await page.screenshot({ path: 'example.png', fullPage: true });
await page.pdf({ path: 'report.pdf', format: 'A4' });
selenium-webdriver 仅支持截图。
// selenium-webdriver: 仅截图
let png = await driver.takeScreenshot();
// 需要手动处理 png 数据写入文件
// 不支持 page.pdf()
nightmare 支持截图。
// nightmare: 仅截图
await nightmare.screenshot('example.png');
// 不支持 PDF
| 特性 | playwright | puppeteer | selenium-webdriver | nightmare |
|---|---|---|---|---|
| 维护状态 | ✅ 活跃 (Microsoft) | ✅ 活跃 (Google) | ✅ 活跃 (社区标准) | ❌ 已停止维护 |
| 浏览器内核 | Chromium, Firefox, WebKit | 主要 Chromium | 所有 (通过 Driver) | Electron 仅 |
| 自动等待 | ✅ 智能内置 | ⚠️ 部分支持 | ❌ 需手动配置 | ⚠️ 链式隐含 |
| API 风格 | 现代 Promise/Async | 现代 Promise/Async | 经典 Promise/Async | 链式调用 |
| PDF 支持 | ✅ 支持 | ✅ 支持 | ❌ 不支持 | ❌ 不支持 |
| 适用场景 | 端到端测试、跨浏览器 | 爬虫、PDF、Chrome 测试 | 传统测试、多语言协作 | 旧项目维护 (不推荐) |
playwright 是现代端到端测试的首选 🏆。
puppeteer 是 Chrome 生态工具的最佳搭档 🛠️。
selenium-webdriver 是企业遗留系统的稳定基石 🏢。
nightmare 应被逐步淘汰 🗑️。
在选择浏览器自动化库时,维护状态和浏览器覆盖范围是两个决定性因素。playwright 凭借其现代化的设计和跨浏览器能力,已成为大多数新项目的默认选择。puppeteer 在 Chrome 特定任务上依然保持优势。selenium-webdriver 则继续服务于需要广泛兼容性和标准协议的场景。而 nightmare 已完成了它的历史使命,不应再出现在新的技术栈中。
如果需要跨浏览器测试(包括 WebKit、Firefox 和 Chromium)或构建高可靠性的端到端测试,请选择 playwright。它提供了强大的自动等待功能、网络拦截能力和现代化的 API 设计,非常适合现代 Web 应用的复杂交互场景。
当需要支持非 Chromium 内核的老旧浏览器,或者团队已经建立了基于 Selenium Grid 的基础设施时,选择 selenium-webdriver。它是行业标准,语言绑定丰富,但配置相对繁琐,运行速度通常慢于现代无头浏览器方案。
不建议在新项目中选择 nightmare。该库已停止维护多年,基于旧版 Electron,存在安全漏洞且不支持现代浏览器特性。如果维护旧项目,建议尽快迁移到 playwright 或 puppeteer 以获得更好的稳定性和支持。
如果项目主要依赖 Chrome 或 Chromium,或者需要生成 PDF、截取页面截图以及进行无头爬虫开发,puppeteer 是理想选择。它与 Chrome DevTools 协议深度集成,启动速度快,且在 Google 生态内更新及时。
Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable, and fast.
| Linux | macOS | Windows | |
|---|---|---|---|
| Chromium 145.0.7632.6 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit 26.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox 146.0.1 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Headless execution is supported for all browsers on all platforms. Check out system requirements for details.
Looking for Playwright for Python, .NET, or Java?
Playwright has its own test runner for end-to-end tests, we call it Playwright Test.
The easiest way to get started with Playwright Test is to run the init command.
# Run from your project's root directory
npm init playwright@latest
# Or create a new project
npm init playwright@latest new-project
This will create a configuration file, optionally add examples, a GitHub Action workflow and a first test example.spec.ts. You can now jump directly to writing assertions section.
Add dependency and install browsers.
npm i -D @playwright/test
# install supported browsers
npx playwright install
You can optionally install only selected browsers, see install browsers for more details. Or you can install no browsers at all and use existing browser channels.
Auto-wait. Playwright waits for elements to be actionable prior to performing actions. It also has a rich set of introspection events. The combination of the two eliminates the need for artificial timeouts - a primary cause of flaky tests.
Web-first assertions. Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met.
Tracing. Configure test retry strategy, capture execution trace, videos and screenshots to eliminate flakes.
Browsers run web content belonging to different origins in different processes. Playwright is aligned with the architecture of the modern browsers and runs tests out-of-process. This makes Playwright free of the typical in-process test runner limitations.
Multiple everything. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.
Trusted events. Hover elements, interact with dynamic controls and produce trusted events. Playwright uses real browser input pipeline indistinguishable from the real user.
Test frames, pierce Shadow DOM. Playwright selectors pierce shadow DOM and allow entering frames seamlessly.
Browser contexts. Playwright creates a browser context for each test. Browser context is equivalent to a brand new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.
Log in once. Save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test, yet delivers full isolation of independent tests.
Codegen. Generate tests by recording your actions. Save them into any language.
Playwright inspector. Inspect page, generate selectors, step through the test execution, see click points and explore execution logs.
Trace Viewer. Capture all the information to investigate the test failure. Playwright trace contains test execution screencast, live DOM snapshots, action explorer, test source and many more.
Looking for Playwright for TypeScript, JavaScript, Python, .NET, or Java?
To learn how to run these Playwright Test examples, check out our getting started docs.
This code snippet navigates to Playwright homepage and saves a screenshot.
import { test } from '@playwright/test';
test('Page Screenshot', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.screenshot({ path: `example.png` });
});
This snippet emulates Mobile Safari on a device at given geolocation, navigates to maps.google.com, performs the action and takes a screenshot.
import { test, devices } from '@playwright/test';
test.use({
...devices['iPhone 13 Pro'],
locale: 'en-US',
geolocation: { longitude: 12.492507, latitude: 41.889938 },
permissions: ['geolocation'],
})
test('Mobile and geolocation', async ({ page }) => {
await page.goto('https://maps.google.com');
await page.getByText('Your location').click();
await page.waitForRequest(/.*preview\/pwa/);
await page.screenshot({ path: 'colosseum-iphone.png' });
});
This code snippet navigates to example.com, and executes a script in the page context.
import { test } from '@playwright/test';
test('Evaluate in browser context', async ({ page }) => {
await page.goto('https://www.example.com/');
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
}
});
console.log(dimensions);
});
This code snippet sets up request routing for a page to log all network requests.
import { test } from '@playwright/test';
test('Intercept network requests', async ({ page }) => {
// Log and continue all network requests
await page.route('**', route => {
console.log(route.request().url());
route.continue();
});
await page.goto('http://todomvc.com');
});