Playwright Python — справочникНа главный сайт

Мониторинг запросов и ответов

Playwright позволяет подписываться на все сетевые события страницы или контекста. Это полезно для логирования, проверки заголовков и отладки.

page.on("request")

def on_request(request):
    print(f"Отправлен: {request.method} {request.url}")

page.on("request", on_request)
page.goto("https://example.com")

page.on("response")

def on_response(response):
    print(f"Получен: {response.status} {response.url}")
    print(f"  Заголовки: {response.headers}")

page.on("response", on_response)

page.on("requestfailed")

def on_failed(request):
    print(f"Ошибка: {request.url} — {request.failure}")

page.on("requestfailed", on_failed)

page.on("requestfinished")

def on_finished(request):
    print(f"Завершён: {request.url}")

page.on("requestfinished", on_finished)

Перехват и маршрутизация (Route)

Метод page.route() перехватывает запросы, соответствующие glob-паттерну, и передаёт управление в обработчик. В обработчике можно продолжить запрос, изменить его, подменить ответ или заблокировать.

Блокировка CSS

page.route("**/*.css", lambda route: route.abort())

Блокировка изображений

page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
Совет: Блокировка ресурсов ускоряет загрузку страницы при тестировании, когда изображения и стили не важны для проверяемой функциональности.

Подмена API-ответа

def handle_api(route):
    route.fulfill(
        status=200,
        content_type="application/json",
        body='{"users": [{"id": 1, "name": "Анна"}]}'
    )

page.route("**/api/**", handle_api)

Ответ из файла

import json

with "mock.json" as f:
    mock_data = json.load(f)

def mock_with_file(route):
    route.fulfill(
        status=200,
        path="mock.json",
        headers={"Content-Type": "application/json"}
    )

page.route("**/api/data", mock_with_file)
Примечание: Параметр path в route.fulfill() принимает путь к файлу относительно рабочей директории теста. Файл должен существовать на момент вызова.

Модификация запроса (route.continue_)

route.continue_() позволяет изменить запрос перед отправкой: URL, метод, заголовки и тело.

def modify_request(route):
    route.continue_(
        headers={
            "Authorization": "Bearer test-token",
            "X-Custom": "true"
        }
    )

page.route("**/api/**", modify_request)

# Изменение URL и метода
page.route("**/old-endpoint", lambda r: r.continue_(
    url="https://new-api.example.com/v2/data",
    method="POST",
    post_data='{"key": "value"}'
))

route.abort() — причины отклонения

Метод route.abort(error_code) отклоняет запрос с указанием причины. Доступные коды ошибок:

КодОписание
"aborted"Операция отменена
"accessdenied"Доступ запрещён
"addressunreachable"Адрес недоступен
"blockedlyresource"Ресурс заблокирован расширением
"connectionrefused"Соединение отклонено
"connectionreset"Соединение сброшено
"internetdisconnected"Интернет отключён
"namenotresolved"DNS не найден
"timedout"Тайм-аут
# Блокировка с разными причинами
page.route("**/analytics.js",
           lambda r: r.abort("connectionrefused"))
page.route("**/tracking.gif",
           lambda r: r.abort("aborted"))

route.fetch — выполнение реального запроса

Метод route.fetch() выполняет настоящий HTTP-запрос из обработчика и возвращает ответ. Это позволяет перехватить запрос, проверить реальный ответ и при необходимости изменить его.

def intercept_and_check(route):
    response = route.fetch()  # реальный запрос
    body = response.text()
    # Модифицируем ответ на лету
    modified = body.replace("старое", "новое")
    route.fulfill(
        status=response.status,
        headers=response.headers,
        body=modified
    )

page.route("**/*.html", intercept_and_check)

Glob-паттерны для route

Playwright использует glob-паттерны для фильтрации URL в page.route():

ПаттернЗначениеПример
**Любая последовательность символов, включая слеши**/api/**
*Любая последовательность, кроме слеша*.css
?Ровно один любой символfile?.html
{a,b}Альтернативы (или)*.{png,jpg}
# Примеры паттернов
page.route("**", handler)                     # все запросы
page.route("**/*.css", handler)              # только CSS
page.route("https://api.example.com/**", handler)
page.route("**/*.{png,jpg,jpeg,gif,svg}", handler)
page.route("**/data?v=?", handler)            # data?v=1, data?v=2

page.unroute() — удаление route

Метод page.unroute(pattern) удаляет ранее установленный перехватчик. Если паттерн не указан, удаляются все.

# Удаление конкретного route
page.unroute("**/*.css")

# Удаление всех route
page.unroute()

HTTPS-аутентификация

Playwright поддерживает базовую HTTP-аутентификацию через параметр http_credentials в контексте.

context = browser.new_context(
    http_credentials={
        "username": "admin",
        "password": "secret"
    }
)
page = context.new_page()
page.goto("https://protected.example.com")
Примечание: Учётные данные базовой аутентификации отправляются автоматически для всех запросов в рамках контекста. Playwright ожидает 401-ответ и повторяет запрос с заголовком Authorization.

HTTP-прокси

Прокси-сервер настраивается при запуске браузера через параметр proxy.

browser = p.chromium.launch(
    proxy={
        "server": "http://proxy.example.com:8080",
        "username": "user",    # опционально
        "password": "pass"     # опционально
    }
)

Поддерживаемые схемы прокси: http, https, socks5, socks5h.

WebSocket

Playwright отслеживает WebSocket-соединения через событие "websocket" на странице или контексте.

def on_websocket(ws):
    print(f"WebSocket: {ws.url}")

    def on_frame_sent(data):
        print(f"Отправлено: {data}")

    def on_frame_received(data):
        print(f"Получено: {data}")

    ws.on("framesent", on_frame_sent)
    ws.on("framereceived", on_frame_received)

page.on("websocket", on_websocket)
page.goto("https://example.com/chat")

Сервис-воркеры

Можно заблокировать регистрацию сервис-воркеров при создании контекста:

context = browser.new_context(
    service_workers='block'
)

Это полезно, когда сервис-воркеры кешируют ресурсы и мешают воспроизводимости тестов.

Полный пример: перехват и подмена API

from playwright.sync_api import sync_playwright
import json

MOCK_USERS = {
    "users": [
        {"id": 1, "name": "Иван", "role": "admin"},
        {"id": 2, "name": "Мария", "role": "user"}
    ]
}

def test_network_interception():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()

        # Логируем все запросы
        page.on("request", lambda req:
                print(f"  Запрос: {req.method} {req.url}"))

        # Блокируем аналитику
        page.route("**/analytics.js",
                   lambda r: r.abort("blockedlyresource"))

        # Подменяем API пользователей
        page.route("**/api/users", lambda r: r.fulfill(
            status=200,
            content_type="application/json",
            body=json.dumps(MOCK_USERS)
        ))

        # Блокируем изображения
        page.route("**/*.{png,jpg,jpeg,gif}",
                   lambda r: r.abort())

        page.goto("https://test.example.com/dashboard")

        // Проверяем, что подмена сработала
        users_text = page.text_content("#users-list")
        assert "Иван" in users_text
        assert "Мария" in users_text

        browser.close()

test_network_interception()

Полный пример: мониторинг трафика

from playwright.sync_api import sync_playwright

def collect_network_logs(url):
    logs = []

    def on_request(req):
        logs.append({
            "type": "request",
            "method": req.method,
            "url": req.url,
            "headers": dict(req.headers)
        })

    def on_response(resp):
        logs.append({
            "type": "response",
            "status": resp.status,
            "url": resp.url
        })

    def on_failed(req):
        logs.append({
            "type": "failed",
            "url": req.url,
            "error": str(req.failure)
        })

    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.on("request", on_request)
        page.on("response", on_response)
        page.on("requestfailed", on_failed)
        page.goto(url, wait_until="networkidle")
        browser.close()

    return logs

# Сбор логов и сохранение
network_logs = collect_network_logs("https://example.com")
with "network_report.json", "w" as f:
    json.dump(network_logs, f, indent=2, ensure_ascii=False)
Важно: Обработчики route выполняются синхронно. Если внутри обработчика требуется асинхронная операция, используйте async-версию page.route_async() и await route.continue_async().