Як скрапити сайти, які рендеряться JavaScript
Багато сучасних сайтів надсилають майже порожній HTML-документ і будують вміст у браузері за допомогою JavaScript. Якщо ваш скрапер завантажує сторінку і не знаходить товарів, цін або тексту статті, вміст є, але він приходить пізніше. Цей посібник показує, як визначити, з яким типом сторінки ви маєте справу, як знайти швидший шлях, ніж браузер, якщо він існує, і як скрапити відрендерені сторінки з Playwright на Python. Наприкінці є примітка про те, що робити, коли відрендерена сторінка показує капчу.
Крок 1: перевірте, чи є вміст у HTML
Відкрийте сторінку з view-source: у своєму браузері або завантажте її звичайним HTTP-запитом і знайдіть у джерелі фрагмент тексту, який ви бачите на екрані. Зазвичай трапляються три варіанти:
- Текст є. Сторінка відрендерена на сервері. Звичайного HTTP-клієнта і парсера HTML достатньо, і їх варто використовувати, бо вони швидші і простіші.
- Тексту немає, але є блок JSON. Багато сайтів вбудовують свій стан у тег script, наприклад
__NEXT_DATA__. Парсіть цей JSON напряму. - Тексту немає, і блоку немає. Сторінка завантажує вміст додатковими запитами. Подивіться на вкладку Network у DevTools, відфільтровану за Fetch/XHR, поки сторінка завантажується.
Крок 2: викличте JSON-ендпоінт, якщо він є
Коли сторінка отримує свої дані з ендпоінту, виклик цього ендпоінту напряму зазвичай найкращий варіант. Він менший, швидший і стабільніший за відрендерену сторінку. Скопіюйте URL запиту, перевірте, які заголовки вимагає сайт, і спробуйте з скрипта. Робіть це лише там, де умови сайту це дозволяють, і дотримуйтеся будь-яких обмежень частоти або автентифікації, яких очікує сайт. Недокументований ендпоінт може змінитися без попередження, тож напишіть парсер так, щоб він голосно падав, коли структура відповіді змінюється.
Крок 3: рендеріть сторінку, коли це потрібно
Якщо вміст справді потребує браузера, керуйте headless-браузером. Playwright - гарний вибір для Python. Приклад нижче відкриває список товарів, чекає, поки з'явиться вміст, і читає текст з кожної картки:
from playwright.sync_api import sync_playwright
URL = "https://example.com/products" # сторінка, яку ви маєте право скрапити
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(URL, wait_until="domcontentloaded", timeout=60_000)
# Чекаємо на елемент, який підтверджує, що дані завантажилися.
page.wait_for_selector(".product-card", timeout=30_000)
titles = page.locator(".product-card h2").all_inner_texts()
browser.close()
for title in titles:
print(title)
Очікування конкретного селектора набагато надійніше, ніж фіксований sleep. Фіксована пауза або замала у повільний день, або марнує час у швидкий. Оберіть елемент, який з'являється лише після завантаження даних.
Крок 4: обробляйте нескінченну прокрутку і пагінацію
Деякі сторінки завантажують більше елементів при прокрутці. Прокрутіть донизу, почекайте, поки кількість елементів збільшиться, і зупиніться, коли вона перестане рости. Сторінки з номерованою пагінацією простіші: знайдіть посилання на наступну сторінку, перейдіть за ним і зупиніться на останній. Завжди встановлюйте максимальну кількість сторінок або прокруток, щоб помилка не змусила краулер працювати вічно.
def scroll_until_done(page, selector: str, max_rounds: int = 20) -> int:
previous = -1
for _ in range(max_rounds):
count = page.locator(selector).count()
if count == previous:
break
previous = count
page.mouse.wheel(0, 4000)
page.wait_for_timeout(1500) # даємо час для завантаження наступної порції
return previous
Крок 5: економте ресурси браузера
Рендеринг дорогий. Блокуйте зображення, шрифти і медіа, які вам не потрібні, і повторно використовуйте один браузер для багатьох сторінок, а не запускайте новий кожного разу. Закривайте сторінки і контексти, коли закінчили, щоб пам'ять не росла протягом довгого запуску.
def block_heavy_assets(route):
if route.request.resource_type in ("image", "font", "media"):
route.abort()
else:
route.continue_()
page.route("**/*", block_heavy_assets)
Крок 6: коли відрендерена сторінка показує капчу
Іноді відрендерена сторінка показує капчу до того, як з'явиться будь-який вміст. Якщо ця капча - PopularCaptcha, жива капча в Scarnox, ви зчитуєте sitekey зі сторінки, запитуєте токен і розміщуєте токен у полі форми, якого очікує сайт. Посібник по PopularCaptcha для Python проходить цей крок. Інші віджети ще не працюють, тож перевірте типи капчі пояснено, перш ніж планувати що-небудь навколо них.
Будьте уважні до самого браузера. Headless-браузер, який явно виглядає автоматизованим, може викликати більше перевірок, а не менше. Прочитайте виявлення headless-браузера, щоб знати сигнали, за якими слід стежити, і віддавайте перевагу повільнішим, рідшим завантаженням сторінок.
Скрапінг відповідально
- Перевірте
robots.txtі умови сайту, перш ніж рендерити його сторінки або викликати його ендпоінти. - Тримайте обмеження частоти низькими і кешуйте результати, щоб завантажувати кожну сторінку один раз.
- Не скрапіть персональні дані без законної підстави.
- Якщо сайт обмежує доступ, зупиніться. Не намагайтеся обійти обмеження.
Що далі
- Скрапінг без блокувань
- Headless-браузер виявлено
- Скрапінг сайтів, захищених Cloudflare
- Обробка капчі в Playwright і Puppeteer
Почніть з безкоштовного пробного кредиту, картка не потрібна: створіть обліковий запис і протестуйте потік PopularCaptcha на сторінці, яка належить вам. Актуальні тарифи дивіться на сторінці тарифів.