Веб-скрейпинг с Playwright стал выбором по умолчанию для всех, кто хочет собирать данные со страниц, которые формируются только после выполнения JavaScript. Созданный компанией Microsoft, Playwright управляет реальным браузером, ожидает контент так, как это делает человек, и предоставляет единый согласованный API для Chromium, Firefox и WebKit. Эта комбинация делает его намного менее хрупким, чем старые инструменты автоматизации, с которых начинало большинство скрейперов.
Данное руководство является практическим пошаговым разбором веб-скрейпинга с Playwright на Python, с кратким примером на Node там, где API различается. Вы установите Playwright и его браузеры, запустите headless Chromium, перейдёте на страницу, дождётесь нужного селектора, извлечёте текст и атрибуты, обработаете взаимодействие «загрузить ещё» и пагинацию, сделаете скриншот и захватите JSON-ответ из сети. В конце мы честно расскажем об операционной реальности: Playwright по-прежнему блокируется при масштабировании, и где управляемый сервис рендеринга с ротацией занимает своё место.
Почему Playwright лучше старых инструментов автоматизации
Если вы писали скрейперы с Selenium или чистым Puppeteer, первое, что замечаешь в Playwright, это исчезновение ненадёжных вызовов sleep(). За это отвечают несколько дизайнерских решений.
- Автоожидание. Прежде чем Playwright кликает, заполняет или читает элемент, он ждёт, пока тот не будет подключён, видим, стабилен и доступен для взаимодействия. Вы перестаёте разбрасывать произвольные задержки по коду, и результирующие скрейперы становятся значительно надёжнее на медленных или анимированных страницах.
- Три браузерных движка, один API. Один и тот же скрипт работает против Chromium, Firefox или WebKit. Когда сайт ведёт себя по-разному в одном движке, вы переключаетесь одним словом вместо переписывания настройки драйвера.
- Надёжные селекторы. Помимо CSS и XPath, Playwright поставляет локаторы и текстовые селекторы, которые разрешаются лениво и перезапрашивают DOM во время действия, поэтому они переживают ре-рендеры, которые сломали бы закешированный дескриптор элемента.
- Async по дизайну. API построен вокруг асинхронного I/O, что делает естественным запуск множества страниц параллельно в рамках одного процесса браузера при масштабировании.
Для понимания того, почему реальный браузер иногда неизбежен, смотрите статью headless-браузеры для веб-скрейпинга. Если у вас есть существующий стек на Selenium и вы хотите сравнение бок о бок, статья скрейпинг динамического контента с Selenium и BeautifulSoup покрывает этот путь.
Предварительные требования
Прежде чем писать код, нужны три вещи, ни одна из которых не занимает много времени.
Python 3.8 или новее. Проверьте версию командой python --version. У Playwright также есть первоклассная привязка для Node.js, если вы предпочитаете JavaScript; концепции из этого руководства соответствуют один к одному, и краткий пример на Node появится позже.
Умение работать с селекторами. Вы должны уметь открыть инструменты разработчика браузера, проинспектировать элемент и считать CSS-селектор. Извлечение данных, это по большей части упражнение с селекторами, когда страница отрендерена.
Цель, которую вам разрешено скрейпить. Используйте сайт, условия которого это допускают, ограничивайтесь публичными данными и соблюдайте robots.txt и разумные ограничения по частоте запросов. Описанные здесь техники универсальны; ответственность за то, куда вы их направляете, лежит на вас.
Установка Playwright и браузеров
Создайте виртуальное окружение, чтобы зависимости оставались изолированными, установите пакет Playwright, затем запустите его установщик для загрузки бинарных файлов браузеров. Этот второй шаг тот, который люди обычно забывают; пакет pip сам по себе не включает браузеры.
python -m venv pw_env source pw_env/bin/activate pip install playwright playwright install chromium
В Windows активируйте окружение командой pw_env\Scripts\activate вместо строки source. Команда playwright install chromium загружает зафиксированную сборку Chromium; передайте без аргументов, чтобы загрузить все три движка. Если вы когда-нибудь видите ошибку об отсутствующем исполняемом файле, это почти всегда означает, что этот шаг установки был пропущен.
Запуск браузера и открытие страницы
Начните с наименьшего полезного скрипта: запустите headless Chromium, откройте страницу, перейдите по URL и прочитайте заголовок. Синхронный API делает первый пример читабельным; к async мы переходим, когда это важно для масштабирования.
from playwright.sync_api import sync_playwright def main(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto("https://quotes.toscrape.com/js/") print(page.title()) browser.close() if __name__ == "__main__": main()
Несколько замечаний о сделанных выборах. headless=True запускает браузер без видимого окна, что нужно для автономных задач; переключите на False в процессе разработки, чтобы наблюдать за работой браузера. Выбранный URL является специально предназначенной JavaScript-рендеренной демо-страницей: цитаты появляются только после выполнения скрипта, что именно тот случай, когда обычный HTTP-запрос возвращает пустой контейнер и Playwright показывает себя с лучшей стороны.
Для чего-либо, помимо одноразового скрипта, создайте контекст браузера с browser.new_context() перед new_page(). Контекст является изолированной сессией со своими куками, хранилищем и user agent, поэтому вы можете запускать несколько независимых страниц, не допуская утечки состояния между ними. Вызов new_page() напрямую, как выше, использует контекст по умолчанию, что нормально для одной страницы.
Ожидание селектора, затем извлечение текста и атрибутов
Это суть веб-скрейпинга с Playwright. Вместо того чтобы угадывать, сколько времени нужно странице, вы ждёте конкретного элемента, который сигнализирует о присутствии данных, и затем читаете их. Локаторы Playwright автоматически ждут, поэтому один вызов и ждёт, и выбирает элемент.
from playwright.sync_api import sync_playwright def scrape_quotes(url): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto(url) page.wait_for_selector("div.quote") results = [] for quote in page.query_selector_all("div.quote"): text = quote.query_selector("span.text").inner_text() author = quote.query_selector("small.author").inner_text() link = quote.query_selector("a").get_attribute("href") results.append({"text": text, "author": author, "link": link}) browser.close() return results
Наиболее важная строка, это page.wait_for_selector("div.quote"). Она блокирует выполнение до тех пор, пока в DOM не появится хотя бы один элемент с цитатой, что означает: JavaScript выполнился и данные присутствуют. После этого query_selector_all возвращает все совпадающие элементы, а inner_text() и get_attribute() извлекают текст и атрибуты соответственно. Считывание href с якоря демонстрирует случай с атрибутом; считывание цитаты и автора демонстрирует случай с текстом. Ни одного фиксированного ожидания нигде.
Обработка кликов «загрузить ещё» и пагинации
Реальные цели редко показывают всё сразу. Два паттерна покрывают большинство из них: кнопка «загрузить ещё», добавляющая контент на месте, и нумерованная пагинация или кнопка «следующая», которая подменяет страницу. Playwright обрабатывает оба, потому что умеет кликать и затем ждать результата.
Для кнопки «загрузить ещё» кликайте в цикле до её исчезновения, ожидая после каждого клика, пока новый контент не осядет.
def load_all(page): while True: button = page.query_selector("button.load-more") if not button or not button.is_visible(): break button.click() page.wait_for_load_state("networkidle")
Для классической пагинации следуйте по ссылке «следующая» до её исчезновения, скрейпя каждую страницу по мере продвижения. Поскольку локаторы перезапрашивают DOM при каждом вызове, вам не нужно беспокоиться об устаревших дескрипторах после навигации.
def scrape_all_pages(page, url): page.goto(url) rows = [] while True: page.wait_for_selector("div.quote") for q in page.query_selector_all("div.quote span.text"): rows.append(q.inner_text()) next_link = page.query_selector("li.next a") if not next_link: break next_link.click() return rows
Обратите внимание на wait_for_load_state("networkidle") в первом фрагменте: он ждёт, пока в течение короткого промежутка времени не будет активных сетевых запросов, что является хорошим сигналом о том, что лениво загружаемый контент появился. Используйте его после действий, которые инициируют фоновые запросы.
Создание скриншота
Скриншоты полезны для отладки скрейпера, возвращающего пустые результаты, и для архивирования того, как выглядела страница в момент захвата. Playwright по умолчанию захватывает видимый viewport или всю прокручиваемую страницу с одним флагом.
page.screenshot(path="page.png", full_page=True)
Когда запуск возвращает пустые данные, полностраничный скриншот, сделанный непосредственно перед извлечением, обычно за секунды объясняет почему: стена куки, CAPTCHA или страница блокировки там, где должен быть ваш контент.
Захват сетевых ответов и JSON
Нередко самые чистые данные находятся не в HTML вообще, а в JSON API, который страница вызывает в фоне. Вместо парсинга отрендеренной разметки вы можете прослушивать сетевые ответы и забирать этот JSON напрямую. Это быстрее и гораздо менее хрупко, чем скрейпинг DOM, потому что форма API меняется реже, чем разметка.
captured = [] def on_response(response): if "/api/" in response.url and response.ok: try: captured.append(response.json()) except Exception: pass page.on("response", on_response) page.goto("https://example.com/listings") page.wait_for_load_state("networkidle")
Хук page.on("response", ...) срабатывает для каждого сетевого ответа. Фильтрация по фрагменту URL изолирует нужные вам вызовы, а response.json() парсит тело за вас. Сначала откройте вкладку Network в инструментах разработчика браузера, чтобы найти, какой эндпоинт несёт данные, затем ищите его здесь. Если сайт активно использует XHR-вызовы, смотрите статью как скрейпить JavaScript-страницы с Python для подробного разбора подхода «API прежде всего».
Тот же скрипт на Node.js
Если ваш стек на JavaScript, привязка для Node почти точно зеркалит Python. Имена методов совпадают, всё основано на промисах, и браузеры устанавливаются так же: npx playwright install chromium.
const { chromium } = require("playwright"); (async () => { const browser = await chromium.launch({ headless: true }); const page = await browser.newPage(); await page.goto("https://quotes.toscrape.com/js/"); await page.waitForSelector("div.quote"); const texts = await page.$$eval("div.quote span.text", els => els.map(e => e.textContent)); console.log(texts); await browser.close(); })();
Python wait_for_selector становится waitForSelector, а $$eval запускает функцию на странице для одновременного извлечения многих элементов. Выбирайте тот язык, который ваша команда уже поддерживает; логика скрейпинга идентична.
Стелс-реальность: Playwright по-прежнему блокируется
Вот часть, которую большинство руководств пропускают. Управление реальным браузером решает проблему рендеринга, но не делает вас невидимым. Современные системы защиты от ботов смотрят на гораздо большее, чем выполняется ли JavaScript. Они снимают отпечаток браузера, инспектируют TLS и HTTP/2 сигнатуры, оценивают поведенческие сигналы и ограничивают частоту запросов по IP. Обычный headless запуск Playwright оставляет следы, и при любом реальном объёме большей проблемой является ваш IP: несколько датацентровых адресов, долбящих один хост, быстро помечаются.
С этим можно бороться. Люди добавляют плагины стелса, рандомизируют user agents и viewports, замедляют запросы и подключают пул прокси. Каждое из них помогает, и каждое является бременем обслуживания. Запуск флота headless-браузеров сам по себе является операционными накладными расходами: они прожорливы по памяти, они падают, им нужна зафиксированная версия браузера, и их параллелизация на нескольких машинах является реальной инфраструктурной работой. Делать всё это и при этом поддерживать здоровый ротирующий пул прокси, откровенно говоря, и является большей частью работы.
О более глубоком руководстве по сохранению незаблокированности читайте как скрейпить сайты без блокировок.
Когда Playwright начинает сталкиваться с блокировками при масштабировании, Crawling API берёт на себя сложную часть. Он рендерит страницу в реальном браузере и направляет запрос через ротирующие резидентные IP на стороне сервера, затем передаёт вам готовый HTML или распарсенные данные за один вызов, так что вы минуете запуск headless-флота и пула прокси самостоятельно. Вы по-прежнему можете держать Playwright локально для взаимодействие-ориентированных потоков, которым действительно нужен драйвер.
Где управляемый API уместен, а где Playwright по-прежнему выигрывает
Это не Playwright против управляемого API; речь о том, чтобы знать, какой инструмент подходит для какой задачи. Обращайтесь к Crawling API, когда узким местом являются блокировки, CAPTCHA или репутация IP, когда вы краулите много страниц и не хотите поддерживать браузерную и прокси-инфраструктуру, или когда вам просто нужен надёжно отрендеренный HTML в большом объёме. Поскольку рендеринг и ротация происходят на стороне сервера, вы делаете простой запрос и парсите результат, без флота для присмотра.
Оставляйте Playwright локально, когда задача действительно интерактивна: многоэтапные формы, аутентифицированные потоки за управляемым вами логином, перетаскивание, загрузка файлов или всё, где нужно выполнить точную последовательность действий пользователя и наблюдать результат. Оба инструмента хорошо компонуются. Многие команды прототипируют и обрабатывают взаимодействие-интенсивные потоки в Playwright, а затем направляют высоконагруженный трафик получения данных через управляемый API, когда блокировки становятся ограничивающим фактором. Если вы хотите ротацию IP как выпадающий эндпоинт, сохраняя собственный браузер, Smart AI Proxy предоставляет резидентную ротацию за стандартным прокси-интерфейсом.
Ключевые выводы
- Playwright устраняет нестабильность. Автоожидание, три браузерных движка за одним API, ленивые локаторы и async делают его надёжнее старых драйверов для рендеренных страниц.
-
Ждите, затем извлекайте. Используйте
wait_for_selector, чтобы убедиться в рендеринге данных, затем читайте текст черезinner_text()и атрибуты черезget_attribute(). -
Клики и пагинация встроены. Запустите цикл кнопки «загрузить ещё» до её исчезновения или следуйте по ссылке «следующая», ожидая
networkidleпосле действий, вызывающих запросы. -
Забирайте JSON когда можете. Прослушивание
page.on("response", ...)для фонового API-вызова быстрее и гораздо менее хрупко, чем парсинг DOM. - Рендеринг это не стелс. Playwright по-прежнему подвергается снятию отпечатков и IP-блокировкам при масштабировании; управляемый API рендеринга с ротацией устраняет накладные расходы на флот и прокси, а Playwright остаётся идеальным для взаимодействие-интенсивных локальных потоков.
Часто задаваемые вопросы
Подходит ли Playwright для веб-скрейпинга?
Да. Playwright управляет реальным браузером, поэтому он обрабатывает страницы с JavaScript-рендерингом, которые обычный HTTP-запрос не может. Его автоожидание устраняет большую часть нестабильности по времени, свойственной старым инструментам, он поддерживает Chromium, Firefox и WebKit через один API, а его ленивые локаторы переживают ре-рендеры. Для взаимодействие-интенсивных или клиент-рендеренных целей это один из сильнейших доступных вариантов.
Стоит ли использовать Playwright с Python или Node.js?
Оба работают; API почти идентичны. Имена методов различаются только в регистре (wait_for_selector в Python становится waitForSelector в Node), и оба устанавливают браузеры одной командой. Выбирайте тот язык, который ваша команда уже поддерживает, чтобы скрейпер вписался в остальной стек.
Как дождаться загрузки контента в Playwright?
Ожидайте конкретного элемента, сигнализирующего о присутствии данных, с помощью page.wait_for_selector("your.selector"), что блокирует до появления этого элемента. Для фоновых запросов, инициируемых кликом, используйте page.wait_for_load_state("networkidle"), чтобы дождаться затихания сетевой активности. Избегайте фиксированных ожиданий; автоожидание Playwright и эти явные ожидания надёжнее.
Можно ли получить блокировку при скрейпинге с Playwright?
Да. Запуск реального браузера решает проблему рендеринга, но не обнаружения. Системы защиты от ботов снимают отпечаток браузера, инспектируют сетевые сигнатуры и ограничивают частоту запросов по IP, поэтому обычные headless запуски помечаются, а датацентровые IP блокируются при объёме. Замедление, рандомизация отпечатков и ротация резидентных IP помогают; управляемый Crawling API объединяет рендеринг и ротацию, чтобы вы не поддерживали этот стек самостоятельно.
Как захватить API или JSON данные с Playwright?
Подключите обработчик через page.on("response", ...), фильтруйте ответы по фрагменту URL, чтобы найти эндпоинт с данными, и вызовите на нём response.json(). Сначала используйте вкладку Network в инструментах разработчика браузера для определения правильного вызова. Чтение базового JSON быстрее и намного менее хрупко, чем парсинг отрендеренного HTML.
Когда стоит использовать Crawling API вместо Playwright?
Переходите на Crawling API, когда блокировки, CAPTCHA или репутация IP становятся вашим узким местом, или когда вы краулите много страниц и не хотите запускать браузерную и прокси-инфраструктуру. Он рендерит и ротирует IP на стороне сервера и возвращает готовый HTML за один вызов. Оставляйте Playwright для подлинно интерактивных локальных потоков вроде аутентифицированных многоэтапных форм и направляйте высоконагруженный трафик получения данных через API.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

