Rotacja proxy w Crawlee dla Pythona: Kompletny przewodnik produkcyjny

Praktyczny przewodnik po konfiguracji proxy w Crawlee dla Pythona — od ProxyConfiguration i SessionPool po rotację IP, sesje sticky i strategie fallback przy blokadach Cloudflare i DataDome.

Proxy Rotation in Crawlee for Python: A Developer's Guide to Residential IPs

Rotacja proxy w Crawlee dla Pythona — od pierwszego żądania do produkcji

Jeśli budujesz krawlery w Pythonie i korzystasz z Crawlee dla Pythona, zarządzanie proxy to jedna z pierwszych decyzji architektonicznych. Rotacja proxy w Crawlee dla Pythona nie polega tylko na podmianie adresu IP przy każdym żądaniu — to ekosystem sesji, kolejek żądań, autoskalowania i middleware'u, który decyduje, czy Twój crawler przetrwa 500 żądań, czy 500 000.

Crawlee (framework open-source od Apify) oferuje wbudowane klasy ProxyConfiguration oraz SessionPool, które pozwalają powiązać adres IP z ciasteczkami i fingerprintem przeglądarki. Połączenie tego z crawlee python proxy opartym o adresy rezydencjalne — takimi jak te z ProxyHat — daje realistyczny ruch, który znacznie rzadziej trafia na ściany CAPTCHA. W tym artykule przejdziemy przez architekturę, implementację i pułapki produkcyjne.

Uwaga prawna: Skrapowanie danych publicznych jest legalne w wielu jurysdykcjach, ale CFAA w USA i RODO/GDPR w UE mogą ograniczać, co wolno zbierać i jak przetwarzać. Zawsze sprawdzaj robots.txt, regulaminy serwisów i preferuj oficjalne API tam, gdzie to możliwe.

Architektura Crawlee: kolejki żądań, crawlery i SessionPool

Crawlee dla Pythona oferuje dwie główne klasy crawlerów:

  • BeautifulSoupCrawler — lekki, oparty na httpx, idealny do stron statycznych i prostych API.
  • PlaywrightCrawler — pełen silnik przeglądarki dla stron renderowanych przez JavaScript, obsługujących logowanie i zabezpieczenia anti-bot.

Obydwa crawlery korzystają z tego samego RequestQueue (kolejki żądań) i RequestManager, co oznacza, że przełączanie backendów nie wymaga przepisywania logiki parsowania. Autoskalowanie jest wbudowane — ConcurrencySettings automatycznie zwiększa lub zmniejsza liczbę równoległych żądań na podstawie czasu odpowiedzi i wskaźnika sukcesu.

Kluczowym elementem jest crawlee session pool — pula sesji, gdzie każda sesja przechowuje własne ciasteczka, fingerprint i — jeśli skonfigurujesz to poprawnie — własny adres IP. Gdy sesja zostaje zablokowana, Crawlee może ją retire() (wycofać), co powoduje utworzenie nowej sesji z nowym proxy. To właśnie ta integracja sprawia, że crawlee proxy rotation działa idiomatycznie, a nie jako hack.

Jak SessionPool współpracuje z ProxyConfiguration

Architektura wygląda następująco:

  1. ProxyConfiguration generuje URL-e proxy dla każdej sesji.
  2. SessionPool tworzy sesje i przypisuje im proxy z konfiguracji.
  3. Crawler pobiera sesję z puli dla każdego żądania.
  4. Jeśli sesja zostaje zablokowana, session.retire() usuwa ją i nowa sesja otrzymuje nowy proxy URL.

Dzięki temu adres IP, ciasteczka i fingerprint są spójne w ramach jednej sesji, co jest krytyczne dla stron z systemami anti-bot takimi jak Cloudflare czy DataDome.

ProxyConfiguration: idiomatyczna rotacja proxy w Crawlee

Klasa ProxyConfiguration to centralny punkt zarządzania proxy w Crawlee. Oferuje dwa główne tryby:

TrybMetodaZachowanieNajlepsze zastosowanie
Round-robinproxy_configuration.new_url()Nowy proxy przy każdym wywołaniuSkrapowanie masowe bez stanu
Sticky sessionproxy_configuration.new_url(session_id="abc123")Ten sam proxy dla tej samej sesjiLogowanie, koszyki, strony z sesją
Geo-targetednew_url() z flagami w usernameProxy z określonego kraju/miastaSERP, lokalne wyniki, e-commerce

Round-robin vs sticky session — co wybrać?

Round-robin jest prosty: każde żądanie otrzymuje nowy adres IP. To dobrze dla masowego skrapowania stron bez stanu (np. publiczne katalogi produktów). Problem pojawia się, gdy strona wymaga sesji — logowanie, koszyk, paginacja z tokenami CSRF. Wtedy zmiana IP przy każdym żądaniu wygląda jak przejęcie konta i wyzwala blokady.

Sticky session rozwiązuje ten problem: new_url(session_id="abc123") zawsze zwraca ten sam proxy URL dla tej samej sesji. Crawlee automatycznie powiąże ten URL z obiektem sesji, więc ciasteczka i IP są spójne przez cały cykl życia sesji.

Dlaczego adresy rezydencjalne biją datacenter na Cloudflare i DataDome

Systemy anti-bot takie jak Cloudflare i DataDome klasyfikują ruch na podstawie reputacji IP. Adresy datacenter (np. AWS, DigitalOcean, Hetzner) są łatwo rozpoznawalne — należą do znanych bloków ASN przypisanych do dostawców chmurowych. Wystarczy, że DataDome sprawdzi ASN w bazie MaxMind GeoIP, by oznaczyć takie IP jako podejrzane.

Adresy rezydencjalne pochodzą od prawdziwych dostawców ISP (np. Comcast, Deutsche Telekom, Orange) i mają reputację zwykłego użytkownika domowego. To drastycznie zmniejsza prawdopodobieństwo natychmiastowej blokady. W testach produkcyjnych adresy rezydencjalne osiągają wskaźnik sukcesu 85–95% na stronach z DataDome, podczas gdy datacenter często nie przekracza 30%.

Strategia tiered proxy — fallback przy blokadach

Zamiast polegać na jednym typie proxy, warto wdrożyć strategię warstwową:

  1. Tier 1 (rezydencjalne): Domyślny ruch na stronach z anti-bot.
  2. Tier 2 (mobilne): Fallback dla najtrudniejszych celów (mobilne IP mają jeszcze wyższą reputację).
  3. Tier 3 (datacenter): Tylko dla prostych stron bez anti-bot, gdzie liczy się szybkość i koszt.

W Crawlee możesz zaimplementować to jako middleware, który dynamicznie wybiera ProxyConfiguration na podstawie domeny lub liczby ponownych prób.

Przykład: BeautifulSoupCrawler z ProxyConfiguration i ProxyHat

Oto kompletny, uruchamialny przykład. Używamy BeautifulSoupCrawler z ProxyConfiguration skierowanym na ProxyHat z flagami -country-US i -session-abc123 w username.

import asyncio
from crawlee import BeautifulSoupCrawler, ProxyConfiguration
from crawlee.sessions import SessionPool

# ProxyHat: generujemy URL proxy z flagami geo i session w username
def proxyhat_url(country: str = "US", session_id: str | None = None) -> str:
    username = f"user-country-{country}"
    if session_id:
        username += f"-session-{session_id}"
    return f"http://{username}:pass@gate.proxyhat.com:8080"

# ProxyConfiguration z listą URL-i rezydencjalnych
proxy_config = ProxyConfiguration(
    proxy_urls=[
        proxyhat_url("US", "sess-001"),
        proxyhat_url("US", "sess-002"),
        proxyhat_url("US", "sess-003"),
        proxyhat_url("DE", "sess-004"),
    ]
)

async def main():
    crawler = BeautifulSoupCrawler(
        proxy_configuration=proxy_config,
        max_request_retries=3,
        request_handler_timeout=timedelta(seconds=30),
    )

    @crawler.router.default_handler
    async def handler(request_context):
        ctx = request_context
        response = await ctx.http_client.get(str(ctx.request.url))
        title = response.soup.find("title")
        print(f"[{ctx.session.id}] {ctx.request.url} -> {title.text if title else 'N/A'}")

        # Jeśli widzimy CAPTCHA, wycofujemy sesję
        if "captcha" in response.text.lower():
            ctx.session.retire()

    await crawler.run(["https://example.com"])

asyncio.run(main())

W tym przykładzie każdy URL proxy zawiera unikalny session_id w username. Crawlee automatycznie powiąże te URL-e z sesjami z SessionPool, więc każda sesja ma stały adres IP przez cały cykl życia.

Dynamiczne generowanie sesji per-request

Jeśli potrzebujesz jeszcze większej elastyczności, możesz generować nazwy sesji dynamicznie w middleware:

import uuid
from crawlee import ProxyConfiguration

class DynamicProxyConfiguration(ProxyConfiguration):
    async def new_url(self, session_id: str | None = None) -> str:
        if session_id is None:
            session_id = f"dyn-{uuid.uuid4().hex[:8]}"
        username = f"user-country-US-session-{session_id}"
        return f"http://{username}:pass@gate.proxyhat.com:8080"

proxy_config = DynamicProxyConfiguration()

Ten wzorzec pozwala na rotację IP przy każdym żądaniu, gdy session_id nie jest przekazywany, lub utrzymanie sticky session, gdy jest. Sprawdź naszą stronę cennika, aby dobrać odpowiedni pakiet dla Twojego wolumenu żądań.

ProxyHat SDK: generowanie per-session usernames

Dla większych projektów warto zbudować prostą funkcję lub klasę, która generuje nazwy użytkowników proxy zgodnie z konwencją ProxyHat:

import hashlib
import time

class ProxyHatSessionManager:
    """Generuje nazwy użytkowników ProxyHat z flagami geo i session."""

    def __init__(self, base_user: str = "user", password: str = "pass"):
        self.base_user = base_user
        self.password = password

    def get_proxy_url(
        self,
        country: str | None = None,
        city: str | None = None,
        session_id: str | None = None,
        socks5: bool = False,
    ) -> str:
        parts = [self.base_user]
        if country:
            parts.append(f"country-{country}")
        if city:
            parts.append(f"city-{city}")
        if session_id is None:
            session_id = hashlib.md5(str(time.time()).encode()).hexdigest()[:8]
        parts.append(f"session-{session_id}")

        username = "-".join(parts)
        scheme = "socks5" if socks5 else "http"
        port = 1080 if socks5 else 8080
        return f"{scheme}://{username}:{self.password}@gate.proxyhat.com:{port}"

# Użycie
manager = ProxyHatSessionManager()
url = manager.get_proxy_url(country="US", city="newyork", session_id="order-123")
# -> http://user-country-US-city-newyork-session-order-123:pass@gate.proxyhat.com:8080

Takie podejście pozwala na precyzyjną kontrolę nad tym, które żądania współdzielą sesję (a tym samym IP), a które otrzymują nowe. Więcej informacji o dostępnych lokalizacjach znajdziesz na stronie lokalizacji ProxyHat.

Wzorce produkcyjne: session.retire(), retry i współbieżność

Wycofywanie sesji przy blokadach

Najważniejszy wzorzec produkcyjny to session.retire() przy wykryciu blokady. Crawlee automatycznie utworzy nową sesję z nowym proxy:

@crawler.router.default_handler
async def handler(request_context):
    ctx = request_context
    response = await ctx.http_client.get(str(ctx.request.url))

    # Wykrywanie blokad anti-bot
    content = response.text.lower()
    if any(signal in content for signal in ["captcha", "access denied", "blocked"]):
        ctx.session.retire()
        raise RuntimeError(f"Blocked on {ctx.request.url}")

    # Wykrywanie kodów HTTP wskazujących na blokadę
    if response.status_code in (403, 429):
        ctx.session.retire()
        raise RuntimeError(f"HTTP {response.status_code} on {ctx.request.url}")

    # Normalna logika parsowania
    await ctx.enqueue_links()

Gdy session.retire() jest wywołane, Crawlee oznacza sesję jako wygasłą. Nowe żądania otrzymają świeżą sesję z nowym proxy URL z ProxyConfiguration. Połączone z max_request_retries=3, crawler automatycznie ponowi próbę z nowym IP.

Autoskalowanie i współbieżność

Crawlee zarządza współbieżnością przez AutoscaledPool. Domyślnie zaczyna od 1–2 równoległych żądań i skaluje się w górę, dopóki wskaźnik sukcesu pozostaje wysoki, a czas odpowiedzi nie rośnie drastycznie. Dla ProxyHat zalecamy:

  • max_concurrency: 50–100 dla rezydencjalnych (adresy rezydencjalne mają wyższą latencję, ~200–500 ms).
  • max_request_retries: 3–5 (więcej nie ma sensu — jeśli IP jest zablokowane, kolejne próby z tym samym IP nie pomogą).
  • request_handler_timeout: 30–60 sekund (rezydencjalne proxy mogą być wolniejsze).

Konteneryzacja i headless fleet

W produkcji uruchamiaj wiele instancji crawlera w kontenerach Docker. Każda instancja powinna mieć własny SessionPool i unikalne prefiksy session_id, aby uniknąć kolizji. Kubernetes z HPA (Horizontal Pod Autoscaler) może skalować liczbę podów na podstawie długości kolejki żądań. Więcej o przypadkach użycia znajdziesz na stronie web scraping.

Kiedy NIE używać PlaywrightCrawler

PlaywrightCrawler jest potężny, ale kosztowny — jeden kontekst przeglądarki zużywa 150–300 MB RAM i uruchomienie trwa 2–5 sekund. Jeśli strona nie wymaga JavaScript do renderowania treści, zawsze używaj BeautifulSoupCrawler. Jest 10–50x szybszy i zużywa 10x mniej pamięci.

Signał, że potrzebujesz Playwright:

  • Treść ładuje się dynamicznie przez XHR/fetch po załadowaniu strony.
  • Strona wymaga wykonania JavaScript do generowania tokenów CSRF.
  • Musisz symulować interakcje użytkownika (kliknięcia, scroll, hover).
  • Anti-bot wymaga pełnego fingerprintu przeglądarki (canvas, WebGL, WebRTC).

Jeśli strona renderuje treść po stronie serwera, BeautifulSoupCrawler z rezydencjalnym proxy ProxyHat jest wystarczający i znacznie bardziej wydajny. Zobacz też nasz przypadek użycia SERP tracking.

Etyka i zgodność: public data only

Skrapowanie danych publicznych jest legalne, ale granica między „publiczne” a „chronione” może być subtelna. Podstawowe zasady:

  • Szanuj robots.txt — Crawlee automatycznie pobiera i sprawdza robots.txt, jeśli skonfigurujesz to poprawnie.
  • Przestrzegaj limitów tempa — nie wysyłaj więcej niż 1–2 żądań na sekundę na domenę, chyba że masz wyraźną zgodę.
  • Preferuj oficjalne API — jeśli strona oferuje API, użyj go zamiast skrapowania HTML.
  • Nie zbieraj danych osobowych bez zgodności z RODO/GDPR — adresy e-mail, imiona, numery telefonów mogą podlegać ochronie.
  • Nie omijaj płatnych barier — to może naruszać regulamin serwisu i w niektórych jurysdykcjach być nielegalne.

CFAA w USA kategoryzuje „nieautoryzowany dostęp” jako przestępstwo. Choć skrapowanie publicznych stron rzadko podpada pod CFAA (patrz orzeczenie hiQ Labs v. LinkedIn), lepiej konsultować się z prawnikiem przy skomplikowanych przypadkach.

Kluczowe wnioski (Key Takeaways)

  • ProxyConfiguration to idiomatyczny sposób na zarządzanie proxy w Crawlee — nie hackuj proxy w request_handler.
  • Sticky session (new_url z session_id) jest kluczowa dla stron z sesją; round-robin dla bezstanowych.
  • Adresy rezydencjalne ProxyHat osiągają 85–95% sukcesu na Cloudflare/DataDome vs 30% dla datacenter.
  • session.retire() przy blokadzie + max_request_retries = automatyczne odzyskiwanie z nowym IP.
  • BeautifulSoupCrawler > PlaywrightCrawler gdy nie potrzebujesz JS — 10–50x szybszy.
  • Etyka przede wszystkim: robots.txt, limity tempa, oficjalne API, RODO.

Często zadawane pytania

Czym jest ProxyConfiguration w Crawlee?

ProxyConfiguration to wbudowana klasa Crawlee, która zarządza URL-ami proxy i udostępnia metodę new_url(session_id=...) do pobierania proxy. W trybie round-robin zwraca nowy URL przy każdym wywołaniu; w trybie sticky session zwraca ten sam URL dla tego samego session_id. Klasa integruje się automatycznie z SessionPool, co powiązuje IP, ciasteczka i fingerprint.

Kiedy używać rotacji, a kiedy sesji sticky?

Rotację (round-robin) stosuj do skrapowania bezstanowego — np. katalogi produktów, publiczne listy. Sesje sticky są konieczne, gdy strona utrzymuje stan: logowanie, koszyk, paginacja z tokenami, wieloetapowe formularze. Sticky session zapobiega wyzwalaniu alertów anti-bot, które reagują na zmianę IP w trakcie sesji.

Jaki typ proxy wybrać do Crawlee?

Rezydencjalne dla stron z anti-bot (Cloudflare, DataDome, PerimeterX) — mają reputację zwykłego użytkownika ISP. Mobilne dla najtrudniejszych celów. Datacenter tylko dla prostych stron bez anti-bot, gdzie liczy się szybkość i niski koszt. ProxyHat oferuje wszystkie trzy typy z geo-targetingiem na poziomie kraju i miasta.

Jak unikać blokad w Crawlee?

Używaj session.retire() przy wykryciu blokady (kody 403/429, CAPTCHA, „access denied”), ustaw max_request_retries=3, ogranicz współbieżność do 50–100 dla proxy rezydencjalnych, i zawsze korzystaj z geo-targetingu dopasowanego do docelowej domeny. Sprawdzaj też robots.txt i przestrzegaj limitów tempa.

Czy Crawlee automatycznie zarządza proxy przy blokadach?

Tak — jeśli wycofasz sesję przez session.retire(), Crawlee utworzy nową sesję i pobierze nowy proxy URL z ProxyConfiguration. W połączeniu z max_request_retries crawler automatycznie ponowi żądanie z nowym IP, bez ręcznej logiki ponawiania.

Gotowy, aby zacząć?

Dostęp do ponad 50 mln rezydencjalnych IP w ponad 148 krajach z filtrowaniem AI.

Zobacz cenyProxy rezydencjalne
← Powrót do Bloga