Скрапінг без блокувань: практичний посібник з кодом
Блокування під час скрапінгу зазвичай означає одне з трьох: ви надсилали забагато запитів занадто швидко, ваш клієнт виглядав зовсім не як браузер або як відомий бот, або ваша IP-адреса є в списку, якому сайт довіряє менше. Усі три можна зменшити кількома звичками: помічати блокування рано, сповільнюватися, коли сайт тисне у відповідь, тримати послідовну і чесну ідентичність і використовувати проксі лише там, де це доречно. Цей посібник розглядає кожен пункт з робочим кодом. Використовуйте його для сайтів, які вам дозволено скрапити.
Розпізнавайте блокування
Блокування не завжди виглядає як сторінка помилки. Стежте за такими ознаками:
- 403 Forbidden, що зазвичай означає, що сайт відмовив вашому клієнту. Зупиніться і перевірте, чи маєте ви право там бути, перш ніж пробувати знову.
- 429 Too Many Requests, що означає, що ви надсилаєте забагато. Зазвичай іде із заголовком
Retry-After. - 503 Service Unavailable, що може означати перевантаження сайту. Відступіть.
- Відповідь 200 майже без вмісту, перенаправлення на сторінку логіну або перевірки, або сторінка, якої бракує даних, які ви очікували.
Записуйте код статусу і короткий фрагмент тіла для кожної невдачі. Блокування, яке не видно у ваших логах, - це блокування, яке ви не можете виправити.
Відступайте з повторами
Коли сайт каже "сповільніться", ваш код має сповільнитися більш ніж трохи. Експоненційна затримка з обмеженням спроб - стандартний підхід. Вона також поважає Retry-After, коли сайт його надсилає:
import time
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "ExampleBot/1.0 (+https://example.com/bot)",
"Accept-Language": "en-US,en;q=0.9",
})
def polite_get(url: str, max_attempts: int = 4) -> requests.Response:
delay = 2.0
resp = None
for _ in range(max_attempts):
resp = session.get(url, timeout=30)
if resp.status_code not in (429, 503):
return resp
retry_after = resp.headers.get("Retry-After", "")
wait = float(retry_after) if retry_after.isdigit() else delay
time.sleep(wait)
delay *= 2
return resp # блокування триває: повертаємо останню відповідь, щоб її обробив виклик
resp = polite_get("https://example.com/products?page=1")
print(resp.status_code, len(resp.text))
Зверніть увагу, чого ця функція не робить. Вона не повторює 403 і не перемикає ідентичності, щоб обійти блокування. 403 - це рішення зупинитися, а не головоломка для розв'язання.
Діагностуйте блокування крок за кроком
Коли запуск починає падати, пройдіться по цих перевірках, перш ніж змінювати щось інше:
- Подивіться на першу URL, що впала, і її код статусу. Блокування зазвичай починається в конкретній точці, а не випадково.
- Порівняйте частоту запитів за хвилини до першої невдачі. Сплеск - найпоширеніша причина.
- Перевірте, чи невдача прив'язана до одного шляху, одного розділу або часу доби.
- Відкрийте ту саму URL один раз вручну у звичайному браузері. Якщо браузер її завантажує, різниця в шаблоні ваших запитів. Якщо браузер також заблокований, сайт тимчасово перестав віддавати сторінку вам.
- Лише після цього змінюйте код. Спочатку сповільніть, другим - додайте відступ, і лише останнім перегляньте свою ідентичність.
Якщо змінювати кілька речей одночасно, неможливо знати, що допомогло.
Тримайте послідовну і чесну ідентичність
Скраперів блокують, коли їхня ідентичність змінюється між запитами або не відповідає рештою трафіку. Одна сесія з одним зрозумілим User-Agent - це найпростіша стабільна ідентичність. Назвіть свого бота, додайте контактне посилання і надсилайте лише ті заголовки, які надіслав би звичайний клієнт. Зміна User-Agent, щоб виглядати як багато різних відвідувачів, - це форма обходу, і вона скоріше підвищує ймовірність блокування, ніж знижує її.
Якщо сайт віддає різний вміст браузерам і простим HTTP-клієнтам, використовуйте справжній браузер для цих сторінок і дотримуйтеся тієї самої межі частоти. Це легітимний шлях, і він тримає шаблон запитів розумним.
Використовуйте проксі обережно
Проксі змінює адресу, з якої надходить запит. Це інструмент для легітимних потреб, наприклад тестування з конкретного регіону на сайті, який ви маєте право тестувати, або розподілу навантаження на вашу власну інфраструктуру. Це не спосіб втекти від ліміту, який сайт встановив для вас. Формат проксі - той, що використовують типи завдань з проксі:
proxies = {
"http": "http://user:pass@proxy.example.net:8000",
"https": "http://user:pass@proxy.example.net:8000",
}
resp = session.get("https://example.com/products", proxies=proxies, timeout=30)
Вибір типу проксі - окреме питання, яке розглядається в резидентних та дата-центрових проксі і ротаційних проксі для скрапінгу.
Коли блокування стає капчею
Деякі сайти відповідають на проблему частоти капчею, а не жорстким блокуванням. Розумний порядок такий: зупиніться, сповільніться, ще раз перевірте правила і лише тоді розглядайте розв'язання. Для віджетів PopularCaptcha жива API Scarnox повертає токен, який ви відправляєте в поле h-captcha-response. Для інших типів капчі перевіряйте список живих типів, адже підтримка reCAPTCHA, Cloudflare Turnstile, GeeTest, AWS WAF та Arkose скоро з'явиться. Детальний посібник - скрапінг з капчею.
Якщо ви починаєте бачити блокування на сайті, причина майже завжди в шаблоні запитів. Уникнення капчі під час скрапінгу описує звички, які цьому запобігають.
Скрапінг відповідально
- Перевірте
robots.txtі умови сайту перед початком, і дотримуйтеся шляхів та затримок, які вони просять. - Зупиніться, коли отримаєте 403 або чітке повідомлення "не скрапити". Не продовжуйте спроби.
- Тримайте частоту запитів достатньо низькою, щоб звичайний відвідувач не помітив вашого трафіку.
- Не збирайте персональні дані, які вам не потрібні, і поводьтеся з тими, що зібрали, відповідно до правил приватності, що на вас поширюються.
- Якщо вам потрібен обсяг, значно більший за той, який сайт комфортно обслуговує, попросіть у власника фід або API.