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:
ProxyConfigurationgeneruje URL-e proxy dla każdej sesji.SessionPooltworzy sesje i przypisuje im proxy z konfiguracji.- Crawler pobiera sesję z puli dla każdego żądania.
- 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:
| Tryb | Metoda | Zachowanie | Najlepsze zastosowanie |
|---|---|---|---|
| Round-robin | proxy_configuration.new_url() | Nowy proxy przy każdym wywołaniu | Skrapowanie masowe bez stanu |
| Sticky session | proxy_configuration.new_url(session_id="abc123") | Ten sam proxy dla tej samej sesji | Logowanie, koszyki, strony z sesją |
| Geo-targeted | new_url() z flagami w username | Proxy z określonego kraju/miasta | SERP, 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ą:
- Tier 1 (rezydencjalne): Domyślny ruch na stronach z anti-bot.
- Tier 2 (mobilne): Fallback dla najtrudniejszych celów (mobilne IP mają jeszcze wyższą reputację).
- 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.






