Selenium или Playwright в Python: чем платишь за каждый
Оба инструмента гоняют браузер из Python, но по-разному ждут элементы, ставятся и параллелятся. Разбираем, где Selenium ещё нужен, а где Playwright снимает половину болей с ожиданиями.

Вы пишете скрипт, который логинится на сайт и забирает данные из таблицы, которая догружается через JavaScript. На Selenium первая же версия падает с NoSuchElementException — элемент ещё не отрисовался, а find_element отработал мгновенно. Вы обкладываете код WebDriverWait, потом ловите StaleElementReferenceException после перерисовки DOM. На Playwright тот же сценарий чаще всего работает без единого явного ожидания. Это не магия и не маркетинг — разная модель ожиданий. Разберём, чем именно отличаются два инструмента и когда какой брать.
Речь про Python: Selenium 4.x (пакет selenium) и Playwright для Python (пакет playwright, ветка 1.x). Оба управляют настоящим браузером, оба умеют Chromium, Firefox и WebKit. Дальше начинаются различия.
Главное различие: как инструмент ждёт элементы
Selenium исторически синхронный и «глупый» по отношению ко времени. driver.find_element(By.CSS_SELECTOR, ...) ищет элемент один раз здесь и сейчас. Если DOM ещё не готов — исключение. Чтобы это лечить, есть явные ожидания через WebDriverWait и expected_conditions, и неявное ожидание implicitly_wait. Смешивать их не рекомендуется: суммарные задержки становятся непредсказуемыми.
Playwright встраивает ожидания в каждое действие. Когда вы вызываете page.click(...), движок сам ждёт, пока элемент появится, станет видимым, перестанет двигаться и будет готов принять клик. Это называется auto-waiting и actionability*. В большинстве сценариев явные wait просто не нужны.
Selenium заставляет вас думать про время явно на каждом шаге. Playwright думает про время за вас — и это меняет не синтаксис, а само количество строк, которые ломаются на CI.
Как это выглядит в коде
Selenium 4.x, ожидание появления кнопки перед кликом:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com/login")
# Ждём до 10 секунд, пока кнопка не станет кликабельной
wait = WebDriverWait(driver, 10)
submit_button = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type=submit]"))
)
submit_button.click()
driver.quit()Playwright 1.x, тот же смысл — ожидание встроено в click:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com/login")
# click сам ждёт видимости и кликабельности кнопки
page.click("button[type=submit]")
browser.close()Разница в одну строку на примере кажется мелочью. На проекте из сотни сценариев она превращается в отсутствие россыпи WebDriverWait, каждый из которых — потенциальный флакающий тест.
Установка и драйверы
Ещё одна практическая боль Selenium — драйверы браузеров. Раньше chromedriver нужно было скачивать вручную и следить, чтобы его версия совпадала с версией Chrome. В Selenium 4.x встроен Selenium Manager, который подтягивает подходящий драйвер сам. Но само наличие связки «браузер + отдельный драйвер + их совместимость» никуда не делось и остаётся источником сбоев в окружениях, где обновляется одно, но не другое.
Playwright ставит браузеры своей командой и пинует их к версии библиотеки:
# установка пакета
pip install playwright
# загрузка совместимых сборок браузеров
playwright installПосле playwright install у вас лежат конкретные сборки Chromium, Firefox и WebKit, привязанные к версии Playwright. Проблема рассинхрона версий снимается на корню — но взамен эти сборки занимают несколько сотен мегабайт на диске, что заметно в контейнерах.
Параллельность и асинхронность
Selenium — синхронный API. Параллелить его принято внешними средствами: pytest-xdist, отдельные процессы, Selenium Grid для распределения по нодам. Внутри одного процесса один драйвер работает последовательно.
Playwright предлагает два API из коробки: синхронный (sync_api) и асинхронный (async_api) на базе asyncio. Асинхронный вариант позволяет держать несколько страниц и контекстов в одном процессе без блокировок. Это удобно, когда вы, например, параллельно обходите десятки страниц одного сайта.
import asyncio
from playwright.async_api import async_playwright
async def fetch_title(context, url):
page = await context.new_page()
await page.goto(url)
title = await page.title()
await page.close()
return title
async def main():
urls = ["https://example.com", "https://example.org"]
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context()
# запускаем страницы параллельно в одном процессе
titles = await asyncio.gather(
*(fetch_title(context, u) for u in urls)
)
await browser.close()
return titles
print(asyncio.run(main()))Таблица: где что выигрывает
| Критерий | Selenium 4.x | Playwright 1.x (Python) |
|---|---|---|
| Ожидания элементов | Явные, ручные (WebDriverWait) | Встроенные в действия |
| Драйверы браузеров | Отдельные, Selenium Manager подтягивает | Свои сборки через playwright install |
| Async из коробки | Нет, только внешние процессы | Да, sync и async API |
| Поддержка браузеров | Chrome, Firefox, Edge, Safari и др. | Chromium, Firefox, WebKit |
| Стандарт W3C WebDriver | Да, спецификация | Свой протокол (CDP и др.) |
| Зрелость экосистемы | Больше, старше, много готовых решений | Моложе, но активно растёт |
| Перехват сетевых запросов | Ограниченно | Штатно (route, интерсепт) |
Когда что выбирать
- Нужен реальный Safari или Edge на конкретной ОС — Selenium через WebDriver умеет работать с системными браузерами. Playwright тестирует движок WebKit, но это не сам Safari со всеми его особенностями.
- Уже есть большой парк Selenium-тестов и Grid — переписывать ради переписывания смысла нет. Selenium стабилен и никуда не уходит.
- Новый проект, тесты флакают на ожиданиях — Playwright снимает основной класс проблем с таймингами. Это самый частый повод перейти.
- Нужен перехват и подмена сетевых запросов, мокирование API — у Playwright это штатно через
page.route, на Selenium — костыльно. - Нужна асинхронная обработка множества страниц в одном процессе — async API Playwright, без плясок с процессами.
Отдельная тема — работа с антибот-защитой сайтов: оба инструмента детектируются, и это не решается выбором между ними. Здесь эту тему не разворачиваем.
Что ломается на практике
У Selenium типичная беда — StaleElementReferenceException: вы получили ссылку на элемент, DOM перерисовался, ссылка протухла. Лечится повторным поиском элемента прямо перед действием, а не хранением ссылки.
У Playwright встроенные ожидания создают ложное чувство надёжности: если селектор совпадает с двумя элементами, действие упадёт со strict mode violation — это защита, а не баг. Пишите однозначные локаторы через page.get_by_role, get_by_text и подобные, а не хрупкие CSS по классам.
Общая ловушка обоих — headless-режим ведёт себя иначе, чем видимый браузер: другие размеры окна, иногда другой рендеринг. Если сценарий проходит с открытым окном, но падает в CI, первым делом проверьте viewport и наличие шрифтов в контейнере.
Что делать дальше
Если начинаете с нуля — берите Playwright, поставьте pip install playwright и playwright install, напишите первый сценарий на синхронном API, а к асинхронному переходите, когда упрётесь в скорость. Если у вас Selenium — не мигрируйте разом, а начните с новых сценариев на Playwright и смотрите, снижается ли доля флакающих запусков на CI. Замеряйте именно долю нестабильных прогонов до и после: это тот показатель, ради которого миграция обычно и затевается. Синтаксис локаторов и полный список действий сверяйте в официальной документации Playwright — API ветки 1.x периодически пополняется.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: Playwright for Python — официальная документация, Selenium — официальная документация
Частые вопросы
У меня Playwright другой версии, примеры сработают?
Скрипт падает в CI, но работает у меня локально — почему?
Можно ли использовать оба инструмента в одном проекте?
Selenium устарел и его пора выбрасывать?
Что быстрее — Selenium или Playwright?
Playwright умеет то же, что Selenium с Grid, — распределять по машинам?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.