AI Agents PRO: LangGraph, AutoGen и LLMOps в продакшнеИнструменты и интеграцииБраузерные агенты (Playwright)

Браузерные агенты (Playwright)

Уроки курсаБраузерные агенты (Playwright)

Введение в Playwright

Представьте, что вам нужно протестировать веб-приложение: зайти на сайт, заполнить форму, нажать кнопку, проверить результат. Делать это вручную каждый раз — утомительно и медленно. Или другая задача: собрать данные с сайта, который загружает контент через JavaScript. Обычные HTTP-запросы не помогут — нужен настоящий браузер.

Playwright — это библиотека для автоматизации браузеров, позволяющая управлять Chromium, Firefox и WebKit из Python. Как правило, её используют для автоматизации тестов веб-приложений, скрапинга данных и симуляции действий пользователя. Основное преимущество Playwright в том, что она поддерживает асинхронное выполнение, надежно работает с динамическими страницами и предоставляет мощные инструменты для работы с селекторами и ожиданиями.

Удобные функции этого фреймворка:

  • Поддерживает Chromium, Firefox и WebKit — можно тестировать сайты под разными браузерами.
  • Полностью работает с асинхронными страницами (SPA, динамический контент).
  • Позволяет делать скриншоты и видео для диагностики тестов.
  • Есть синхронное и асинхронное API, что удобно для разных сценариев.

Запуск браузера

Для начала работы с Playwright необходимо установить библиотеку. Это можно сделать с помощью команды:

pip install playwright

После установки необходимо инициализировать браузеры (скачать бинарные файлы Chromium, Firefox и WebKit):

python -m playwright install

Теперь можно написать код для запуска браузера. Пример кода для открытия страницы выглядит следующим образом:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)  # Запуск браузера Chromium
    page = browser.new_page()  # Создание новой страницы
    page.goto("https://example.com")  # Переход на сайт
    print(page.title())  # Вывод заголовка страницы
    browser.close()  # Закрытие браузера

В данном примере используется синхронный API Playwright. Браузер запускается, открывается новая страница, осуществляется переход на указанный URL, и выводится заголовок страницы.

Пояснения:

  • headless=True — браузер работает без графического интерфейса (по умолчанию). Удобно для серверов или CI/CD.
  • headless=False — можно визуально наблюдать за действиями браузера. Полезно при отладке.
  • page.goto(url) — открывает страницу. Можно указать таймаут: page.goto(url, timeout=10000).

Работа с селекторами — как найти элементы на странице

После загрузки страницы нужно найти конкретные элементы: кнопку для клика, поле для ввода текста, заголовок для проверки. Для этого используются селекторы — способы описать, какой именно элемент нас интересует.

Селекторы позволяют находить элементы на веб-странице. Playwright поддерживает несколько типов селекторов:

CSS-селекторы:

page.locator("button.submit")

Найдет все кнопки с классом submit.

XPath:

page.locator("//button[@id='submit']")

Позволяет точнее находить элементы, особенно в сложной структуре HTML. В этом примере:

  • // — область поиска по всему документу. Не важно, где находится элемент: в <body>, внутри <div> или глубоко вложен, в отличие от /, который задаёт жёсткий путь от корня.
  • button — тип узла. Ищем HTML-элемент <button>. Другие элементы (input, a, div) не подойдут.
  • [@id='submit'] — фильтр. Квадратные скобки — условие отбора. @id — обращение к атрибуту id. 'submit' — точное значение.
  • В итоге: выбрать со всей страницы только те <button>, у которых id="submit".

Текстовые селекторы:

page.locator("text=Отправить")

Ищет элемент по тексту.

Лучше использовать locator, а не старый query_selector, потому что локатор поддерживает встроенные ожидания и автоматически ждёт появления и готовности элементов для взаимодействия.

Ожидания — работа с динамическим контентом

Современные сайты часто загружают контент динамически: кнопка появляется после загрузки JavaScript, данные подгружаются через API. Если попытаться кликнуть на элемент, которого ещё нет на странице, получим ошибку. Для этого нужны ожидания.

Ожидания необходимы для корректного взаимодействия с элементами, которые могут загружаться асинхронно. В Playwright есть два основных способа ожиданий:

  1. Встроенные ожидания локаторов
    Методы локатора (click(), fill(), inner_text() и другие) автоматически дожидаются, пока элемент:

    • появится в DOM (Document Object Model),
    • станет видимым,
    • станет доступным для взаимодействия.

    Пример:

    heading = page.locator("h1") # ждёт, пока h1 станет доступен 
    print(heading.inner_text()) 
    
    heading.click(timeout=3000) # тоже ждёт готовности элемента

    Здесь нет необходимости явно вызывать wait_for_selector() — Playwright всё делает сам.

  2. Ожидание навигации
    Иногда требуется дождаться перехода на другую страницу после действия пользователя (например, после клика по ссылке):

    page.locator("a#next").click() # Клик по ссылке 
    page.wait_for_navigation() # Явное ожидание завершения перехода

Отличие от старого подхода

При использовании query_selector методы не ждут элемент, поэтому часто нужно делать:

page.wait_for_selector("h1", timeout=5000)  # ждёт до 5 секунд
element = page.query_selector("h1") 
print(element.inner_text())

Локаторы делают это автоматически, упрощая код и снижая вероятность ошибок из-за асинхронной загрузки контента.

Параметр wait_until — когда страница считается загруженной

Когда мы вызываем page.goto(), возникает вопрос: когда считать, что страница загружена? Когда HTML получен? Когда все картинки скачаны? Когда JavaScript выполнился?

wait_until определяет, когда Playwright считает, что переход на страницу завершён.

page.goto("https://example.com", wait_until="load")

Варианты:

  • load — происходит, когда всё: DOM + ресурсы (картинки, CSS, JS) загружено. Это классическое поведение по умолчанию.
  • commit — срабатывает сразу после того, как браузер получил ответ на основной запрос и начал загрузку документа. Не ждёт CSS, JS или изображений. Полезно, если нужно моментально начать работу с DOM, не дожидаясь полной загрузки.
  • domcontentloaded — срабатывает, когда HTML-документ полностью загружен и распарсен, но ресурсы (картинки, стили, шрифты) могут ещё загружаться. Полезно для SPA, где JS сам подгружает данные, или скриптов, которые работают с DOM сразу после загрузки.
  • networkidle (устаревший) — считается завершённым, когда нет сетевых соединений ≥ 500 мс. Раньше использовался для SPA, чтобы ждать подгрузки API-запросов. Сейчас Playwright не рекомендует использовать, так как это часто непредсказуемо — иногда сетевые пулы остаются открытыми. Рекомендуется ждать конкретный элемент на странице через селектор или ответ от сети через wait_for_response, чтобы точно определить готовность нужного контента.

Ретраи (Retries) — устойчивость к временным сбоям

Даже стабильные сайты иногда тормозят или недоступны из-за сетевых проблем. Вместо того чтобы падать с ошибкой, лучше повторить попытку.

Иногда сайты временно недоступны, и лучше повторить действие, чем падать:

from playwright.sync_api import TimeoutError

for attempt in range(3):
    try:
        page.goto("https://news.ycombinator.com/")
        break  # если успешно — выход из цикла
    except TimeoutError:
        print(f"Попытка {attempt + 1} не удалась. Повтор...")
else:
    print("Не удалось загрузить страницу после 3 попыток")
  • Повторные попытки позволяют обрабатывать временные сбои сети или замедленную загрузку страниц.
  • Комбинируется с ожиданиями для устойчивой работы.

Ошибки и крайние случаи

При работе с Playwright могут возникать различные ошибки:

  • Элемент не найден: Если указанный селектор не соответствует ни одному элементу на странице, будет выброшено исключение.
  • Таймаут ожидания: Если элемент не появился в течение заданного времени, будет выброшено исключение о таймауте.
  • Проблемы с навигацией: Если переход на новую страницу не завершился, это может привести к ошибкам.

Даже на стабильных сайтах могут быть неожиданные изменения (редизайн, обновление структуры), поэтому блоки try/except обязательны:

try:
    page.locator("button.submit").click()
except Exception as e:
    print("Не удалось нажать кнопку:", e)

Рекомендации:

  • Логируйте ошибки с помощью print или logging.
  • Используйте скриншоты при ошибках для понимания, что пошло не так:
    page.screenshot(path="error.png")
    

Работа с несколькими страницами и вкладками

Иногда нужно работать с несколькими сайтами одновременно или открыть ссылку в новой вкладке. Playwright позволяет открывать несколько вкладок и работать с ними независимо:

page1 = browser.new_page()
page1.goto("https://example.com")

page2 = browser.new_page()
page2.goto("https://example.org")
  • Каждая вкладка — отдельный контекст для действий.
  • Можно параллельно взаимодействовать с несколькими страницами.

Асинхронное API

Для высокой производительности (например, одновременная обработка десятков страниц) Playwright полностью поддерживает async/await. Пример:

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        await page.goto("https://example.com")

        # Клик по ссылке
        await page.locator("text=Learn more").click()

        # Получаем весь текст страницы
        page_text = await page.inner_text("body")
        print(page_text)

        await browser.close()

asyncio.run(main())

Артефакты: скриншоты и видео

Для диагностики проблем (особенно в CI/CD, где нет возможности визуально наблюдать) полезно сохранять визуальные артефакты:

1. Скриншоты:

page.screenshot(path="screenshot.png")

2. Запись видео сессии:

context = browser.new_context(record_video_dir="videos/")
page = context.new_page()
page.goto("https://example.com/")
# По окончании:
context.close()  # видео сохраняется в папку videos/

Итоги

Playwright позволяет полностью управлять браузером, поддерживает синхронное и асинхронное взаимодействие с элементами. С помощью селекторов и ожиданий можно надежно находить и взаимодействовать с элементами страницы, даже если они загружаются динамически. Встроенные механизмы ожиданий, ретраи и возможность записи артефактов делают автоматизацию браузера стабильной и удобной для отладки.

Дальше по курсу

Разберём как изолировать процессы, а также работать с контекстом выполнения (куки, локальное хранилище).