Автоматизация браузера для AI-агентов на Python

Для оснащения Python AI-агента возможностью работать с веб-браузером (навигация, авторизация, заполнение форм, анализ контента) существуют четыре принципиально различных класса решений, различающихся по степени интеллектуальности, стоимости и сложности интеграции. Выбор конкретного подхода определяется тремя факторами: характером решаемых задач, допустимым бюджетом и глубиной контроля над процессом.

Наиболее перспективным направлением в 2025–2026 годах стал Model Context Protocol (MCP) — открытый стандарт от Anthropic, представленный в ноябре 2024 года, который стремительно превращается в де-факто шину подключения внешних инструментов к AI-агентам.

Статистика экосистемы MCP:

  • 97+ млн ежемесячных загрузок MCP SDK
  • 10 000+ опубликованных MCP-серверов (оценка на начало 2026)
  • Официальные серверы от Notion, Linear, Figma, Stripe, Microsoft

Практическая рекомендация для большинства сценариев:

  • Playwright как исполнительный слой (надёжный, кроссбраузерный, с отличной Python-поддержкой) в связке с официальным @playwright/mcp сервером.
  • Для задач, требующих «нейронативного» поведения («описал цель — получил результат»), оптимален browser-use — открытая библиотека с финансированием $17 млн [^techcrunch].

Часть I. Таксономия подходов

1.1. Три уровня абстракции

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

┌─────────────────────────────────┐
│ Уровень 3: Интенциональный       │
│ "Что сделать?"                   │
│ • browser-use                    │
│ • Skyvern                        │
├─────────────────────────────────┤
│ Уровень 2: Семантический         │
│ "Что на странице?"               │
│ • Дерево доступности (a11y tree)│
│ • @playwright/mcp                │
├─────────────────────────────────┤
│ Уровень 1: Исполнительный        │
│ "Как кликнуть?"                  │
│ • Playwright / Puppeteer / Selenium │
└─────────────────────────────────┘

Уровень 1 — Исполнительный слой (низкоуровневое управление)

Библиотеки, напрямую управляющие браузером через Chrome DevTools Protocol (CDP) или WebDriver. Агент самостоятельно определяет последовательность действий.

Инструмент Ключевое преимущество Ограничение для AI
Playwright Авто-ожидания, кроссбраузерность, трассировка Нет встроенной логики принятия решений
Selenium Зрелость, сообщество, корпоративные интеграции Многословность, хрупкость к динамическому DOM
Puppeteer Глубокая интеграция с Chrome Ориентирован на Node.js, Python-порты фрагментированы

Уровень 2 — Семантический слой (слой доступности)

Промежуточная абстракция, заменяющая сырой DOM или скриншоты деревом доступности (accessibility tree). Это структурированное представление содержит:

  • Семантические роли элементов (button, textbox, heading)
  • Состояния (focused, disabled, expanded)
  • ARIA-атрибуты и текстовые метки

Почему это важно для LLM: модель получает 1–50 КБ структурированного текста вместо мегабайт пикселей или неструктурированного HTML. Это радикально снижает стоимость инференса и повышает стабильность парсинга.

Эталонная реализация: сервер @playwright/mcp от Microsoft, который транслирует возможности Playwright в MCP-инструменты, используя accessibility tree как основной формат представления страницы.

Уровень 3 — Интенциональный слой (целеориентированный)

Высокоуровневые системы, принимающие описание задачи на естественном языке и самостоятельно декомпозирующие её в последовательность действий.

Решение Тип Ключевая фича
browser-use Open-source + Cloud Цикл «наблюдение → размышление → действие» из коробки
Skyvern Hybrid (OSS core + managed) Визуальное позиционирование на уровне пикселей
AIME Browser-Use Fork Оптимизированная версия для специфичных сценариев

MCP как транспорт: Протокол Model Context Protocol служит стандартизированным механизмом вызова инструментов между любым из этих уровней и агентом, независимо от языка реализации сервера (Python, Go, Node.js).

1.2. Архитектурная схема подключения

┌─────────────────────────────────────────────────────────────┐
│                     Ваш Python AI-агент                     │
│             (LangChain / LangGraph / Custom Loop)           │
└───────────────────────┬─────────────────────────────────────┘
                        │   MCP Client SDK (mcp-python)
                        ▼
┌─────────────────────────────────────────────────────────────┐
│                 MCP-сервер (browser-related)                │
│                                                               │
│  ▶ Вариант A: @playwright/mcp  ──►  Playwright  ──►  CDP    │
│  ▶ Вариант B: community/mcp-browser-use                      │
│  ▶ Вариант C: Кастомный сервер (Skyvern / browser-use API)  │
└───────────────────────┬─────────────────────────────────────┘
                        │   CDP / HTTP / Unix socket
                        ▼
┌─────────────────────────────────────────────────────────────┐
│              Браузер (Chromium / Firefox / WebKit)           │
│        Запуск: локально, в Docker или облачный хостинг       │
└─────────────────────────────────────────────────────────────┘

Accessibility Tree (a11y tree) - семантическое представление страницы

Accessibility tree (дерево доступности, сокращённо a11y tree) — это внутреннее представление веб-страницы, которое браузер создаёт на основе DOM для передачи семантической информации вспомогательным технологиям (скринридерам, переключателям, голосовому управлению).

a11y = abbreviation для "accessibility" (буква "a" + 11 букв + буква "y")

Архитектурное место в браузере

┌─────────────────┐
│   HTML-код      │
└────────┬────────┘
         ▼
┌─────────────────┐
│   DOM Tree      │ ← Все элементы, атрибуты, текст
│   (полный)      │
└────────┬────────┘
         ▼
┌─────────────────┐
│ Accessibility   │ ← Только семантически значимые
│ Tree (a11y)     │   элементы с ролями, состояниями, именами
└────────┬────────┘
         ▼
┌─────────────────┐
│ Platform APIs   │ ← Windows UIA, macOS AXAPI, Linux AT-SPI
│ (OS-specific)   │
└────────┬────────┘
         ▼
┌─────────────────┐
│ Assistive Tech  │ ← NVDA, VoiceOver, TalkBack, JAWS
└─────────────────┘

Браузеры преобразуют разметку во внутреннее представление — DOM-дерево, а затем создают на его основе accessibility tree, который используется платформенными Accessibility API для предоставления представления, понятного вспомогательным технологиям.

Структура узла accessibility tree

Каждый объект в дереве доступности содержит четыре ключевых свойства:

Свойство Описание Пример
name Как можно обратиться к этому элементу? "Отправить", "Поиск по сайту"
role Какова роль элемента? button, textbox, navigation, heading
state Какое текущее состояние? checked, disabled, expanded, focused
description Дополнительное описание (опционально) "Таблица с результатами поиска за 2025 год"

Пример представления элемента

{
  "role": "button",
  "name": "Войти",
  "state": {
    "enabled": true,
    "focused": false
  },
  "description": "Авторизация в аккаунте"
}

Дополнительно дерево может содержать информацию о возможных действиях: ссылку можно перейти, текстовое поле — заполнить, чекбокс — переключить [[5]].

⚙️ Как работает: процесс построения

Шаг 1: Парсинг HTML → DOM

Браузер парсит HTML и строит полное дерево всех узлов, включая декоративные <div>, <span>, стили и скрипты.

Шаг 2: Фильтрация и обогащение → a11y tree

Браузер применяет алгоритмы для создания accessibility tree:

// Псевдокод процесса
function buildAccessibilityTree(domNode) {
  // 1. Пропускаем невидимые элементы
  if (!isVisible(domNode) || domNode.ariaHidden) return null;

  // 2. Определяем роль (явную или неявную)
  const role = domNode.getAttribute('role') 
            || inferImplicitRole(domNode.tagName);

  // 3. Вычисляем доступное имя (Accessible Name Computation)
  const name = computeAccessibleName(domNode); // из aria-label, textContent, alt и т.д.

  // 4. Собираем состояния
  const states = {
    checked: domNode.checked,
    disabled: domNode.disabled,
    expanded: domNode.getAttribute('aria-expanded') === 'true',
    // ...
  };

  // 5. Рекурсивно обрабатываем детей
  const children = domNode.children
    .map(child => buildAccessibilityTree(child))
    .filter(node => node !== null);

  return { role, name, states, children };
}

Шаг 3: Экспорт через платформенные API

Дерево передаётся в ОС через специализированные API:

  • Windows: UI Automation (UIA)
  • macOS/iOS: NSAccessibility / AXAPI
  • Linux: AT-SPI
  • Web: ARIA + Chrome DevTools Protocol

Сравнение: DOM vs a11y tree для LLM

Критерий Сырой DOM / HTML Accessibility Tree
Объём данных 500 КБ – 5 МБ 1–50 КБ
Семантика Требует парсинга и интерпретации Явные role, name, state
Устойчивость к изменениям Хрупкие CSS/XPath селекторы Стабильные семантические идентификаторы
Токены для LLM Дорого, много шума Дёшево, только сигнал
Понимание intent Модель должна «догадываться» Явные действия: click, type, select

Ключевой инсайт: Accessibility tree — это тот же структурированный интерфейс, который используют скринридеры. Вместо хрупких CSS-селекторов вы таргетируете элементы по их семантическим ролям и именам.

Пример: как LLM видит страницу

Через DOM (упрощённо):

<div class="auth-form"><div class="field-wrapper"><input id="i1" class="form-control" ...></div>...</div>

→ LLM должна парсить классы, угадывать назначение поля.

Через a11y tree:

[1] textbox "Email" | required=True | focused=False
[2] textbox "Password" | required=True | type="password"
[3] button "Войти" | enabled=True

→ LLM сразу понимает: «это форма входа, нужно заполнить поля 1 и 2, затем кликнуть 3».


Как использовать a11y tree: практические примеры

1. Через Playwright (Python)

from playwright.async_api import async_playwright

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

    # Получение snapshot accessibility tree
    snapshot = await page.accessibility.snapshot()

    # Рекурсивный обход для поиска элемента
    def find_element(node, role=None, name=None):
        if (role is None or node.get("role") == role) and \
           (name is None or node.get("name") == name):
            return node
        for child in node.get("children", []):
            result = find_element(child, role, name)
            if result:
                return result
        return None

    email_field = find_element(role="textbox", name="Email")
    print(f"Нашел поле: {email_field}")

2. Через Chrome DevTools Protocol (низкоуровневый доступ)

# Используя библиотеку `cdp` или `pycdp`
from cdp import Page, Accessibility

# Включение accessibility domain
await cdp.send(Accessibility.enable())

# Получение полного дерева
ax_tree = await cdp.send(Accessibility.get_full_ax_tree())

# Пример узла:
# {
#   "nodeId": "AX123",
#   "role": {"value": "button"},
#   "name": {"value": "Submit"},
#   "state": {"enabled": True},
#   "bounds": {"x": 100, "y": 200, "width": 80, "height": 30}
# }

3. В связке с MCP-сервером (@playwright/mcp)

// Конфигурация MCP-клиента
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp"]
    }
  }
}
# Агент вызывает инструмент через MCP
result = await mcp_client.call_tool(
    "playwright_get_accessibility_tree",
    {"simplify": True}  # Упрощённый формат для LLM
)
# Возвращает компактное представление:
# [1] link "Главная" | href="/"
# [2] button "Меню" | expanded=False
# [3] textbox "Поиск" | placeholder="Введите запрос..."

Playwright MCP позволяет LLM взаимодействовать с веб-страницами, используя данные accessibility tree, устраняя необходимость в дорогих vision-моделях.


🎯 Преимущества для браузерной автоматизации

✅ Устойчивость к изменениям вёрстки

- Старый HTML: <button class="btn-primary">Login</button>
+ Новый HTML: <div role="button" aria-label="Войти в систему"></div>

# CSS-селектор ".btn-primary" сломается
# Но a11y-запрос role="button" + name~="войти" продолжит работать

✅ Компактность и экономия токенов

  • DOM страницы: ~2 МБ → ~500 000 токенов
  • a11y tree той же страницы: ~20 КБ → ~5 000 токенов
  • Экономия: до 99% стоимости инференса

✅ Семантическая точность

Accessibility semantics становятся основой надёжной AI-автоматизации: MCP-подход неявно поощряет приложения с правильной семантикой.

✅ Совместимость с существующими стандартами

  • Не нужно изобретать новый формат — браузер уже вычисляет это для скринридеров
  • Любое улучшение доступности сайта автоматически улучшает работу вашего агента

Ограничения и подводные камни a11y tree

1. Не все элементы попадают в a11y tree

  • Элементы с aria-hidden="true" или display: none исключаются
  • Декоративные <div>, <span> без семантики не включаются
  • Кастомные виджеты без ARIA-атрибутов могут быть «невидимы»

Решение: Добавляйте семантику через ARIA:

<!-- Плохо: декоративный div, невидимый для a11y -->
<div onclick="submitForm()">Отправить</div>

<!-- Хорошо: явная роль и имя -->
<div role="button" 
     tabindex="0" 
     aria-label="Отправить форму"
     onclick="submitForm()">
  Отправить
</div>

2. Платформенные различия

Accessibility — это вопрос, сильно зависящий от платформы. Разные браузеры и ОС могут представлять дерево немного по-разному.

Рекомендация: Тестируйте на целевой платформе (Chromium + Linux для серверных агентов).

3. Динамический контент требует обновления

a11y tree обновляется асинхронно. После AJAX-запросов или анимаций может потребоваться явная синхронизация:

# Playwright: дождаться обновления a11y tree
await page.wait_for_load_state("networkidle")
snapshot = await page.accessibility.snapshot()

Accessibility Tree для текстового контента: подходит или нет?

Задача a11y tree подходит? Рекомендация
Навигация по странице ✅ Отлично Используйте get_by_role() / get_by_label()
Заполнение форм, клики ✅ Отлично Используйте семантические локаторы Playwright
Извлечение статьи/блога ⚠️ Частично Лучше Readability / Firecrawl
Парсинг каталога товаров ⚠️ Частично Комбинация: семантические локаторы + DOM
Чтение документации ❌ Неоптимально Markdown-конвертеры

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

✅ Включается в дерево доступности

Тип контента Пример Как выглядит в a11y tree
Заголовки <h1>, <h2>, <h3> role="heading", name="Заголовок статьи", level=1
Ссылки <a href="...">Текст</a> role="link", name="Текст ссылки"
Кнопки <button>Отправить</button> role="button", name="Отправить"
Формы <input>, <label> role="textbox", name="Email"
Семантические регионы <nav>, <main>, <article> role="navigation", role="main"
Текст с ARIA-метками <div aria-label="Описание"> name="Описание"
Альтернативный текст изображений <img alt="..."> role="img", name="alt-текст"

❌ Часто исключается или упрощается

Тип контента Почему теряется Пример
Параграфы без семантики <p> вне регионов/без ARIA может быть отфильтрован Основной текст статьи
Декоративный текст Считается незначимым для скринридеров <span class="icon-text">
Текст в кастомных виджетах Компоненты без ARIA-атрибутов <div class="custom-card">
Таблицы данных Упрощаются, если нет role="table" или заголовков <table> без семантики
Списки без контекста Могут быть свёрнуты, если не в регионе <ul><li>...</li></ul>
Текст внутри canvas/SVG Не попадает в a11y tree без явных ARIA-описаний Инфографика, диаграммы

⚠️ Важно: Поведение accessibility tree зависит от браузера (Chromium/Firefox/WebKit) и платформы. То, что видно в Chromium, может отличаться в Firefox.

Практический пример: сравнение объёма данных - Страница: новостная статья (примерные цифры)

Источник Объём Что включено
Сырой HTML ~150 КБ Всё, включая стили, скрипты, мета-теги
DOM textContent ~25 КБ Весь видимый текст
Accessibility tree ~8 КБ Заголовки, ссылки, кнопки, семантические регионы
Readability (article) ~12 КБ Основной текст статьи + метаданные
Firecrawl Markdown ~10 КБ Чистый контент, оптимизированный для LLM

Вывод: a11y tree может потерять значительную часть текстового контента по сравнению с методами извлечения статей. Точная цифра зависит от структуры страницы.

Сценарий 1: Навигация + взаимодействие (формы, клики)

# ✅ Playwright с семантическими локаторами — идеальный выбор
# Примечание: accessibility.snapshot() deprecated, используйте get_by_role()
from playwright.async_api import async_playwright

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

    # Находим элементы по ARIA-роли и имени — устойчиво к изменениям вёрстки
    email_field = page.get_by_role("textbox", name="Email")
    await email_field.fill("user@example.com")

    submit_btn = page.get_by_role("button", name="Войти")
    await submit_btn.click()

Почему семантические локаторы: устойчивы к изменениям вёрстки, компактные селекторы, точная семантика.

⚠️ Важно: page.accessibility.snapshot() deprecated в Playwright. Для тестирования доступности используйте Axe или аналогичные инструменты. Для навигации агента используйте get_by_role(), get_by_label(), get_by_text().


Сценарий 2: Извлечение текста статьи / блога

from readability import Document
import requests

def extract_article_python(url: str) -> dict:
    headers = {"User-Agent": "Mozilla/5.0"}
    response = requests.get(url, headers=headers, timeout=30)
    response.raise_for_status()

    doc = Document(response.text)
    return {
        "title": doc.title(),
        "content": doc.summary(),          # HTML-контент статьи
        "text_content": doc.text_content(), # Только текст
        "short_title": doc.short_title()
    }

# Использование
# result = extract_article_python("https://example.com/article")
# print(result["text_content"])

Почему не a11y tree: потеряете параграфы, цитаты, детали контента, не связанные с интерактивными элементами.


Сценарий 3: Гибридный подход (навигация + чтение)

# =============================================================================
# HybridAgent: Playwright для навигации + python-readability для контента
# =============================================================================
# Установка зависимостей:
#   pip install playwright python-readability lxml html_text requests
#   playwright install chromium

from playwright.async_api import Page, async_playwright
from readability import Document
from typing import Optional

class HybridAgent:
    """
    Агент для навигации по сайту и извлечения основного контента.

    - Навигация: семантические локаторы Playwright (устойчивы к изменениям вёрстки)
    - Извлечение контента: python-readability (чистый Python, без JS-инъекций)
    """

    def __init__(self, page: Page):
        self.page = page

    async def navigate_by_goal(self, goal: str) -> bool:
        """
        Простая навигация по семантическим локаторам.
        В продакшене здесь может быть LLM-планировщик.
        """
        # Пример: если в goal есть "статья", ищем ссылку "Статьи" или "Блог"
        keywords = {
            "статья": ["Статьи", "Блог", "Articles", "Blog"],
            "документация": ["Документация", "Docs", "Documentation"],
            "товар": ["Каталог", "Товары", "Products", "Shop"],
        }

        for category, labels in keywords.items():
            if category in goal.lower():
                for label in labels:
                    link = self.page.get_by_role("link", name=label)
                    if await link.count() > 0:
                        await link.first.click()
                        await self.page.wait_for_load_state("networkidle")
                        return True
        return False

    async def extract_content_python(self) -> dict:
        """
        Извлекает основной контент страницы через python-readability.
        Работает с текущим состоянием DOM (после навигации/динамической подгрузки).
        """
        # Получаем HTML текущего состояния страницы (после JS-рендеринга)
        html = await self.page.content()

        # Парсим через python-readability (чистый Python, без JS)
        doc = Document(html)

        return {
            "title": doc.title(),
            "summary_html": doc.summary(),        # Очищенный HTML статьи
            "text_content": doc.text_content(),   # Только текст, без разметки
            "short_title": doc.short_title(),
            "author": getattr(doc, "author", None),  # если доступно
        }

    async def navigate_and_extract(self, goal: str) -> dict:
        """
        Полный пайплайн: навигация → извлечение контента.
        """
        # Шаг 1: Навигация по семантическим локаторам
        await self.navigate_by_goal(goal)

        # Шаг 2: Небольшая задержка для полной подгрузки динамического контента
        await self.page.wait_for_timeout(500)  # 500ms, при необходимости увеличить

        # Шаг 3: Извлечение контента через python-readability
        content = await self.extract_content_python()

        # Fallback: если Readability не смог выделить контент, берём весь текст
        if not content["text_content"] or len(content["text_content"].strip()) < 100:
            content["text_content"] = await self.page.evaluate(
                "() => document.body.innerText"
            )
            content["fallback_used"] = True
        else:
            content["fallback_used"] = False

        return content

# =============================================================================
# Пример использования
# =============================================================================
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")

        agent = HybridAgent(page)
        result = await agent.navigate_and_extract("найти статью про ИИ")

        print(f"Заголовок: {result['title']}")
        print(f"Текст (первые 500 символов): {result['text_content'][:500]}...")
        print(f"Использован fallback: {result['fallback_used']}")

        await browser.close()

# Запуск:
# import asyncio
# asyncio.run(main())

Почему гибрид: лучшее из обоих миров — устойчивая навигация + полный контент.

📋 Сравнительная таблица методов извлечения

Метод Для навигации Для контента Объём данных Устойчивость Стоимость
Семантические локаторы (Playwright) ✅ Отлично ⚠️ Частично Низкий ✅ Высокая Низкая
DOM textContent ⚠️ Средне ✅ Хорошо Средний (10–100 КБ) ⚠️ Средняя Низкая
Readability.js ❌ Нет ✅ Отлично Средний (5–50 КБ) ✅ Высокая Низкая
Firecrawl API ❌ Нет ✅ Отлично Низкий (5–30 КБ) ✅ Высокая $$
Скриншот + Vision ✅ Хорошо ✅ Хорошо Высокий (изображение) ✅ Высокая \($\)$
Сырой HTML ⚠️ Средне ✅ Хорошо Высокий (100 КБ–5 МБ) ❌ Низкая $$

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

  • Основная задача — взаимодействие: клики, формы, навигация
  • Нужна устойчивость к изменениям вёрстки
  • Ограничен бюджет токенов для LLM
  • Работаете с известными, семантически правильными сайтами

Используйте Readability / Firecrawl, когда:

  • Основная задача — извлечение текста: статьи, блоги, документация
  • Нужен чистый контент для RAG / индексации
  • Сайт имеет сложную вёрстку с рекламой, сайдбарами
  • Не требуется интерактивность

Используйте гибридный подход, когда:

  • Нужно и навигация, и чтение: «найди статью про X и извлеми ключевые тезисы»
  • Продакшен-система с разными типами задач
  • Готовы инвестировать в более сложную архитектуру

Практический чеклист

┌─────────────────────────────────────────────────────────┐
│  Что вы хотите сделать с страницей?                     │
└─────────────────────────────────────────────────────────┘
                          │
        ┌─────────────────┼─────────────────┐
        ▼                 ▼                 ▼
   Только чтение    Только клики/формы   И то, и другое
        │                 │                 │
        ▼                 ▼                 ▼
  Firecrawl /       get_by_role() +    Гибридный подход:
  Readability       Playwright MCP     • локаторы для навигации
                                        • Readability для контента

Accessibility tree не теряет «весь» контент, но он теряет значительную часть текстового содержимого, которое не связано с интерактивными элементами или семантической структурой.

Для AI-агентов оптимальная стратегия:

  1. Навигация и взаимодействие → семантические локаторы Playwright (get_by_role(), get_by_label())
  2. Извлечение контента → Readability.js, Firecrawl, Jina Reader
  3. Продакшен-системы → Гибридный подход с маршрутизацией задач

Инструменты для инспекции a11y tree

Chrome DevTools

  1. Откройте DevTools → вкладка Elements
  2. Панель Accessibility (включите в ⋮ → More tools)
  3. Или в консоли: chrome://accessibility

Playwright Inspector

npx playwright codegen --save-trace example.zip example.com
# Trace viewer покажет a11y snapshot для каждого шага

Онлайн-валидаторы

Дополнительные ресурсы про Accessibility tree

Ресурс Описание
MDN: Accessibility tree Официальная документация, свойства узлов
web.dev: The Accessibility Tree Глубокое объяснение процесса построения
Playwright Accessibility API Программный доступ к a11y tree
ARIA Authoring Practices Как правильно добавлять семантику
Fazm: Accessibility Tree vs DOM Сравнение для AI-агентов

Часть II. Инструменты и технологии - обзор

2.1. Playwright (Microsoft) — фундамент современной автоматизации

Сайт: playwright.dev
Лицензия: Apache 2.0
Python-пакет: pip install playwright
Статус: Стабильный продакшен, активная разработка под эгидой Microsoft

Ключевые технические преимущества

Кроссбраузерность из единого API
Единый интерфейс для Chromium, Firefox и WebKit. Для AI-агентов это критично: ряд сайтов рендерит контент по-разному в зависимости от движка, и возможность проверить отображение во всех трёх повышает надёжность парсинга.

Автоматическое ожидание готовности элементов
Playwright автоматически дожидается:

  • Появления элемента в DOM
  • Его видимости и интерактивности
  • Завершения сетевых запросов (опционально)
# Пример: не нужно писать явные ожидания
await page.click("#submit-btn")  # Playwright сам дождётся, когда кнопка станет кликабельной

Это радикально снижает количество StaleElementReferenceException и нестабильных сценариев по сравнению с Selenium.

Трассировка и наблюдаемость
Встроенный механизм записи:

  • Видео и скриншоты каждого действия
  • Структурированные логи с таймингами
  • Снэпшоты DOM и accessibility tree

Незаменимо для отладки падающих агентских сценариев и аудита действий в процессах, связанных с безопасностью.

Гибкие режимы выполнения Режим Использование
headless Продакшен, фоновое выполнение
headed Отладка, визуальная верификация
connect_over_cdp Подключение к уже запущенному Chrome (через --remote-debugging-port)

Codegen
Утилита playwright codegen записывает действия в браузере и генерирует код на Python/JS. Полезна для быстрого создания тестовых скриптов и прототипов.

Ограничения

⚠️ Нет встроенной логики принятия решений
Playwright — это «двигатель без руля». Агент несёт полную ответственность за определение следующего действия на каждом шаге. Это осознанный компромисс: максимальный контроль ценой необходимости самостоятельного построения цикла работы агента.

⚠️ Python-привязка
Исторически немного отстаёт от JS/TS версии по скорости добавления новых функций, хотя разрыв значительно сокращён усилиями Microsoft.


2.2. @playwright/mcp — официальный MCP-сервер от Microsoft

Документация: playwright.dev/docs/getting-started-mcp
Публикация: Официальный пакет Microsoft, активная поддержка

Почему дерево доступности — правильный выбор для AI-агентов

Подход Что получает модель Проблемы
Сырой HTML/DOM Мегабайты неструктурированного текста Огромный контекст, хрупкие селекторы
Скриншот + Vision Пиксели, требует multimodal-модели Дорого, медленно, сложно парсить текст
Accessibility tree 1–50 КБ структурированных семантических данных Меньше шума, больше сигнала

Пример представления элемента в accessibility tree:

[button] "Отправить" | role=button | focused=False | disabled=False | aria-label=None

Доступные MCP-инструменты

После подключения сервера агент получает стандартный набор инструментов:

{
  "tools": [
    {"name": "playwright_navigate", "description": "Перейти по URL"},
    {"name": "playwright_click", "description": "Клик по элементу (селектор или a11y-описание)"},
    {"name": "playwright_fill", "description": "Ввод текста в поле"},
    {"name": "playwright_select_option", "description": "Выбор значения в <select>"},
    {"name": "playwright_evaluate", "description": "Выполнение произвольного JS в контексте страницы"},
    {"name": "playwright_screenshot", "description": "Получение скриншота (base64)"},
    {"name": "playwright_get_accessibility_tree", "description": "Получение снапшота a11y tree"}
  ]
}

Варианты развёртывания

Способ Команда / Конфиг Когда использовать
Локальный npx @playwright/mcp Прототипирование, локальная разработка
Docker Официальный образ mcr.microsoft.com/playwright/mcp CI/CD, изолированная среда
Удалённый (TCP) Подключение по ws://host:port Микросервисная архитектура, масштабирование

Пример конфигурации для MCP-клиента:

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp"],
      "env": {
        "PLAYWRIGHT_BROWSERS_PATH": "/opt/browsers"
      }
    }
  }
}

2.3. browser-use — AI-нативная обёртка над Playwright

GitHub: github.com/browser-use/browser-use
Лицензия: MIT
Финансирование: $17 млн, раунд Seed, март 2025 [^techcrunch]

Архитектура

Натуральный язык - запрос пользователя
        │
        ▼
┌─────────────────┐
│  LLM (GPT-4o /  │
│  Claude / Ollama)│
└────────┬────────┘
         │ План действий (JSON Schema)
         ▼
┌─────────────────┐
│  Контроллер     │
│  • Машина состояний │
│  • История шагов   │
│  • Обработка ошибок│
└────────┬────────┘
         │ Команды Playwright
         ▼
┌─────────────────┐
│  Playwright +   │
│  Chromium       │
└─────────────────┘

Ключевые возможности

Цикл «наблюдение → размышление → действие» из коробки
Библиотека реализует полноценный агентский паттерн с управлением историей состояний для восстановления после ошибок.

Структурированный вывод
Использует Pydantic-схемы для гарантии parseable ответов модели, что критично для надёжного выполнения действий.

Встроенные решения для продакшена (премиум-версия)

  • Решение reCAPTCHA v2/v3 и hCaptcha
  • Ротация резидентных и датацентровых прокси
  • Скрытие признаков автоматизации (stealth mode)
  • Сохраняемость длительных задач (checkpoints)

Режимы развёртывания

Режим Требования Когда использовать
Полностью локальный Playwright + Ollama, GPU 16+ ГБ VRAM Приватные данные, нулевые облачные расходы
Гибридный Локальная библиотека + коммерческий LLM API (OpenAI/Anthropic) Баланс контроля и качества
Облачный Только API-ключ, нулевая локальная настройка Быстрый старт, команды без MLOps

Бенчмарки

⚠️ Методологическое примечание: Результаты приведены согласно Steel.dev Leaderboard на февраль 2026. Организации используют разные подмножества датасета WebVoyager, разные evaluator'ы (GPT-4V judge vs собственные) и некоторые результаты являются самоотчётными. Прямое сравнение чисел требует учёта этих различий.

Агент WebVoyager Score Тип Примечания
Browser Use (официальный) 89.1% Open-source Основная эталонная реализация, полный датасет
AIME Browser-Use 92.34% Fork Отдельная оптимизированная версия

Известные ограничения

⚠️ Меньше контроля над промежуточными шагами
Если требуется тонкая настройка логики на каждом этапе взаимодействия, browser-use добавляет контрпродуктивный слой абстракции. Библиотека ориентирована на сквозную автоматизацию, а не на программное взаимодействие с детализацией.

⚠️ Вендор-лок и расходы
Облачный вариант несёт риски vendor lock-in и регулярные расходы, пропорциональные объёму задач.

Пример использования (гибридный режим)

from browser_use import Browser, BrowserConfig, BrowserAgent
from langchain_openai import ChatOpenAI

# Инициализация
llm = ChatOpenAI(model="gpt-4o", temperature=0)
browser = Browser(config=BrowserConfig(headless=True))

# Создание и запуск агента
agent = BrowserAgent(
    task="Найди самый дешёвый RTX 5090 на маркетплейсе и сохрани ссылку в файл",
    llm=llm,
    browser=browser
)

result = await agent.run()
print(f"Результат: {result}")

2.4. Skyvern — AI-нативная RPA для сложных бизнес-процессов

GitHub: github.com/skyvern-ai/skyvern
Сайт: skyvern.com
Лицензия: Проприетарное ядро + компоненты с открытым кодом (Apache 2.0)

Архитектурные особенности

🔹 Визуальное позиционирование
Мультимодальная модель анализирует страницу на уровне пикселей, находит элементы по пространственным координатам. Это позволяет адаптироваться к изменениям вёрстки без зависимости от CSS-селекторов.

🔹 Движок бизнес-процессов
Декларативное определение длинных рабочих процессов, включая:

  • Ветвление по условиям
  • Повторные попытки с экспоненциальной задержкой
  • Ожидание специфичных состояний страницы

🔹 API-first подход
Все операции доступны через REST endpoints, Python SDK предоставляет удобную обёртку.

🔹 Инфраструктурная интеграция

  • Встроенная ротация прокси (датацентровые + ISP)
  • Обработка сложного контента: infinite scroll, lazy-loaded изображения, модальные диалоги, cross-origin iframe

Бенчмарки

Примечание: Цифры приведены для контекста. Для Skyvern в официальном README указывается результат 64.4% на датасете WebBench [^skyvern-readme]. Результат 85.85% на WebVoyager заявлен для версии 2.0, но методология может отличаться от эталонной.

Датасет Результат Комментарий
WebVoyager (заявлено) 85.85% Полный набор, методология может варьироваться
WebBench (официально) 64.4% Другой датасет, другая методология оценки

Развёртывание

Вариант Требования Когда выбирать
Self-hosted GPU-сервер (NVIDIA RTX 3090+ / A100), Docker, Kubernetes Полный контроль, приватные данные, масштабирование
Managed Cloud Только API-ключ, оплата по использованию Команды без MLOps, быстрый старт, предсказуемые расходы

⚠️ Практическое ограничение: Самостоятельное развёртывание связано со значительной операционной сложностью. Команды без выделенных MLOps-ресурсов столкнутся с нетривиальной нагрузкой на настройку и поддержку.


2.5. Selenium — legacy-чемпион с большим комьюнити

Сайт: selenium.dev
История: Основан в 2004 году [^selenium-history]; в 2025 году проект отметил 20 лет развития
Текущая версия: Selenium 4.x (соответствует W3C WebDriver)

Где Selenium по-прежнему выигрывает

Массивное сообщество и архив знаний
Практически любую проблему можно решить с помощью тредов на StackOverflow десятилетней давности. Инженеры, впервые работающие с Selenium-проектами, найдут ответы быстрее, чем для любых альтернатив.

Корпоративные интеграции
SAP, Oracle EBS, Salesforce и старые ERP-системы часто имеют протестированные Selenium UI automation workflows, документированные SOP и сложившиеся центры компетенций.

Grid-архитектура
Selenium Grid обеспечивает распределённое выполнение тестов на нескольких машинах/датацентрах — зрелый, проверенный в бою паттерн горизонтального масштабирования, недоступный нативно в Playwright.

Почему Selenium субоптимален для AI-агентских сценариев

Нулевой встроенный интеллект
Selenium не понимает семантически, что такое «форма входа» — нужно программировать явные ожидания, обработчики исключений и логику восстановления. Для AI-агента это означает изобретение колеса на каждом workflow.

Эпидемия StaleElementReferenceException
Динамический DOM (SPA, React/Vue, асинхронные обновления) приводит к тому, что элементы становятся недействительными в середине скрипта. Автоматическое ожидание в Playwright радикально снижает эту частоту.

Многословность

# Selenium
element = driver.find_element(By.CSS_SELECTOR, ".btn-primary")
WebDriverWait(driver, 10).until(element_to_be_clickable(element))
element.click()

# Playwright (эквивалент)
await page.click(".btn-primary")  # Авто-ожидание включено по умолчанию

Рекомендация по использованию

Сценарий Выбор
Расширение существующей Selenium-кодовой базы AI-возможностями ✅ Selenium + AI-обёртка
Строго детерминированные, хорошо изученные workflows ✅ Selenium
Команда уже владеет Selenium, временные ограничения ✅ Остаться на Selenium
Новый проект, AI-агент с неопределёнными сценариями Выбирайте Playwright

2.6. Puppeteer — решение Google, ориентированное на Node.js

Сайт: pptr.dev

Библиотека Node.js от Google для управления headless Chrome. Ситуация с Python фрагментирована: несколько сторонних портов (pyppeteer, pyppeteer2) существуют, но ни один не имеет уровня проработки и активной поддержки, равного официальному Puppeteer.

Стратегическая оценка для Python-разработчиков

Рекомендация: Если нет убедительной конкретной причины, решаемой только через Puppeteer (что встречается редко), предпочитайте Playwright.

Причины:

  • Эквивалентные возможности управления Chrome
    • Кроссбраузерность (Firefox, WebKit)
    • Автоматическое ожидание элементов
    • Более удобные async-конструкции в официальной Python-привязке

Легитимный use-case

Проекты, ориентированные исключительно на Chrome/ium и уже инвестировавшие в экосистему Puppeteer, могут использовать режим "channel": "chrome" в Playwright, который внутренне запускает Chrome через CDP-подключение, эквивалентное Puppeteer, сохраняя знакомые парадигмы.


2.7. Новые игроки и специализированные сервисы

Steel.dev — браузерный API для AI-агентов

Сайт: steel.dev
GitHub: steel-dev/steel-browser

Открытый браузерный API, созданный специально для AI-агентов. Предоставляет REST API + Python SDK + Node SDK поверх управляемого парка headless-браузеров.

Примечательные особенности:

  • Встроенное решение капчи
  • Ротация прокси и управление отпечатками (fingerprinting)
  • Воспроизведение сессий для отладки
  • Интеграция с leaderboard'ом WebVoyager

Релевантен как: Infrastructure-as-a-service слой под browser-use или пользовательскими агентами.

Firecrawl — краулер, оптимизированный для LLM

Сайт: firecrawl.dev

Веб-краулер, оптимизированный для преобразования сайтов в форматы, готовые для LLM: чистый Markdown, структурированный JSON, скриншоты.

Характеристика Значение
Основное назначение Экстракция контента без интерактивности
Форматы вывода Markdown, JSON, HTML, скриншоты
Тарифы Free: 500 кредитов/мес; Paid: от ~$16/мес [^firecrawl-pricing]
Когда использовать Парсинг статей, документаций, каталогов; RSS-ingestion; маркетинговые исследования

⚠️ Не является полнофункциональным инструментом браузерной автоматизации. Не подходит для задач, требующих авторизации, заполнения форм или взаимодействия с динамическим контентом.

Jina AI Reader — простой экстрактор контента

Сайт: jina.ai/reader

Одноэндпоинт-сервис: POST {url} → получить Markdown/JSON представление страницы.

Преимущества:

  • Нулевая настройка, один HTTP-запрос
  • Оптимален для быстрого прототипирования и PoC
  • Freemium-модель с щедрым бесплатным тарифом

Ограничения: Только чтение, без интерактивности.


Часть III. Количественная картина — бенчмарк WebVoyager

⚠️ Критически важное методологическое примечание
Организации используют разные подмножества датасета WebVoyager (полный / частичный), разные evaluator'ы (GPT-4V judge vs собственные скрипты), и некоторые результаты являются самоотчётными без независимой верификации. Прямое сравнение чисел требует учёта этих различий. Таблица ниже представляет усреднённые значения из официально заявленных submission на момент февраля 2026 согласно Steel.dev Browser Agent Leaderboard.

Место Агент WebVoyager Score Тип Примечания
1 Surfer 2 (H Company) 97.1% Коммерческий Передовой результат, февраль 2026
2 Magnitude 93.9% Исследовательский Методология варьируется, публичная доступность ограничена
3 AIME Browser-Use 92.34% Гибридный Отдельный форк browser-use
4 Browserable 90.4% Платформа Методология варьируется
5 Browser Use (официальный) 89.1% Open-source Основная эталонная реализация
6 OpenAI Operator 87% Потребительский продукт Интегрирован в ChatGPT Pro
7 Skyvern 2.0 85.85% Open-source гибрид Самоотчёт, методология может отличаться
8 Google Project Mariner 83.5% Потребительский продукт На базе Gemini, интегрирован в Chrome

Интерпретационные заметки

🔹 Более высокий балл ≠ линейно лучшая продакшен-надёжность
WebVoyager тестирует фиксированный набор из ~15 сайтов. В продакшене будут:

  • Неизвестные ранее сайты с уникальной вёрсткой
  • Более тяжёлая защита от ботов (Cloudflare, Imperva)
  • Неограниченная вариативность задач, не отражённая в бенчмарке

🔹 Trade-off: breadth vs benchmark optimization
OpenAI Operator и Google Mariner намеренно жертвовали оптимизацией под конкретный бенчмарк ради более широкой применимости. Более низкие баллы не означают слабый продукт — они отражают иные приоритеты в дизайне.

🔹 Open-source конкурирует с коммерческими решениями
browser-use (89.1%), являясь open-source решением, демонстрирует результаты, сопоставимые с коммерчески разработанными операторами. Это важный сигнал для команд, выбирающих между контролем и удобством.


Часть IV. Архитектурные паттерны интеграции

Паттерн A: Plug-in MCP Toolkit (минимальные обязательства)

Параметр Значение
Сложность ★☆☆☆☆
Гибкость ★★☆☆☆
Стоимость Бесплатно (OSS)
Время до первого результата Часы

Сценарий: Быстрое прототипирование, ограниченные детерминированные задачи, учебный период.

Реализация:

  1. Агент реализует MCP-клиент (Python SDK mcp)
  2. Конфигурация добавляет @playwright/mcp:
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@playwright/mcp"],
      "env": {
        "PLAYWRIGHT_BROWSERS_PATH": "0"
      }
    }
  }
}
  1. Агент получает структурированные инструменты управления браузером.

Что получает агент:

# Пример вызова через MCP-клиент
result = await mcp_client.call_tool(
    "playwright_navigate",
    {"url": "https://example.com"}
)

Ограничение: По-прежнему по сути scripted-поведение. Интеллект живёт выше по течению в planning-модуле агента, а не в браузерном слое.


Паттерн B: AI-нативная библиотечная интеграция (средние инвестиции)

Параметр Значение
Сложность ★★★☆☆
Гибкость ★★★☆☆
Стоимость Бесплатно – \($\), зависит от хостинга
Время до первого результата Дни

Сценарий: Команды, желающие получить доказанное AI-поведение из коробки, не инвестируя в построение собственных циклов работы агента.

Реализация:

from browser_use import Browser, BrowserAgent
from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
agent = BrowserAgent(
    task="Авторизуйся на LinkedIn и сохрани список контактов в CSV",
    llm=llm
)
result = await agent.run()

Преимущества:

  • Цикл «наблюдение → размышление → действие» из коробки
  • Встроенная обработка ошибок и восстановление
  • Поддержка нескольких вкладок и контекстов

Компромиссы:

  • Меньше прозрачности в промежуточных шагах
  • Сложнее внедрять свою бизнес-логику на промежуточных этапах
  • Расходы по подписке при использовании облачной версии

Паттерн C: Пользовательский Agent Loop с Playwright (высокие инвестиции, максимальный контроль)

Параметр Значение
Сложность ★★★★☆
Гибкость ★★★★★
Стоимость Предсказуемая (платите только за LLM-токены)
Время до первого результата Недели

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

Архитектура цикла:

┌─────────────────────────────────┐
│ 1. Playwright: получить a11y tree│
└────────────┬────────────────────┘
             ▼
┌─────────────────────────────────┐
│ 2. LLM: проанализировать снапшот│
│    + цель → сгенерировать action│
└────────────┬────────────────────┘
             ▼
┌─────────────────────────────────┐
│ 3. Playwright: выполнить action │
│    → получить новое состояние   │
└────────────┬────────────────────┘
             ▼
┌─────────────────────────────────┐
│ 4. Проверка: завершено?         │
│    • Да → вернуть результат     │
│    • Нет → повтор с шага 1      │
└─────────────────────────────────┘

Ключевые компоненты для проектирования:

🔧 Асинхронный Playwright-контроллер

class RobustPlaywrightController:
    async def execute_with_retry(self, action: dict, max_retries: int = 3):
        for attempt in range(max_retries):
            try:
                return await self._execute_action(action)
            except ElementStaleError:
                await asyncio.sleep(2 ** attempt)  # экспоненциальная задержка
                continue
        raise ActionExecutionError(f"Failed after {max_retries} attempts")

🔧 Буфер истории (краткосрочная память)

from collections import deque

class AgentMemory:
    def __init__(self, max_steps: int = 20):
        self.history = deque(maxlen=max_steps)

    def add_step(self, action: dict, observation: str, llm_reasoning: str):
        self.history.append({
            "step": len(self.history) + 1,
            "action": action,
            "observation": observation,
            "reasoning": llm_reasoning
        })

🔧 Классификаторы исключений

def classify_error(error: Exception) -> RecoveryStrategy:
    if isinstance(error, ElementStaleError):
        return RecoveryStrategy.RETRY_WITH_REFRESH
    elif "CAPTCHA" in str(error):
        return RecoveryStrategy.DELEGATE_TO_HUMAN
    elif "login required" in str(error).lower():
        return RecoveryStrategy.INJECT_CREDENTIALS
    return RecoveryStrategy.FAIL

🔧 Управление токен-бюджетом

class TokenBudget:
    def __init__(self, max_tokens: int = 100_000):
        self.used = 0
        self.max = max_tokens

    def can_proceed(self, estimated_cost: int) -> bool:
        return self.used + estimated_cost <= self.max

    def record_usage(self, tokens: int):
        self.used += tokens
        if self.used > self.max * 0.9:
            logger.warning(f"Token budget at {self.used/self.max:.1%}")

🔧 Логирование наблюдаемости

import structlog

logger = structlog.get_logger()

async def run_agent_step(goal: str, memory: AgentMemory):
    snapshot = await playwright.get_accessibility_tree()
    action, reasoning = await llm.decide(snapshot, goal, memory.history)

    logger.info("agent_step",
        step=len(memory.history)+1,
        action=action,
        reasoning=reasoning,
        token_estimate=estimate_tokens(snapshot, reasoning)
    )

    result = await playwright.execute(action)
    memory.add_step(action, result, reasoning)
    return result

Инструменты для построения каркаса:

  • LangChain / LangGraph: типизированные абстракции для tool-calling, управления состоянием и графов диалога
  • Pydantic: валидация схем действий и ответов
  • structlog / Loguru: структурированное логирование с контекстом

Паттерн D: Облачный браузер как услуга (аутсорсинг инфраструктурной сложности)

Параметр Значение
Сложность ★★☆☆☆
Гибкость ★★★☆☆
Стоимость Регулярная подписка (\($\))
Время до первого результата Часы
Провайдеры: Сервис Ключевая фича Когда выбирать
Steel.cloud Инфраструктура с открытым API, интеграция с leaderboard'ом Команды, ценящие прозрачность и открытость
Bright Data Web Unlocker