Сеть
Введение
Playwright предоставляет API для мониторинга и модификации сетевого трафика браузера, как HTTP, так и HTTPS. Любые запросы, которые делает страница, включая XHR и fetch запросы, могут быть отслежены, изменены и обработаны.
Мокирование API
Ознакомьтесь с нашим руководством по мокированию API, чтобы узнать больше о том, как
- мокировать API-запросы и никогда не обращаться к API
- выполнять API-запрос и изменять ответ
- использовать HAR-файлы для мокирования сетевых запросов.
HTTP-аутентификация
Выполнение HTTP-аутентификации.
- Sync
- Async
context = browser.new_context(
http_credentials={"username": "bill", "password": "pa55w0rd"}
)
page = context.new_page()
page.goto("https://example.com")
context = await browser.new_context(
http_credentials={"username": "bill", "password": "pa55w0rd"}
)
page = await context.new_page()
await page.goto("https://example.com")
HTTP-прокси
Вы можете настроить страницы для загрузки через HTTP(S) прокси или SOCKSv5. Прокси может быть установлен глобально для всего браузера или для каждого контекста браузера отдельно.
Вы можете дополнительно указать имя пользователя и пароль для HTTP(S) прокси, а также указать хосты, которые нужно обойти прокси.
Вот пример глобального прокси:
- Sync
- Async
browser = chromium.launch(proxy={
"server": "http://myproxy.com:3128",
"username": "usr",
"password": "pwd"
})
browser = await chromium.launch(proxy={
"server": "http://myproxy.com:3128",
"username": "usr",
"password": "pwd"
})
Также возможно указать его для каждого контекста:
- Sync
- Async
browser = chromium.launch()
context = browser.new_context(proxy={"server": "http://myproxy.com:3128"})
browser = await chromium.launch()
context = await browser.new_context(proxy={"server": "http://myproxy.com:3128"})
Сетевые события
Вы можете отслеживать все Request и Response:
- Sync
- Async
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)
import asyncio
from playwright.async_api import async_playwright, Playwright
async def run(playwright: Playwright):
chromium = playwright.chromium
browser = await chromium.launch()
page = await 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))
await page.goto("https://example.com")
await browser.close()
async def main():
async with async_playwright() as playwright:
await run(playwright)
asyncio.run(main())
Или дождаться сетевого ответа после нажатия кнопки с помощью page.expect_response():
- Sync
- Async
# Используйте шаблон URL с глобальными символами
with page.expect_response("**/api/fetch_data") as response_info:
page.get_by_text("Update").click()
response = response_info.value
# Используйте шаблон URL с глобальными символами
async with page.expect_response("**/api/fetch_data") as response_info:
await page.get_by_text("Update").click()
response = await response_info.value
Вариации
Ожидание Response с page.expect_response()
- Sync
- Async
# Используйте регулярное выражение
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
# Используйте регулярное выражение
async with page.expect_response(re.compile(r"\.jpeg$")) as response_info:
await page.get_by_text("Update").click()
response = await response_info.value
# Используйте предикат, принимающий объект ответа
async with page.expect_response(lambda response: token in response.url) as response_info:
await page.get_by_text("Update").click()
response = await response_info.value
Обработка запросов
- Sync
- Async
page.route(
"**/api/fetch_data",
lambda route: route.fulfill(status=200, body=test_data))
page.goto("https://example.com")
await page.route(
"**/api/fetch_data",
lambda route: route.fulfill(status=200, body=test_data))
await page.goto("https://example.com")
Вы можете мокировать конечные точки API, обрабатывая сетевые запросы в вашем скрипте Playwright.
Вариации
Настройте маршрут для всего контекста браузера с помощью browser_context.route() или страницы с помощью page.route(). Это будет применяться к всплывающим окнам и открытым ссылкам.
- Sync
- Async
context.route(
"**/api/login",
lambda route: route.fulfill(status=200, body="accept"))
page.goto("https://example.com")
await context.route(
"**/api/login",
lambda route: route.fulfill(status=200, body="accept"))
await page.goto("https://example.com")
Изменение запросов
- Sync
- Async
# Удаление заголовка
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"))
# Удаление заголовка
async def handle_route(route):
headers = route.request.headers
del headers["x-secret"]
await route.continue_(headers=headers)
await page.route("**/*", handle_route)
# Продолжение запросов как POST.
await page.route("**/*", lambda route: route.continue_(method="POST"))
Вы можете продолжать запросы с модификациями. Пример выше удаляет HTTP-заголовок из исходящих запросов.
Прерывание запросов
Вы можете прерывать запросы, используя page.route() и route.abort().
- Sync
- Async
page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
# Прерывание на основе типа запроса
page.route("**/*", lambda route: route.abort() if route.request.resource_type == "image" else route.continue_())
await page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
# Прерывание на основе типа запроса
await page.route("**/*", lambda route: route.abort() if route.request.resource_type == "image" else route.continue_())
Изменение ответов
Чтобы изменить ответ, используйте APIRequestContext для получения оригинального ответа, а затем передайте ответ в route.fulfill(). Вы можете переопределить отдельные поля в ответе с помощью опций:
- Sync
- Async
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)
async def handle_route(route: Route) -> None:
# Получить оригинальный ответ.
response = await route.fetch()
# Добавить префикс к заголовку.
body = await response.text()
body = body.replace("<title>", "<title>My prefix:")
await route.fulfill(
# Передать все поля из ответа.
response=response,
# Переопределить тело ответа.
body=body,
# Принудительно установить тип контента как html.
headers={**response.headers, "content-type": "text/html"},
)
await page.route("**/title.html", handle_route)
Шаблоны URL с глобами
Playwright использует упрощенные шаблоны с глобами для сопоставления URL в методах перехвата сети, таких как page.route() или page.expect_response(). Эти шаблоны поддерживают базовые подстановочные знаки:
- Звездочки:
- Одиночная
*
соответствует любым символам, кроме/
- Двойная
**
соответствует любым символам, включая/
- Одиночная
- Вопросительный знак
?
соответствует любому одиночному символу, кроме/
- Фигурные скобки
{}
могут использоваться для сопоставления списка опций, разделенных запятыми,
- Квадратные скобки
[]
могут использоваться для сопоставления набора символов - Обратный слэш
\
может использоваться для экранирования любых специальных символов (обратите внимание, что сам обратный слэш нужно экранировать как\\
)
Примеры:
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
**/v[0-9]*
соответствуетhttps://example.com/v1/
, но неhttps://example.com/vote/
**/*.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() позволяют вашим тестам нативно маршрутизировать запросы и выполнять имитацию и перехват.
- Если вы используете нативные функции Playwright browser_context.route() и page.route(), и кажется, что сетевые события отсутствуют, отключите Service Workers, установив service_workers в
'block'
. - Возможно, вы используете инструмент имитации, такой как Mock Service Worker (MSW). Хотя этот инструмент работает из коробки для имитации ответов, он добавляет свой собственный Service Worker, который берет на себя сетевые запросы, делая их невидимыми для browser_context.route() и page.route(). Если вас интересует как тестирование сети, так и имитация, рассмотрите возможность использования встроенных функций browser_context.route() и page.route() для имитации ответов.
- Если вас интересует не только использование Service Workers для тестирования и имитации сети, но и маршрутизация и прослушивание запросов, сделанных самими Service Workers, пожалуйста, ознакомьтесь с этой экспериментальной функцией.