Сеть (Network)
Мониторинг запросов, перехват и подмена трафика, WebSocket, прокси и аутентификация
Мониторинг запросов и ответов
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")
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().