Scrapuj bez blokad: praktyczny przewodnik z kodem
Blokada podczas scrapingu zwykle oznacza jedną z trzech rzeczy: wysłałeś zbyt wiele żądań zbyt szybko, twój klient wyglądał zupełnie nie jak przeglądarka albo znany bot, albo twój adres IP znajduje się na liście, której strona ufa mniej. Wszystkie trzy możesz ograniczyć kilkoma nawykami: zauważaj blokadę wcześnie, zwalniaj, gdy strona cofa cię, zachowuj spójną i uczciwą tożsamość i używaj proxy tylko tam, gdzie jest to właściwe. Ten przewodnik omawia każdy z nich z działającym kodem. Stosuj go na stronach, które wolno ci scrapować.
Rozpoznaj blokadę
Blokada nie zawsze wygląda jak strona z błędem. Obserwuj:
- 403 Forbidden, co zwykle oznacza, że strona odrzuciła twojego klienta. Zatrzymaj się i sprawdź, czy w ogóle wolno ci tam być, zanim spróbujesz ponownie.
- 429 Too Many Requests, co oznacza, że wysyłasz za dużo. Często towarzyszy temu nagłówek
Retry-After. - 503 Service Unavailable, co może oznaczać przeciążenie strony. Cofnij się.
- Odpowiedź 200 z prawie pustą treścią, przekierowanie na stronę logowania lub wyzwania, albo strona bez oczekiwanych danych.
Zapisuj kod statusu i krótką próbkę treści dla każdego błędu. Blokadę, której nie widzisz w logach, nie da się naprawić.
Cofaj się z ponowieniami
Gdy strona mówi „zwolnij”, twój kod powinien zwolnić wyraźnie. Standardowym podejściem jest wykładniczy backoff z limitem prób. Uwzględnia też Retry-After, gdy strona go wysyła:
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 # wciąż zablokowany: zwróć ostatnią odpowiedź do obsługi przez wywołującego
resp = polite_get("https://example.com/products?page=1")
print(resp.status_code, len(resp.text))
Zwróć uwagę, czego ta funkcja nie robi. Nie ponawia 403 i nie rotuje tożsamości, żeby obejść jedną blokadę. 403 to decyzja, by przestać, a nie zagadka do rozwiązania.
Diagnozuj blokadę krok po kroku
Gdy przebieg zaczyna zawodzić, przejdź przez te sprawdzenia, zanim cokolwiek zmienisz:
- Spójrz na pierwszy nieudany adres URL i jego kod statusu. Blokada zwykle zaczyna się w konkretnym punkcie, a nie losowo.
- Porównaj tempo żądań w minutach przed pierwszym błędem. Seria to najczęstsza przyczyna.
- Sprawdź, czy błąd jest przypisany do jednej ścieżki, jednej sekcji lub jednej pory dnia.
- Pobierz ten sam adres raz ręcznie w zwykłej przeglądarce. Jeśli przeglądarka go załaduje, różnica leży we wzorcu twoich żądań. Jeśli przeglądarka też jest zablokowana, strona na razie przestała serwować ci tę stronę.
- Dopiero potem zmieniaj kod. Najpierw zwolnij, po drugie dodaj backoff, a tożsamość przeglądaj na końcu.
Zmiana kilku rzeczy naraz uniemożliwia ustalenie, co pomogło.
Zachowaj spójną, uczciwą tożsamość
Scrapery są blokowane, gdy ich tożsamość zmienia się między żądaniami albo gdy nie pasuje do reszty ruchu. Pojedyncza sesja z jednym, wyraźnym User-Agentem to najprostsza stabilna tożsamość. Nazwij swojego bota, dodaj link kontaktowy i wysyłaj tylko nagłówki, które wysłałby zwykły klient. Zmienianie User-Agentów, żeby wyglądać jak wielu różnych odwiedzających, to forma unikania wykrycia i zwykle zwiększa prawdopodobieństwo blokady, a nie je zmniejsza.
Jeśli strona serwuje inną treść przeglądarkom niż zwykłym klientom HTTP, używaj prawdziwej przeglądarki dla tych stron i zachowaj ten sam limit tempa. To uprawniona droga, która utrzymuje sensowny wzorzec żądań.
Używaj proxy z rozwagą
Proxy zmienia adres, z którego pochodzi żądanie. To narzędzie do uprawnionych potrzeb, takich jak testowanie z konkretnego regionu na stronie, którą wolno ci testować, albo rozłożenie obciążenia na własną infrastrukturę. Nie jest sposobem na ucieczkę od limitu, który strona ci nałożyła. Format proxy to ten, którego używają typy zadań proxy:
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)
Wybór typu proxy to osobna kwestia, omówiona w proxy rezydencjalnych a datacenter i rotujących proxy do scrapingu.
Gdy blokada staje się captcha
Niektóre strony odpowiadają na problem z tempem captcha zamiast twardą blokadą. Rozsądna kolejność to: zatrzymaj się, zwolnij, sprawdź ponownie zasady, a dopiero potem rozważ rozwiązanie. Dla widgetów PopularCaptcha żywe API Scarnox zwraca token, który wysyłasz w polu h-captcha-response. Dla innych typów captcha sprawdź listę obsługiwanych na żywo, ponieważ reCAPTCHA, Cloudflare Turnstile, GeeTest, AWS WAF i Arkose są wkrótce. Szczegółowy przewodnik to captcha w web scrapingu.
Jeśli zaczynasz widzieć blokady na jakiejś stronie, przyczyną niemal zawsze jest wzorzec żądań. Unikanie captcha przy scrapingu omawia nawyki, które temu zapobiegają.
Scrapuj odpowiedzialnie
- Sprawdź
robots.txti regulamin strony, zanim zaczniesz, i stosuj się do ścieżek oraz opóźnień, o które proszą. - Zatrzymaj się, gdy dostaniesz 403 lub wyraźną wiadomość „nie scrapuj”. Nie próbuj dalej.
- Utrzymuj tempo na tyle niskie, by zwykły odwiedzający nie zauważył twojego ruchu.
- Nie zbieraj danych osobowych, których nie masz powodu przechowywać, i obchodź się z tymi, które zbierasz, zgodnie z zasadami prywatności, które cię obowiązują.
- Jeśli potrzebujesz znacznie większego wolumenu, niż strona jest w stanie wygodnie obsłużyć, poproś właściciela o feed lub API.