Перейти к основному содержимому

Сеть

Введение

Playwright предоставляет API для мониторинга и модификации сетевого трафика браузера, как HTTP, так и HTTPS. Любые запросы, которые делает страница, включая XHR и fetch запросы, могут быть отслежены, изменены и обработаны.

Мокирование API

Ознакомьтесь с нашим руководством по мокированию API, чтобы узнать больше о том, как

  • мокировать API-запросы и никогда не обращаться к API
  • выполнять API-запрос и изменять ответ
  • использовать HAR-файлы для мокирования сетевых запросов.

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

Выполнение HTTP-аутентификации.

context = browser.new_context(
http_credentials={"username": "bill", "password": "pa55w0rd"}
)
page = context.new_page()
page.goto("https://example.com")

HTTP-прокси

Вы можете настроить страницы для загрузки через HTTP(S) прокси или SOCKSv5. Прокси может быть установлен глобально для всего браузера или для каждого контекста браузера отдельно.

Вы можете дополнительно указать имя пользователя и пароль для HTTP(S) прокси, а также указать хосты, которые нужно обойти прокси.

Вот пример глобального прокси:

browser = chromium.launch(proxy={
"server": "http://myproxy.com:3128",
"username": "usr",
"password": "pwd"
})

Также возможно указать его для каждого контекста:

browser = chromium.launch()
context = browser.new_context(proxy={"server": "http://myproxy.com:3128"})

Сетевые события

Вы можете отслеживать все Request и Response:

from playwright.sync_api import sync_playwright, Playwright

def run(playwright: Playwright):
chromium = playwright.chromium
browser = chromium.launch()
page = browser.new_page()
# Подписка на события "request" и "response".
page.on("request", lambda request: print(">>", request.method, request.url))
page.on("response", lambda response: print("<<", response.status, response.url))
page.goto("https://example.com")
browser.close()

with sync_playwright() as playwright:
run(playwright)

Или дождаться сетевого ответа после нажатия кнопки с помощью page.expect_response():

# Используйте шаблон URL с глобальными символами
with page.expect_response("**/api/fetch_data") as response_info:
page.get_by_text("Update").click()
response = response_info.value

Вариации

Ожидание Response с page.expect_response()

# Используйте регулярное выражение
with page.expect_response(re.compile(r"\.jpeg$")) as response_info:
page.get_by_text("Update").click()
response = response_info.value

# Используйте предикат, принимающий объект ответа
with page.expect_response(lambda response: token in response.url) as response_info:
page.get_by_text("Update").click()
response = response_info.value

Обработка запросов

page.route(
"**/api/fetch_data",
lambda route: route.fulfill(status=200, body=test_data))
page.goto("https://example.com")

Вы можете мокировать конечные точки API, обрабатывая сетевые запросы в вашем скрипте Playwright.

Вариации

Настройте маршрут для всего контекста браузера с помощью browser_context.route() или страницы с помощью page.route(). Это будет применяться к всплывающим окнам и открытым ссылкам.

context.route(
"**/api/login",
lambda route: route.fulfill(status=200, body="accept"))
page.goto("https://example.com")

Изменение запросов

# Удаление заголовка
def handle_route(route):
headers = route.request.headers
del headers["x-secret"]
route.continue_(headers=headers)
page.route("**/*", handle_route)

# Продолжение запросов как POST.
page.route("**/*", lambda route: route.continue_(method="POST"))

Вы можете продолжать запросы с модификациями. Пример выше удаляет HTTP-заголовок из исходящих запросов.

Прерывание запросов

Вы можете прерывать запросы, используя page.route() и route.abort().

page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())

# Прерывание на основе типа запроса
page.route("**/*", lambda route: route.abort() if route.request.resource_type == "image" else route.continue_())

Изменение ответов

Чтобы изменить ответ, используйте APIRequestContext для получения оригинального ответа, а затем передайте ответ в route.fulfill(). Вы можете переопределить отдельные поля в ответе с помощью опций:

def handle_route(route: Route) -> None:
# Получить оригинальный ответ.
response = route.fetch()
# Добавить префикс к заголовку.
body = response.text()
body = body.replace("<title>", "<title>My prefix:")
route.fulfill(
# Передать все поля из ответа.
response=response,
# Переопределить тело ответа.
body=body,
# Принудительно установить тип контента как html.
headers={**response.headers, "content-type": "text/html"},
)

page.route("**/title.html", handle_route)

Шаблоны URL с глобами

Playwright использует упрощённые glob-шаблоны для сопоставления URL в методах перехвата сети, таких как page.route() или page.expect_response(). Эти шаблоны поддерживают базовые подстановочные символы:

  1. Звёздочки:
    • Одна * сопоставляет любые символы, кроме /
    • Две ** сопоставляют любые символы, включая /
  2. Знак вопроса ? сопоставляет только сам символ ?. Если нужно сопоставить любой символ, используйте *.
  3. Фигурные скобки {} можно использовать, чтобы сопоставлять список вариантов, разделённых запятыми ,
  4. Обратный слэш \ можно использовать для экранирования любых специальных символов (учтите, что сам обратный слэш нужно экранировать как \\)

Примеры:

  • https://example.com/*.js сопоставляет https://example.com/file.js, но не https://example.com/path/file.js
  • https://example.com/?page=1 сопоставляет https://example.com/?page=1, но не https://example.com
  • **/*.js сопоставляет и https://example.com/file.js, и https://example.com/path/file.js
  • **/*.{png,jpg,jpeg} сопоставляет все запросы изображений

Важные заметки:

  • Шаблон с глобами должен соответствовать всему URL, а не только его части.
  • При использовании глобов для сопоставления URL учитывайте полную структуру URL, включая протокол и разделители пути.
  • Для более сложных требований к сопоставлению рассмотрите возможность использования [RegExp] вместо шаблонов с глобами.

WebSockets

Playwright поддерживает инспекцию, имитацию и модификацию WebSockets из коробки. Ознакомьтесь с нашим руководством по имитации API, чтобы узнать, как имитировать WebSockets.

Каждый раз, когда создается WebSocket, срабатывает событие page.on("websocket"). Это событие содержит экземпляр WebSocket для дальнейшей инспекции фреймов веб-сокета:

def on_web_socket(ws):
print(f"WebSocket opened: {ws.url}")
ws.on("framesent", lambda payload: print(payload))
ws.on("framereceived", lambda payload: print(payload))
ws.on("close", lambda payload: print("WebSocket closed"))

page.on("websocket", on_web_socket)

Отсутствующие сетевые события и Service Workers

Встроенные методы Playwright — browser_context.route() и page.route() — позволяют тестам нативно маршрутизировать запросы, а также выполнять мокирование и перехват.

Если вы используете нативные browser_context.route() и page.route(), и вам кажется, что сетевые события пропадают, отключите Service Worker’ы, установив service_workers в 'block'.

Возможно, вы используете инструмент для моков, например Mock Service Worker (MSW). Хотя он отлично работает для мокирования ответов, он добавляет собственный Service Worker, который перехватывает сетевые запросы — из‑за этого они становятся невидимыми для browser_context.route() и page.route(). Если вам нужно и тестировать сеть, и делать моки, рассмотрите использование встроенных browser_context.route() и page.route() для мокирования ответов.