Статичный WEB постепенно уходит в прошлое. Вслед за простым HTML плавно покидает нас и привычный многим «монолит» — это когда результирующий HTML для динамических сайтов генерировался силами сервера и передавался клиентам и их браузерам в готовом виде. Многие крупные сайты и веб-сервисы теперь полностью написаны на JavaScript. Если вы попытаетесь загрузить их HTML-код, то сильно удивитесь: кроме тега <HEAD> и <BODY> там ничего нет. Их содержимое — это ссылки на большое количество скриптов. Они загружаются непосредственно в браузере и выполняются в нём же, формируя по мере необходимости нужный HTML-код.
Всё это существенно усложняет процесс парсинга. Теперь нельзя просто взять связку из Python Requests и библиотеки BeautifulSoup, чтобы проанализировать содержимое и извлечь нужные данные. Сначала нужно выполнить все скрипты, отрендерить страницу и только потом смотреть на её HTML-код. Для этого нужен либо специальный JavaScript-движок, либо полноценный браузер (в браузерах такие движки уже встроены: V8 для Chromium-based и SpiderMonkey для Firefox).
Этот материал для тех, кто хочет узнать, как срейпить вебсайты с Python, если страницы имеют динамический контент: какие для этого потребуются инструменты и библиотеки, существуют ли сильные подходы и методы, на какие ошибки и подводные камни можно напороться.
Современный web сильно изменился. Когда-то всё начиналось с HTML-кода. Он давал в основном только возможность разметки, чтобы документы и web-страницы не выглядели убого. Браузеры получали HTML-контент, рендерили его и показывали пользователям.
Когда появились препроцессоры гипертекста (на базе полноценных языков программирования, таких как PHP), открылась возможность собирать web-страницы из разных блоков и элементов. Само содержимое стало храниться в специальных базах данных. По факту уже такие сайты стали называть «динамическими». За их работу стали отвечать CMS-системы, такие как WordPress, Joomla, Drupal и т.п.
Но и этого было мало. Оказалось, что динамическими могут быть не только сайты, но и контент. Особую популярность получил JavaScript, который отвечал за точечное изменение содержимого прямо в браузере — после взаимодействия с пользователем. Это была известная многим технология AJAX (она позволяла «перезагружать» и заново рендерить только отдельный блок или элемент — без перезагрузки всей страницы). Но в какой-то момент разработчики сайтов выяснили, что «монолит» из-за препроцессинга тратит слишком много времени и сил на то, чтобы подготовить браузеру результирующий HTML-код. И как не пытались они усилить серверы, их мощностей всё равно не хватало для того, чтобы справиться с потоком большого числа пользователей. Привет, Google, Amazon, Facebook!
Оригинальным ответом стали генераторы статических сайтов — на базе таких JavaScript-фреймворков, как React, Angular и Vue. Но пусть название вас не смущает. Сайты в них получаются отнюдь не статическими. Просто, теперь вместо того, чтобы каждый раз по требованию пользователя собирать результирующий HTML, сайты собираются непосредственно в браузерах. А так как JavaScript-скрипты относятся к категории «просто файлов» (то есть для сервера это статический контент), их можно раскидать по другим серверам, например, внутри распределённой CDN-сети. Тогда нагрузка на хостинг падает в разы. Плюс появляется возможность удобного децентрализованного хранения содержимого.
В итоге, когда клиент запрашивает конкретную страницу, его перенаправляют на ближайшее к нему CDN-хранилище, подтягивают из хранилища нужные JS-скрипты и собирают страницу внутри браузера.
Хитро? Да, если бы не проблемы со сбором данных с таких «динамических web-страниц». Ведь чтобы их распарсить, нужно сначала загрузить все JS-скрипты, выполнить их в специальной среде (JS-движок чем-то напоминает Java-машину) и только потом сформировать итоговый HTML-код. Проще прогнать сайт через реальный браузер. Примерно так и появилась потребность в headless-браузерах, а также в web-драйверах, которые могли организовать обмен данными с обычными браузерами (речь о таких библиотеках, как Selenium, Playwright, Puppeteer).
Вообще, статические web-страницы — это чистый HTML-код. Такие страницы можно сохранить в виде обычных текстовых документов с расширением .html. Тогда они будут открываться в любом браузере.
Пример такой статической страницы:
<!DOCTYPE html>
<html>
<head>
<title>Это заголовок страницы, который отображается в результатах поисковой выдачи, а также при наведении курсора на вкладку браузера</title>
<meta charset="utf-8">
</head>
<body>
<h1>А это заголовок, который показывается в теле страницы</h1>
<p>
Это просто абзац, внутри может быть какой-то текст с описанием чего-то полезного…
</p>
<h2>Подзаголовок, пусть будет название списка «Типы прокси»:</h2>
<ul id="thebestlist">
<li>Резидентные прокси</li>
<li>Мобильные прокси</li>
<li>Общие прокси</li>
<li>Датацентровые прокси</li>
<li>Приватные прокси</li>
</ul>
</body>
</html>
Можете поэкспериментировать: создайте простой текстовый документ на своём ПК, скопируйте в него код из примера, и измените расширение с .txt на .html. Двойной клик по файлу должен открыть страницу в браузере.
Страницы, которые создаются с помощью PHP (или других гипертекстовых препроцессоров), сначала выполняются в специальной среде на сервере и только потом отдаются браузеру. Технически это тот же «статический контент». Так как браузер работает с простым HTML.
Динамические web-страницы выглядят немного иначе. Самый яркий пример динамической web-страницы в современном Интернете — выдача Google. От HTML там только <title>, <head> и <body>. И те используются в основном для вставки ссылок на JS-скрипты.
Попытка «увидеть» осмысленный контент или вёрстку ни к чему не приведёт. Более того, код намеренно минифицирован (из него удаляются пробелы, перекаты и другие спецсимволы, чтобы уменьшить размер кода).
По такому же принципу организованы многие другие крупные сайты и веб-сервисы, поэтому можно парсить TikTok, собирать данные с IMDb и изучать другие платформы..
Но самый распространённый формат динамического контента, на подавляющем большинстве сайтов и сервисов — это встраивание блоков или элементов с подгрузкой на основе действий пользователя. Это могут быть:
Лучшие прокси-серверы для доступа к ценным данным со всего мира.
Примерно здесь уже должна быть понятна вся трагедия web-скрапинга динамического контента. Но на всякий случай уточним детали:
Так как вебстраницы с динамическим контентом нужно предварительно рендерить, то для парсинга на языке Python над могут потребоваться следующие инструменты и библиотеки:
Это стандартный джентльменский набор для веб-скрапинга динамического контента. При желании можете посмотреть дополнительные библиотеки для парсинга на Python.
Остальное будет выявляться по мере необходимости: инструменты для реализации случайных задержек и асинхронных запросов, скрипты для продвинутой ротации прокси, библиотеки удобного форматирования данных и т.п.
Headless-браузеры в списке намеренно не указали, так как сейчас в большинстве случаев они устанавливаются пакетом вместе с web-драйверами.
Общий алгоритм web скрапинга динамического контента выглядит следующим образом:
Ниже подробнее погрузимся в технические детали парсинга динамических web-страниц.
Отдельно стоит оговорить возможность обмена данными с целевыми сайтами по API. Некоторые крупные площадки знают, что информация с их страниц участвует в разных бизнес-процессах. И специально для того, чтобы разгрузить свой основной сайт (web-версию интерфейса), для парсеров предоставляется отдельный интерфейс — программный (API, то есть Application Programming Interface, русск. «Программный Интерфейс Приложения»).
API можно найти, например, у таких площадок, как Amazon (платный, с небольшим объёмом бесплатных обращений), IMDb (платный, формат GraphQL), TikTok (бесплатно, в том числе для бизнес-задач).
Соответственно, вы можете написать свой Python-скрипт, который будет формировать запросы к нужному API-ресурсу и разбирать полученные ответы на составляющие. Обычно данные поставляются в структурированном виде, поэтому разобрать их на отдельные фрагменты будет несложно. Никакие headless-браузеры и web-драйверы для этого не нужны. Достаточно простейших библиотек для работы с HTTP-запросами, например, Python Requests (тут даже есть встроенная поддержка разбора ответов в формате JSON).
Основная проблема — у каждого сайта обычно свой API. То есть свой набор команд и формат ответов. Плюс для подключения обязательно нужно зарегистрировать аккаунт разработчика и получить API-ключ (он выступает в роли идентификатора). На каждый аккаунт обычно выставляются специальные технические лимиты. Тут стоит сказать, что, если вы превысите ограничения, никаких серьёзных санкций за это не применят. Максимум вместо ответа будут возвращать ошибку. Как только лимит сбросится, вы снова сможете обмениваться данными.
Многие крупные сервисы, как поиск Google, видеохостинг YouTube и т.п., не имеют API. Но их данные крайне важны и могут участвовать в комплексной аналитике.
Вместо этого вы можете использовать сервисы-посредники, которые реализуют недостающий API. Но они как правило платные.
Схема работы таких сервисов выглядит примерно так:
Пример такого сервиса — Froxy Web Scraper. Вы можете работать со многими крупными площадками, такими как Google, Ask, Yahoo, Bing, YouTube, Amazon, AliExpress, Ebay, Wildberries, Pinterest и прочими. Для нестандартных задач есть опция получения результирующего HTML-кода по списку целевых страниц. Для каждого задания на парсинг можно установить периодичность и параметры таргетинга (местоположение прокси, вплоть до города и оператора связи, а также тип адреса: мобильный или резидентный). По окончании парсинга сервис может отправлять вызовы на указанные веб-хуки — чтобы ваш скрипт мог начать скачивание результатов и пустить их в дальнейшую обработку.
Пример работы в материале про парсинг Google SERP.
Получите доступ к прокси-сети с 200+ локациями и 10+ миллионами IP-адресов.
Установка библиотеки выполняется командой:
pip install selenium
PIP — это штатный менеджер пакетов для Python. Обратите внимание, что для успешного выполнения ссылка в Windows на исполняемый файл pip.exe должна быть добавлена в переменные окружения.
WebDriver и пакет совместимых headless-браузеров будут установлены автоматически. Это займёт некоторое время, поэтому обязательно дождитесь успешного завершения процесса установки.
Пример простейшего скрипта, который будет обращаться к специальному тестовому сайту (scrapingcourse.com/javascript-rendering) и собирать список товаров:
import time
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
# Настройки Selenium
driver = webdriver.Chrome() # Запуск браузера
driver.get("https://www.scrapingcourse.com/javascript-rendering") # Открываем целевой сайт
time.sleep(5) # Ждём загрузки результатов
# Получение результатов поиска
results = driver.find_elements(By.CSS_SELECTOR, "div.product-item") # Контейнеры с товарами
data = [] # Список для хранения данных
for result in results:
try:
title = result.find_element(By.CSS_SELECTOR, "span.product-name").text # Заголовок
link = result.find_element(By.TAG_NAME, "a").get_attribute("href") # Ссылка
price = result.find_element(By.CSS_SELECTOR, "span.product-price").text # Цена
data.append([title, link, price]) # Добавляем в список
except Exception as e:
print(f"Ошибка при парсинге: {e}")
# Закрытие браузера
driver.quit()
# Сохранение в CSV
csv_filename = "products_results.csv"
with open(csv_filename, "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["Заголовок", "Ссылка", "Цена"])
writer.writerows(data)
# Вывод данных в консоль
for item in data:
print(item)
Создайте текстовый файл с именем «web_scraping_dynamic_content_with_selenium». Замените расширение с .txt на .py. Откройте его в любом текстовом редакторе, скопируйте и вставьте код, приведённый выше. Сохраните изменения.
Теперь осталось запустить скрипт (команда для консоли,:
python web_scraping_dynamic_content_with_selenium.py
Если вы не переходили в каталог расположения скрипта, то укажите полный путь к файлу, например:
python C:\my-scraper\web_scraping_dynamic_content_with_selenium.py
После запуска и закрытия окна браузера (подождите 5 секунд) в консоли должен вывестись список товаров с ценами и ссылками. А рядом со скриптом (в той же папке) появится CSV-файл с тем же содержимым, но уже в готовом табличном виде.
Установка библиотеки выполняется несколькими командами. Первая ставит саму библиотеку и зависимости к ней, вторая — доустанавливает все необходимые браузеры:
pip install playwright
playwright install
Если вам нужен конкретный браузер, то его нужно указать в качестве дополнительного аргумента. Например, так:
playwright install chromium
Пример простейшего скрипта, который работает точно также, как и скрипт для Selenium. То есть обращается к тестовому динамическому web-сайту (scrapingcourse.com/javascript-rendering) и собирает с него список товаров:
import csv
from playwright.sync_api import sync_playwright
# Логика парсинга Товаров
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=False) # Запуск браузера без фонового режима
page = browser.new_page()
# Открываем целевой сайт
page.goto("https://www.scrapingcourse.com/javascript-rendering")
# Ждем загрузки результатов. Здесь указываем конкретный селектор (вместо ожидания по времени, дефолтный таймаут 30 секунд, на случай, если выбранный селектор не будет найден)
page.wait_for_selector("div.product-item")
# Собираем результаты
results = page.locator("div.product-item").all()
data = []
for result in results:
try:
title = result.locator("span.product-name").inner_text() # Заголовок
link = result.locator("a").get_attribute("href") # Ссылка
price = result.locator("span.product-price").inner_text() # Цена
data.append([title, link, price])
except Exception as e:
print(f"Ошибка при парсинге: {e}")
browser.close() # Закрываем браузер
# Сохранение в CSV
csv_filename = "results_playwright.csv"
with open(csv_filename, "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["Заголовок", "Ссылка", "Цена"])
writer.writerows(data)
# Вывод данных в консоль
for item in data:
print(item)
Создайте текстовый файл с именем «web_scraping_dynamic_content_with_playwright». Скопируйте код, представленный выше, и сохраните файл. Закройте и смените расширение с .txt на .py.
Запустите скрипт в консоли:
python web_scraping_dynamic_content_with_playwright.py
Вывод будет аналогичным первому скрипту. В каталоге со скриптом должен появиться файл results_playwright.csv (с таблицей товаров, ссылок и цен).
Во-первых, нужно детально изучить и понять, как работают синтаксические анализаторы. Это краеугольный камень для качественного парсинга — они нужны для поиска и перемещения по DOM-структуре (по схеме HTML-разметки).
Без понимания того, что и где нужно искать, вы не сможете «объяснить» ничего своей программе.
Разные web-драйвера используют разные синтаксисы для поиска элементов и для сбора из них данных.
Например, Playwright работает с локаторами (это внутренний механизм), а также поддерживает синтаксис XPath и CSS. Если этого недостаточно, вы можете доустановить библиотеки других синтаксических анализаторов, таких как BeautifullSoup (тут будет уже другой синтаксис и порядок поиска по DOM-структуре).
У Selenium немного другой подход, но он во-многом похож на работу Playwright. Внутренний инструмент «By» может искать элементы на основе имён, идентификаторов, тегов, классов, CSS-селекторов и XPath-синтаксиса. Опять же, никто не запрещает вам передавать результирующий HTML в другой синтаксический анализатор.
Во-вторых, крупные web-сервисы с динамическим содержимым давно научились определять headless-браузеры по цифровым отпечаткам. Чтобы избежать блокировок и показа капчи, нужно скрывать эти следы и стараться работать через прокси (так как каждый новый IP-адрес прокси — это новый клиент, который изучается и блокируется системами безопасности в индивидуальном порядке).
Прокси к headless-браузерам обычно подключается на уровне отдельно взятого экземпляра. Поэтому, чтобы не придумывать костыли с ротацией прокси и запуском новых браузеров (ведь память вашего устройства не бесконечная), логично изначально подключать ротируемые прокси.
Современные сайты сильно усложнились. Теперь это не просто HTML-страницы, а полноценные web-приложения. Работать с динамическим содержимым стало заметно труднее, так как вы не можете напрямую получить DOM-структуру и выбрать нужные данные из HTML. Вместо этого вам нужно загружать сайт или динамические страницы в настоящем браузере (его роль могут выполнять headless-браузеры или антидетекты) и только потом приступать к парсингу.
Несмотря на то, вы работаете с контентом через браузеры, вам всё равно придётся продумать массу технических деталей, среди которых эмулирование цифровых отпечатков, имитация поведения пользователя и работа через прокси.
Найти качественные прокси можно у нас. Froxy — это 10+ млн. IP (резидентные, мобильные и серверные прокси с таргетингом до города и оператора связи).
Если вам не хочется усложнять свой Python-скрапер, всегда можно задействовать парсинг по API. Froxy сможет помочь и здесь. Вы можете отправить задание на сбор данных по конкретным сайтам. Мы всё сделаем сами и отдадим вам готовую структурированную информацию в JSON или CSV-формате. Никаких прокси и headless-браузеров настраивать не придётся. Подробности — в описании услуги скрапер Froxy.