Playwright для JavaScript (Node.js): полное руководство с примерами
Playwright — это современная библиотека с открытым исходным кодом для автоматизации браузеров, разработанная компанией Microsoft. Она предоставляет единый API для управления Chromium, Firefox и WebKit (движок Safari) через протокол DevTools. В отличие от Selenium, Playwright изначально проектировался для работы с современными веб-приложениями и поддерживает такие сценарии, как перехват сетевых запросов, эмуляция мобильных устройств, работа с несколькими страницами и вкладками, а также автоматическое ожидание элементов.
Основное назначение Playwright — это end-to-end (E2E) тестирование веб-приложений. Однако его возможности выходят далеко за рамки тестирования: библиотека используется для парсинга динамических сайтов (SPA), автоматизации рутинных задач (например, заполнение форм), создания скриншотов и PDF-отчётов, а также для мониторинга производительности веб-страниц. Благодаря поддержке всех трёх основных браузерных движков, вы можете быть уверены, что ваше приложение работает корректно в любом окружении.
Ключевое преимущество Playwright перед конкурентами — это скорость и надёжность. Библиотека использует асинхронную архитектуру на основе промисов, автоматически ожидает появления элементов (нет необходимости писать sleep()), а также умеет работать с iframe, всплывающими окнами и перехватывать консольные сообщения браузера. Playwright также предоставляет встроенный генератор тестов (codegen), который записывает ваши действия в браузере и превращает их в готовый код.
Установка Playwright
Для установки Playwright в проекте на Node.js выполните следующие команды в терминале:
npm init -y
npm install playwright
npx playwright install
Первая команда создаёт файл package.json, вторая — устанавливает саму библиотеку, а третья — загружает браузеры (Chromium, Firefox и WebKit). Если вы хотите установить только один браузер, используйте npx playwright install chromium.
Для TypeScript дополнительно установите типы:
npm install -D @playwright/test
Если вы используете Playwright в качестве тестового фреймворка, рекомендуется установить @playwright/test, который включает в себя тест-раннер, фикстуры и конфигурацию.
Быстрый старт: минимальный рабочий пример
Создайте файл example.js и вставьте следующий код:
const { chromium } = require('playwright');
(async () => {
// Запускаем браузер (headless: true — без графического интерфейса)
const browser = await chromium.launch({ headless: true });
// Создаём новую страницу (вкладку)
const page = await browser.newPage();
// Переходим на сайт
await page.goto('https://example.com');
// Получаем заголовок страницы
const title = await page.title();
console.log('Заголовок страницы:', title);
// Делаем скриншот
await page.screenshot({ path: 'example.png' });
// Закрываем браузер
await browser.close();
})();
Запустите скрипт командой node example.js. В результате вы увидите в консоли заголовок страницы, а в папке появится файл example.png со скриншотом.
Этот пример демонстрирует базовый цикл работы с Playwright: запуск браузера, создание страницы, навигация, взаимодействие с DOM и закрытие.
Основные методы и классы Playwright
Ниже приведены ключевые методы и классы библиотеки. Для каждого указана сигнатура, краткое описание и рабочий пример.
1. browser.newPage()
Сигнатура: async browser.newPage(options?: BrowserContextOptions): Promise<Page>
Описание: Создаёт новую страницу (вкладку) в текущем контексте браузера. Каждая страница изолирована от других — у неё свои cookies, localStorage и сессионные данные.
const { chromium } = require('playwright');
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
2. page.goto(url, options?)
Сигнатура: async page.goto(url: string, options?: { waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit' }): Promise<Response | null>
Описание: Переходит по указанному URL. Параметр waitUntil определяет, когда считать навигацию завершённой. По умолчанию — 'load' (событие window.load).
await page.goto('https://github.com', { waitUntil: 'networkidle' });
3. page.click(selector, options?)
Сигнатура: async page.click(selector: string, options?: { button?: 'left' | 'right' | 'middle', clickCount?: number, delay?: number }): Promise<void>
Описание: Кликает по элементу, найденному по CSS-селектору. Playwright автоматически ожидает, пока элемент станет видимым и стабильным (не анимированным).
await page.click('button#submit'); // Клик по кнопке с id="submit"
await page.click('a.link', { button: 'right' }); // Правый клик
4. page.fill(selector, value)
Сигнатура: async page.fill(selector: string, value: string): Promise<void>
Описание: Очищает поле ввода (input, textarea) и вводит указанный текст. Это предпочтительный способ ввода текста, так как он эмулирует реальное поведение пользователя.
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('#message', 'Привет, мир!');
5. page.waitForSelector(selector, options?)
Сигнатура: async page.waitForSelector(selector: string, options?: { state?: 'attached' | 'detached' | 'visible' | 'hidden', timeout?: number }): Promise<ElementHandle>
Описание: Ожидает появления элемента в DOM. Параметр state позволяет уточнить условие: 'visible' — элемент видим, 'hidden' — скрыт.
await page.waitForSelector('.loading-spinner', { state: 'hidden' }); // Ждём, пока спиннер исчезнет
6. page.evaluate(pageFunction, arg?)
Сигнатура: async page.evaluate(pageFunction: Function | string, arg?: any): Promise<any>
Описание: Выполняет JavaScript-код в контексте страницы. Позволяет получить доступ к глобальным переменным, изменить DOM или вызвать функции, недоступные через Playwright API.
const result = await page.evaluate(() => {
return document.title + ' - ' + window.location.href;
});
console.log(result); // Пример: "Example Domain - https://example.com"
7. page.screenshot(options?)
Сигнатура: async page.screenshot(options?: { path?: string, fullPage?: boolean, clip?: { x: number, y: number, width: number, height: number } }): Promise<Buffer>
Описание: Делает скриншот текущей страницы. Если fullPage: true, захватывается вся страница, включая прокручиваемые области.
await page.screenshot({ path: 'fullpage.png', fullPage: true });
// Скриншот только определённой области
await page.screenshot({ path: 'clip.png', clip: { x: 100, y: 100, width: 300, height: 200 } });
8. page.setViewportSize(viewportSize)
Сигнатура: async page.setViewportSize(viewportSize: { width: number, height: number }): Promise<void>
Описание: Изменяет размер окна просмотра (viewport). Полезно для тестирования адаптивного дизайна или эмуляции мобильных устройств.
await page.setViewportSize({ width: 375, height: 812 }); // iPhone X
9. page.waitForNavigation(options?)
Сигнатура: async page.waitForNavigation(options?: { url?: string | RegExp, waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' }): Promise<Response | null>
Описание: Ожидает завершения навигации. Часто используется после клика по ссылке или отправки формы, когда нужно дождаться загрузки новой страницы.
await page.click('a.next-page');
await page.waitForNavigation(); // Ждём, пока загрузится следующая страница
10. browser.newContext(options?)
Сигнатура: async browser.newContext(options?: BrowserContextOptions): Promise<BrowserContext>
Описание: Создаёт новый изолированный контекст браузера. Контексты позволяют симулировать несколько независимых сессий (разные пользователи, cookies, localStorage) в одном браузере.
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
Реальный практический пример: парсинг интернет-магазина
Рассмотрим сценарий: нам нужно собрать информацию о товарах со страницы каталога интернет-магазина (название, цена, ссылка). Сайт использует динамическую подгрузку (JavaScript). Playwright отлично справляется с такими задачами.
const { chromium } = require('playwright');
async function parseCatalog(url) {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1280, height: 720 },
locale: 'ru-RU'
});
const page = await context.newPage();
try {
// Переходим на страницу каталога
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
// Ждём появления карточек товаров
await page.waitForSelector('.product-card', { timeout: 10000 });
// Прокручиваем страницу вниз, чтобы подгрузить все товары (если есть lazy load)
await page.evaluate(async () => {
await new Promise((resolve) => {
let totalHeight = 0;
const distance = 300;
const timer = setInterval(() => {
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= document.body.scrollHeight) {
clearInterval(timer);
resolve();
}
}, 200);
});
});
// Извлекаем данные
const products = await page.evaluate(() => {
const items = [];
const cards = document.querySelectorAll('.product-card');
cards.forEach(card => {
const title = card.querySelector('.product-title')?.innerText.trim();
const price = card.querySelector('.product-price')?.innerText.trim();
const link = card.querySelector('a')?.href;
if (title && price) {
items.push({ title, price, link });
}
});
return items;
});
console.log(`Найдено товаров: ${products.length}`);
console.log('Первый товар:', products[0]);
// Сохраняем в JSON
const fs = require('fs');
fs.writeFileSync('products.json', JSON.stringify(products, null, 2));
return products;
} catch (error) {
console.error('Ошибка при парсинге:', error.message);
await page.screenshot({ path: 'error.png' });
throw error;
} finally {
await browser.close();
}
}
// Запуск
parseCatalog('https://example-shop.com/catalog')
.then(data => console.log('Готово!'))
.catch(err => console.error('Фатальная ошибка:', err));
В этом примере мы:
- Создаём изолированный контекст с настройками viewport и локали
- Ожидаем полной загрузки страницы (
networkidle) - Прокручиваем страницу для подгрузки контента
- Извлекаем данные через
page.evaluate() - Сохраняем результат в JSON
- Обрабатываем ошибки и делаем скриншот при сбое
Советы по производительности и типичные ошибки
Советы по производительности
- Используйте контексты вместо новых браузеров. Создание нового контекста (
browser.newContext()) значительно быстрее, чем запуск нового экземпляра браузера. Для параллельных тестов используйте один браузер и несколько контекстов. - Отключайте ненужные функции. Если вам не нужны скриншоты или видео, отключите их в конфигурации. Также можно отключить загрузку изображений и CSS для ускорения:
const context = await browser.newContext({
viewport: { width: 1280, height: 720 },
userAgent