← Browser Run / browser-run
Playwright
Playwright ↗ это open source пакет, разработанный Microsoft, который умеет выполнять задачи автоматизации браузера. Его обычно используют для написания фронтенд-тестов, создания скриншотов или обхода страниц.
Команда Workers создала форк версия Playwright ↗ который был изменён для совместимости с Cloudflare Workers и Browser Run.
Наша версия имеет открытый исходный код, её можно найти в форк Playwright от Cloudflare ↗. Пакет npm можно установить из npmjs ↗ как @cloudflare/playwright ↗:
npm i -D @cloudflare/playwrightИспользуйте Playwright в Worker
В этом пример ↗, вы будете запускать тесты Playwright в Cloudflare Worker с помощью todomvc ↗ приложение.
Если вы хотите пропустить шаги и быстро начать, выберите Deploy to Cloudflare ниже.
Убедитесь, что у вас есть привязка browser настроенный в конфигурационном файле Wrangler:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "cloudflare-playwright-example",
"main": "src/index.ts",
"workers_dev": true,
"compatibility_flags": ["nodejs_compat"],
// Set this to today's date
"compatibility_date": "2026-08-28",
"upload_source_maps": true,
"browser": {
"binding": "MYBROWSER",
},
}"$schema" = "./node_modules/wrangler/config-schema.json"
name = "cloudflare-playwright-example"
main = "src/index.ts"
workers_dev = true
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-08-28"
upload_source_maps = true
[browser]
binding = "MYBROWSER"Установите npm-пакет:
npm i -D @cloudflare/playwrightРассмотрим несколько примеров использования Playwright:
Сделайте скриншот
Автоматизация браузера часто применяется для создания снимков веб-страниц. Этот скрипт указывает браузеру перейти на https://demo.playwright.dev/todomvc ↗, создайте несколько элементов, сделайте снимок страницы и верните изображение в ответе.
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
const img = await page.screenshot();
await browser.close();
return new Response(img, {
headers: {
"Content-Type": "image/png",
},
});
},
};Trace
Трассировка Playwright представляет собой подробный журнал выполнения вашего сценария: она фиксирует такие данные, как клики пользователя и переходы по страницам, скриншоты страницы, а также все сообщения консоли, используемые для отладки. Разработчики могут снять trace.zip файл и либо откройте его локально ↗ или загрузите его в Playwright Trace Viewer ↗, GUI-инструмент, который помогает изучать данные.
Пример worker, который генерирует файл трассировки:
import fs from "fs";
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
// Start tracing before navigating to the page
await page.context().tracing.start({ screenshots: true, snapshots: true });
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
// Stop tracing and save the trace to a zip file
await page.context().tracing.stop({ path: "trace.zip" });
await browser.close();
const file = await fs.promises.readFile("trace.zip");
return new Response(file, {
status: 200,
headers: {
"Content-Type": "application/zip",
},
});
},
};Утверждения
Один из самых распространённых сценариев использования Playwright: тестирование программного обеспечения. API Playwright включает функции проверки утверждений (assertions), подробнее см. Утверждения ↗ в документации Playwright. Вот пример Worker, который выполняет expect() тестовые утверждения todomvc ↗ демонстрационную страницу:
import { launch } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
await expect(page.getByTestId("todo-title")).toHaveCount(TODO_ITEMS.length);
await Promise.all(
TODO_ITEMS.map((value, index) =>
expect(page.getByTestId("todo-title").nth(index)).toHaveText(value),
),
);
},
};Storage state
Playwright поддерживает состояние хранилища ↗ чтобы получить и сохранить cookie-файлы и другие данные хранилища. В этом примере вы будете использовать состояние хранилища, чтобы сохранять cookie-файлы и другие данные хранилища в Workers KV.
Сначала убедитесь, что у вас есть пространство имён KV. Создать новое можно с помощью команды:
npx wrangler kv namespace create KVЗатем добавьте пространство имён KV в файл конфигурации Wrangler:
{
"name": "storage-state-examples",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
// Set this to today's date
"compatibility_date": "2026-08-28",
"browser": {
"binding": "MYBROWSER",
},
"kv_namespaces": [
{
"binding": "KV",
"id": "<YOUR-KV-NAMESPACE-ID>",
},
],
}name = "storage-state-examples"
main = "src/index.ts"
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-08-28"
[browser]
binding = "MYBROWSER"
[[kv_namespaces]]
binding = "KV"
id = "<YOUR-KV-NAMESPACE-ID>"Теперь вы можете использовать storage state для сохранения файлов cookie и других данных хранилища в KV:
// gets persisted storage state from KV or undefined if it does not exist
const storageStateJson = await env.KV.get("storageState");
const storageState = storageStateJson
? ((await JSON.parse(
storageStateJson,
)) as BrowserContextOptions["storageState"])
: undefined;
await using browser = await launch(env.MYBROWSER);
// creates a new context with storage state persisted in KV
await using context = await browser.newContext({ storageState });
await using page = await context.newPage();
// do some actions on the page that may update client-side storage
// gets updated storage state: cookies, localStorage, and IndexedDB
const updatedStorageState = await context.storageState({ indexedDB: true });
// persists updated storage state in KV
await env.KV.put("storageState", JSON.stringify(updatedStorageState));Keep Alive
Если пользователи не указывают browser.close() то экземпляр браузера останется открытым, готовым к повторному подключению, а также повторно используется но по умолчанию она автоматически закрывается через 1 минуту бездействия. Пользователи могут по желанию продлить время простоя до 10 минут с помощью keep_alive параметр, заданный в миллисекундах:
const browser = await playwright.launch(env.MYBROWSER, { keep_alive: 600000 });При таком подходе браузер остаётся открытым до 10 минут, даже если он неактивен.
Повторное использование сессии
Чтобы повысить производительность Worker для Browser Run, лучше всего повторно использовать сессии: не закрывайте браузер после завершения работы, а подключайтесь к той же сессии при каждом новом запросе. Playwright обрабатывает browser.close ↗ иначе, чем Puppeteer. В Playwright, если браузер был получен с помощью connect сессии, сессия отключится. Если браузер был получен с помощью launch сессии, сессия закроется.
import { env } from "cloudflare:workers";
import { acquire, connect } from "@cloudflare/playwright";
async function reuseSameSession() {
// acquires a new session
const { sessionId } = await acquire(env.BROWSER);
for (let i = 0; i < 5; i++) {
// connects to the session that was previously acquired
const browser = await connect(env.BROWSER, sessionId);
// ...
// this will disconnect the browser from the session, but the session will be kept alive
await browser.close();
}
}Задайте собственный User-Agent
Чтобы указать собственный user agent в Playwright, задайте его в опциях при создании нового browser context с помощью browser.newContext(). Все страницы, созданные впоследствии в этом контексте, будут использовать новый user agent. Это полезно, если целевой сайт показывает разное содержимое в зависимости от user agent.
const context = await browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
});Локальная отладка в headful-режиме (экспериментально)
При локальной разработке с wrangler dev или vite dev, Chrome по умолчанию запускается в режиме headless. Чтобы запустить Chrome в видимом режиме (headful), задайте X_BROWSER_HEADFUL переменную окружения:
X_BROWSER_HEADFUL=true npx wrangler devИли с помощью Cloudflare Vite plugin:
X_BROWSER_HEADFUL=true npx vite devЭто открывает окно браузера, чтобы вы могли наблюдать за работой автоматизации Playwright в режиме реального времени, что упрощает отладку навигации, выбора элементов и взаимодействия со страницей.
Управление сессиями
Чтобы упростить управление сессиями браузера, мы расширили Playwright API новыми методами:
Список открытых сеансов
playwright.sessions() выводит список текущих выполняющихся сессий. Результат будет выглядеть примерно так:
[
{
"connectionId": "2a2246fa-e234-4dc1-8433-87e6cee80145",
"connectionStartTime": 1711621704607,
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
"startTime": 1711621703708
},
{
"sessionId": "565e05fb-4d2a-402b-869b-5b65b1381db7",
"startTime": 1711621703808
}
]Обратите внимание, что сессия 478f4d7d-e943-40f6-a414-837d3736a1dc есть активное подключение Worker (connectionId=2a2246fa-e234-4dc1-8433-87e6cee80145), в то время как сеанс 565e05fb-4d2a-402b-869b-5b65b1381db7 бесплатен. Пока соединение активно, другие Worker не смогут подключиться к этой сессии.
Список последних сеансов
playwright.history() выводит список недавних сессий, как открытых, так и закрытых. Это удобно, чтобы оценить текущий объём использования.
[
{
"closeReason": 2,
"closeReasonText": "BrowserIdle",
"endTime": 1711621769485,
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
"startTime": 1711621703708
},
{
"closeReason": 1,
"closeReasonText": "NormalClosure",
"endTime": 1711123501771,
"sessionId": "2be00a21-9fb6-4bb2-9861-8cd48e40e771",
"startTime": 1711123430918
}
]Сессия 2be00a21-9fb6-4bb2-9861-8cd48e40e771 была закрыта явным образом с помощью browser.close() клиентом, тогда как сессия 478f4d7d-e943-40f6-a414-837d3736a1dc была закрыта из-за достижения максимального времени простоя (см. ограничения).
Эту информацию можно также посмотреть в панели управления, хотя и с небольшой задержкой.
Действующие лимиты
playwright.limits() выводит ваши текущие лимиты:
{
"activeSessions": [
{ "id": "478f4d7d-e943-40f6-a414-837d3736a1dc" },
{ "id": "565e05fb-4d2a-402b-869b-5b65b1381db7" }
],
"allowedBrowserAcquisitions": 1,
"maxConcurrentSessions": 2,
"timeUntilNextAllowedBrowserAcquisition": 0
}activeSessionsвыводит идентификаторы текущих открытых сессий.maxConcurrentSessionsопределяет, сколько браузеров может быть открыто одновременно.allowedBrowserAcquisitionsуказывает, можно ли открыть новую сессию браузера согласно ограничения на месте.timeUntilNextAllowedBrowserAcquisitionопределяет время ожидания перед запуском нового браузера.
Playwright API
Полный API Playwright можно найти по адресу Документация Playwright API ↗.
Перечисленные ниже возможности пока поддерживаются не полностью, но мы активно работаем над ними:
- Playwright Test ↗ кроме Утверждения ↗
- Компоненты ↗
- Firefox ↗, Android ↗ и Electron ↗, а также различных версий Chrome
- Видео ↗
Это неполный список : ожидайте быстрых изменений по мере того, как мы стремимся к более полному паритету с оригинальным набором функций. Также вы можете посмотреть последние результаты тестов ↗ для получения подробного и актуального списка полностью поддерживаемых функций.