Puppeteer и Playwright (как обсуждается в Playwright Comprehensive Guide: Multi-Browser Testing, Auto-Wait, and Trace Viewer Mastery) представляют современное поколение инструментов автоматизации браузеров, оба произошли от команд в Google и Microsoft соответственно. В то время как Puppeteer был пионером высокоуровневой автоматизации Chrome DevTools Protocol (CDP), Playwright (как обсуждается в Cypress Deep Dive: Architecture, Debugging, and Network Stubbing Mastery) появился как мультибраузерное решение, устраняющее ограничения Puppeteer. Этот всесторонний анализ сравнивает оба инструмента по архитектуре, функциям, производительности и практическим случаям использования для информирования вашей стратегии автоматизации.
Введение
Автоматизация браузеров значительно эволюционировала с эры WebDriver. Puppeteer (как обсуждается в Percy, Applitools & BackstopJS: Visual Regression Testing Solutions Compared), выпущенный Google в 2017 году, продемонстрировал, что прямой доступ к CDP может обеспечить более быструю и надежную автоматизацию, чем инструменты на основе WebDriver. Опираясь на эту основу, команда Playwright от Microsoft (многие бывшие контрибьюторы Puppeteer) запустила Playwright в 2020 году с амбициозными целями: истинная поддержка кросс-браузерности, улучшенные возможности отладки и более опинионированный фреймворк тестирования.
Ключевые Вопросы, на Которые Отвечает Эта Статья:
- Каковы фундаментальные архитектурные различия?
- Когда следует выбирать Puppeteer или Playwright?
- Как они сравниваются в реальных сценариях?
- Каковы соображения по миграции?
Исторический Контекст и Происхождение
Генезис Puppeteer
Выпуск: Январь 2017 командой Google Chrome
Первоначальные Цели:
- Предоставить высокоуровневый API поверх Chrome DevTools Protocol
- Включить автоматизацию headless Chrome
- Поддержать веб-скрейпинг и генерацию PDF
- Предложить более простую альтернативу Selenium
Эволюция:
- Изначально только Chrome
- Добавлена поддержка Firefox (экспериментальная) через протокол Juggler
- Сосредоточен на Chrome/Chromium как основной цели
- Поддерживается командой Google Chrome
Появление Playwright
Выпуск: Январь 2020 Microsoft
Предыстория Команды:
- Возглавляется бывшими основными контрибьюторами Puppeteer
- Инженеры, которые создали Puppeteer в Google
Философия Дизайна:
- Кросс-браузерность с первого дня (Chromium, Firefox, WebKit)
- Создан для тестирования, а не просто автоматизации
- Комплексная трассировка и отладка
- Автоматическое ожидание и устойчивость
Архитектурное Сравнение
Слой Протокола
Puppeteer:
API Puppeteer → Chrome DevTools Protocol → Chromium/Chrome
→ Firefox (через Juggler)
Playwright:
API Playwright → Протокол Playwright → Браузерные патчи
→ Chromium (CDP)
→ Firefox (Juggler)
→ WebKit (Пользовательский протокол)
Ключевое Различие: Playwright патчит браузеры во время сборки, обеспечивая согласованные API для всех браузеров. Puppeteer полагается на нативные протоколы браузеров.
Матрица Поддержки Браузеров
Функция | Puppeteer | Playwright |
---|---|---|
Chromium | ✅ Полная поддержка | ✅ Полная поддержка |
Chrome | ✅ Полная поддержка | ✅ Полная поддержка |
Firefox | ⚠️ Экспериментальная | ✅ Полная поддержка |
WebKit/Safari | ❌ Не поддерживается | ✅ Полная поддержка |
Edge | ✅ Через Chromium | ✅ Полная поддержка |
Протокол | CDP (нативный) | Пользовательские патчи |
Версионирование Браузера | Установлен в системе | Включен в Playwright |
Установка и Настройка
Puppeteer:
npm install puppeteer
# Автоматически загружает Chromium
npm install puppeteer-core
# Без загрузки браузера, использует системный Chrome
Playwright:
npm install playwright
npx playwright install # Загружает все браузеры
npm install @playwright/test # С test runner
npx playwright install chromium # Один браузер
Сравнение Размера Установки:
- Puppeteer + Chromium: ~170-300 МБ
- Playwright + Все браузеры: ~1 ГБ
- Playwright + Только Chromium: ~300 МБ
API и Опыт Разработчика
Базовая Автоматизация
Puppeteer:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com');
await page.type('#username', 'testuser');
await page.type('#password', 'password123');
await page.click('button[type="submit"]');
await page.waitForSelector('.dashboard');
const title = await page.title();
console.log('Заголовок страницы:', title);
await browser.close();
})();
Playwright:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await page.waitForSelector('.dashboard');
const title = await page.title();
console.log('Заголовок страницы:', title);
await browser.close();
})();
Схожесть API: ~90% совместимость API для базовых операций, но Playwright предлагает дополнительные функции.
Продвинутые Селекторы
Puppeteer:
// CSS селекторы
await page.click('#submit-button');
await page.click('.form-control:nth-child(2)');
// XPath
await page.waitForXPath('//button[contains(text(), "Отправить")]');
const [button] = await page.$x('//button[@type="submit"]');
await button.click();
// Пользовательский текстовый селектор (через page.evaluate)
await page.evaluate(() => {
const button = Array.from(document.querySelectorAll('button'))
.find(b => b.textContent.includes('Отправить'));
button.click();
});
Playwright:
// CSS селекторы
await page.click('#submit-button');
// Текстовый селектор
await page.click('text=Отправить');
await page.click('button:has-text("Отправить")');
// XPath
await page.click('xpath=//button[@type="submit"]');
// Продвинутые селекторы
await page.click('button >> text=Отправить'); // Цепочка
await page.click('button:right-of(:text("Пользователь"))'); // На основе layout
await page.click('button:below(#header)');
await page.click('button:near(.form-group)');
// На основе ролей (доступность)
await page.click('role=button[name="Отправить"]');
Победитель: Playwright - более богатый движок селекторов с селекторами на основе layout и ролей.
Поведение Автоматического Ожидания
Puppeteer:
// Часто необходимо ручное ожидание
await page.waitForSelector('#result');
await page.waitForFunction(() => document.querySelector('#result').textContent !== '');
// Ожидание бездействия сети
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
// Пользовательские ожидания
await page.waitForTimeout(1000);
await page.waitForFunction(
() => window.appReady === true,
{ timeout: 5000 }
);
Playwright:
// Встроенное автоматическое ожидание
await page.click('#result'); // Автоматически ждет элемент
// Авто-ожидание для:
// - Элемент виден
// - Элемент стабилен (не анимируется)
// - Элемент включен
// - Элемент получает события
// Все еще поддерживает явные ожидания
await page.waitForSelector('#result', { state: 'visible' });
await page.waitForLoadState('networkidle');
await page.waitForFunction(() => window.appReady === true);
Победитель: Playwright - более агрессивное и интеллектуальное автоматическое ожидание уменьшает потребность в явных ожиданиях.
Множественные Контексты и Параллельное Выполнение
Puppeteer:
const browser = await puppeteer.launch();
// Инкогнито контексты
const context1 = await browser.createIncognitoBrowserContext();
const context2 = await browser.createIncognitoBrowserContext();
const page1 = await context1.newPage();
const page2 = await context2.newPage();
await Promise.all([
page1.goto('https://example.com'),
page2.goto('https://example.com')
]);
// Изолированное хранилище, cookies, сессии
Playwright:
const browser = await chromium.launch();
// Контексты браузера (как инкогнито)
const context1 = await browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'Пользовательский User Agent',
locale: 'ru-RU',
timezoneId: 'Europe/Moscow',
permissions: ['geolocation']
});
const context2 = await browser.newContext({
viewport: { width: 375, height: 667 }, // Мобильный
isMobile: true,
hasTouch: true
});
// Параллельное выполнение с разными контекстами
const [page1, page2] = await Promise.all([
context1.newPage(),
context2.newPage()
]);
Победитель: Playwright - больше опций конфигурации для контекстов, лучшая поддержка мультиконтекста.
Кросс-Браузерное Тестирование
Согласованность Браузера
Puppeteer:
- Chrome/Chromium: Отлично, нативная поддержка
- Firefox: Ограниченная, экспериментальная, отсутствующие функции
- Safari: Не поддерживается
Playwright:
const { chromium, firefox, webkit } = require('playwright');
async function testAllBrowsers() {
for (const browserType of [chromium, firefox, webkit]) {
const browser = await browserType.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// Одинаковый API для всех браузеров
await page.screenshot({ path: `screenshot-${browserType.name()}.png` });
await browser.close();
}
}
Реальный Кросс-Браузерный Тест:
const { test, expect } = require('@playwright/test');
test.describe('Кросс-браузерный логин', () => {
test('должен успешно войти', async ({ page, browserName }) => {
await page.goto('https://example.com/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await expect(page.locator('.dashboard')).toBeVisible();
// Специфичные для браузера проверки
if (browserName === 'webkit') {
// Проверки специфичные для Safari
await expect(page.locator('.webkit-notice')).toBeVisible();
}
});
});
Победитель: Playwright - комплексная, согласованная поддержка кросс-браузерности является основной функцией.
Перехват Сети и Мокирование
Перехват Запросов
Puppeteer:
await page.setRequestInterception(true);
page.on('request', (request) => {
if (request.resourceType() === 'image') {
request.abort();
} else if (request.url().includes('/api/users')) {
request.respond({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, name: 'Тестовый Пользователь' }
])
});
} else {
request.continue();
}
});
await page.goto('https://example.com');
Playwright:
// Перехват на основе маршрутов
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
await page.route('**/api/users', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, name: 'Тестовый Пользователь' }
])
});
});
// Или изменить запросы
await page.route('**/api/**', route => {
const headers = route.request().headers();
headers['Authorization'] = 'Bearer fake-token';
route.continue({ headers });
});
await page.goto('https://example.com');
Победитель: Playwright - более гибкая маршрутизация, встроенная запись HAR.
Интеграция с Фреймворком Тестирования
Puppeteer с Jest
// jest-puppeteer.config.js
module.exports = {
launch: {
headless: true,
args: ['--no-sandbox']
},
browserContext: 'default'
};
// test.spec.js
describe('Тест Логина', () => {
beforeAll(async () => {
await page.goto('https://example.com');
});
it('должен отобразить форму логина', async () => {
await expect(page).toMatch('Логин');
const title = await page.title();
expect(title).toBe('Логин - Example');
});
it('должен успешно войти', async () => {
await page.type('#username', 'testuser');
await page.type('#password', 'password123');
await page.click('button[type="submit"]');
await page.waitForSelector('.dashboard');
const url = page.url();
expect(url).toContain('/dashboard');
});
});
Test Runner Playwright
// playwright.config.js
module.exports = {
testDir: './tests',
timeout: 30000,
retries: 2,
use: {
headless: true,
viewport: { width: 1920, height: 1080 },
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry'
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
{ name: 'webkit', use: { browserName: 'webkit' } }
]
};
// test.spec.js
const { test, expect } = require('@playwright/test');
test.describe('Тест Логина', () => {
test.beforeEach(async ({ page }) => {
await page.goto('https://example.com');
});
test('должен отобразить форму логина', async ({ page }) => {
await expect(page.locator('h1')).toHaveText('Логин');
await expect(page).toHaveTitle('Логин - Example');
});
test('должен успешно войти', async ({ page }) => {
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/.*dashboard/);
await expect(page.locator('.dashboard')).toBeVisible();
});
});
Победитель: Playwright - опинионированный test runner со встроенными утверждениями, параллелизацией, повторами, скриншотами, видео и трассировками.
Возможности Отладки
Отладка Puppeteer
// Slowmo
const browser = await puppeteer.launch({
headless: false,
slowMo: 100 // Замедлить на 100мс
});
// Devtools
const browser = await puppeteer.launch({
headless: false,
devtools: true
});
// Скриншоты
await page.screenshot({ path: 'screenshot.png', fullPage: true });
// Логи консоли
page.on('console', msg => console.log('ЛОГ СТРАНИЦЫ:', msg.text()));
// Ошибки страницы
page.on('pageerror', error => console.log('ОШИБКА СТРАНИЦЫ:', error.message));
Отладка Playwright
// Inspector (интерактивная отладка)
// Установить переменную окружения
// PWDEBUG=1 npm test
// Или в коде
await page.pause(); // Открывает inspector
// Trace viewer (отладка с перемоткой времени)
const context = await browser.newContext({
recordVideo: { dir: 'videos/' },
recordTrace: { dir: 'traces/' }
});
// Просмотр трассировки: npx playwright show-trace trace.zip
// Codegen (запись действий)
// npx playwright codegen https://example.com
// Режим headed со slomo
const browser = await chromium.launch({
headless: false,
slowMo: 1000
});
Победитель: Playwright - trace viewer обеспечивает отладку с перемоткой времени, inspector более отполирован.
Матрица Решений
Выбирайте Puppeteer Если:
- ✅ Вам нужна только поддержка Chrome/Chromium
- ✅ Вы создаете веб-скрейперы
- ✅ Вам нужна легковесная автоматизация
- ✅ Вам нужно генерировать PDF или скриншоты
- ✅ У вас существующая кодовая база Puppeteer
- ✅ Вы предпочитаете минимальные абстракции
Выбирайте Playwright Если:
- ✅ Вам нужно кросс-браузерное тестирование (Firefox, Safari)
- ✅ Вы создаете E2E тестовые наборы
- ✅ Вам нужен интегрированный test runner
- ✅ Вам нужна продвинутая отладка (trace viewer)
- ✅ Вам нужно автоматическое ожидание и повторы
- ✅ Вам нужна эмуляция мобильных/планшетов
- ✅ Вы начинаете новый проект
Заключение
И Puppeteer, и Playwright являются отличными инструментами автоматизации браузеров, каждый превосходит в разных сценариях. Puppeteer остается предпочтительным выбором для веб-скрейпинга, генерации PDF и автоматизации специфичной для Chrome, где его зрелость и простота сияют. Playwright, однако, появился как превосходное решение для комплексного E2E тестирования, предлагая подлинную поддержку кросс-браузерности, мощные возможности отладки и опинионированный фреймворк тестирования, который решает общие болевые точки.
Для новых проектов автоматизации тестирования преимущества Playwright—истинная поддержка кросс-браузерности, trace viewer, автоматическое ожидание и интегрированный test runner—делают его рекомендуемым выбором. Для скрейпинга, генерации PDF или автоматизации только Chrome легковесный подход и зрелая экосистема Puppeteer остаются привлекательными.
Хорошие новости: миграция между ними относительно проста благодаря схожести API, позволяя командам переключаться по мере развития потребностей.