Hinweis: Dieser Beitrag behandelt das Sammeln öffentlich zugänglicher Daten. Die unbefugte automatisierte Abfrage nicht-öffentlicher Systeme kann gegen den Computer Fraud and Abuse Act (CFAA) in den USA oder gegen die DSGVO in der EU verstoßen. Prüfen Sie robots.txt, Nutzungsbedingungen und geltendes Recht, bevor Sie crawlen, und ziehen Sie offizielle APIs vor, wenn verfügbar.
Wer mit Crawlee für Python in Produktion crawlt, trifft früher oder später auf dieselbe Wand: Ein Ziel blockiert die Anfragen nach wenigen Hundert Requests. Die Ursache ist selten der Code, sondern die IP-Ebene. Proxy-Rotation in Crawlee für Python löst dieses Problem, wenn sie framework-idiomatisch über ProxyConfiguration und den SessionPool integriert wird — nicht als nachträgliches Hack. Dieser Guide zeigt, wie Sie Residential Proxies über das ProxyHat-Gateway an Crawlee anbinden, Sessions an IPs binden und Blocks sauber retiren.
Proxy-Rotation in Crawlee für Python: Was dahinter steckt
Crawlee für Python ist das offizielle Scraping-Framework von Apify und bietet zwei Crawler-Basisklassen: BeautifulSoupCrawler für reine HTTP-Anfragen und PlaywrightCrawler für Headless-Browser. Beide speisen sich aus einer einheitlichen Request-Queue und nutzen einen AutoscaledPool, der Concurrency basierend auf CPU- und Speicherauslastung automatisch skaliert. Darüber liegt der SessionPool, der Cookies, Fingerprints und — wenn konfiguriert — Proxy-IPs an eine logische Session bindet.
Die zentrale Erkenntnis: Crawlee behandelt Proxies nicht als einfache URL-Liste, sondern als Session-Ressource. Eine Session ist ein Bündel aus IP, Cookies und Identität. Wenn eine Seite Sie blockiert, müssen Sie die gesamte Session verwerfen, nicht nur die IP wechseln. Genau hier setzt ProxyConfiguration an.
Architektur: Request Queue, Autoscaling und SessionPool
Bevor wir Proxies konfigurieren, müssen wir verstehen, wie Crawlee eine Anfrage verarbeitet:
- Request Queue: Eine deduplizierte, persistente Warteschlange. Jeder Request hat einen eindeutigen
uniqueKey, sodass dieselbe URL nicht zweimal geladen wird (außer bei explizitem Retry). - AutoscaledPool: Verwaltet die Anzahl gleichzeitiger Requests. Erhöht die Concurrency, wenn CPU unter 70 % bleibt und Speicher unter Schwellenwert, drosselt bei Überlast. Typische Werte: 10–100 gleichzeitige Requests je nach Ziel-Toleranz.
- SessionPool: Hält einen Pool von
Session-Objekten. Jede Session trackt Cookies, einensession_id, eine zugehörige Proxy-URL und einen Block-Status. Der Pool recyclet blockierte Sessions und gibt neue aus.
Die offizielle Crawlee-Dokumentation beschreibt diese Schichten im Detail. Für uns ist wichtig: Wenn Sie ProxyConfiguration an den SessionPool koppeln, generiert Crawlee pro Session eine eigene Proxy-URL und hält diese stabil, bis die Session retired wird.
ProxyConfiguration: Die idiomatiche Schnittstelle
Die Klasse ProxyConfiguration ist der framework-idiomatische Weg, Proxies in Crawlee zu verwalten. Sie implementiert die Methode new_url(), die eine Proxy-URL zurückgibt. Der entscheidende Parameter ist session_id:
- Ohne
session_id:new_url()liefert bei jedem Aufruf eine neue rotierende Proxy-URL. Das ist Round-Robin-Rotation — nützlich für massenhafte, statuslose Anfragen. - Mit
session_id:new_url(session_id="abc123")liefert für dieselbe Session-ID immer dieselbe Proxy-URL zurück. Das ist sticky rotation — die IP bleibt stabil, solange die Session lebt. Essential für Login-Flows, Cart-Sessions oder Seiten, die IP-Stabilität prüfen.
Dieses Muster ist kein Workaround — es ist exakt die Erweiterungspunkt-Architektur, die Crawlee vorsieht. Der SessionPool ruft intern proxy_configuration.new_url(session_id=session.id) auf, sodass jede Session ihre eigene IP erhält.
Round-Robin vs. Session-gebundene Rotation
| Strategie | Methode | IP-Verhalten | Ideal für | Nachteil |
|---|---|---|---|---|
| Round-Robin | new_url() ohne Session | Neue IP pro Request | Massen-Scraping statusloser Seiten | Cookies/Fingerprints verlieren Gültigkeit |
| Sticky Session | new_url(session_id=...) | Stabile IP pro Session | Logins, SERP-Tracking, Paginierung | Block wirkt auf gesamte Session |
| Tiered Fallback | Middleware mit Retire | Wechsel bei Block | Geschützte Ziele (Cloudflare, DataDome) | Höhere Proxy-Kosten |
Warum Residential Proxies Datacenter-IPs schlagen
Cloudflare, DataDome und PerimeterX klassifizieren IPs anhand ihrer ASN. Datacenter-IPs stammen aus AWS, Hetzner, OVH und ähnlichen Rechenzentren — diese ASN-Ranges sind öffentlich und werden von Anti-Bot-Systemen fast immer als „verdächtig" eingestuft. Residential Proxies stammen von echten ISPs (Deutsche Telekom, Comcast, Vodafone) und haben eine legitime ASN-Zugehörigkeit. Die Block-Rate sinkt drastisch: Während Datacenter-IPs bei Cloudflare-geschützten Zielen oft nach 20–50 Requests blockiert werden, können Residential Proxies Tausende Anfragen pro IP überstehen, wenn das Verhaltens-Muster natürlich bleibt.
Mobile Proxies (4G/5G) sind noch resistenter, weil große Carrier wie T-Mobile Millionen Geräte hinter wenigen NAT-IPs betreiben — eine Blockade einer einzelnen mobilen IP würde Tausende legitime Nutzer treffen. Für die meisten Crawling-Aufgaben sind Residential Proxies jedoch der beste Kompromiss aus Kosten und Erfolgsquote.
Daumenregel: Datacenter für ungeschützte, einfache Ziele. Residential für alles mit WAF/Bot-Detection. Mobile für die härtesten Ziele oder wenn Sie IP-Stabilität über Stunden brauchen.
ProxyHat an Crawlee anbinden: Runnable Beispiel
ProxyHat stellt ein Gateway unter gate.proxyhat.com bereit. Geo-Targeting und Session-Pinning werden über den Username kodiert. Das ist ideal für Crawlees ProxyConfiguration, weil wir die Session-ID direkt in den Username einbauen können.
Beispiel 1: BeautifulSoupCrawler mit session-gebundenen Residential Proxies
import asyncio
from crawlee.beautifulsoup_crawler import BeautifulSoupCrawler
from crawlee.proxy_configuration import ProxyConfiguration
PROXYHAT_USER = "mein_user"
PROXYHAT_PASS = "mein_pass"
async def main():
# ProxyConfiguration, die pro session_id eine stabile Proxy-URL erzeugt.
# Der Username enthält -session-{id}, sodass ProxyHat dieselbe IP liefert.
proxy_config = ProxyConfiguration(
proxy_urls=[
f"http://{PROXYHAT_USER}-country-US-session-abc123:"
f"{PROXYHAT_PASS}@gate.proxyhat.com:8080"
],
)
crawler = BeautifulSoupCrawler(
proxy_configuration=proxy_config,
max_request_retries=3,
max_requests_per_crawl=500,
max_concurrency=10,
)
@crawler.router.default_handler
async def handler(request, request_context):
session = request_context.session
response = await request_context.http_client.send(
request.as_http_request(),
session=session,
)
html = response.read().decode()
title = response.html.css_first("title").text if response.html.css_first("title") else ""
print(f"[{session.id}] {request.url} -> {title[:60]}")
await crawler.run(["https://example.com/page-1"])
if __name__ == "__main__":
asyncio.run(main())
Beispiel 2: Dynamische Session-IDs über eine Custom ProxyConfiguration
Wenn Sie pro Session eine eigene ProxyHat-Session generieren wollen, überschreiben Sie new_url in einer Unterklasse:
import hashlib
from crawlee.proxy_configuration import ProxyConfiguration
class ProxyHatProxyConfiguration(ProxyConfiguration):
def __init__(self, base_user: str, password: str, country: str = "US"):
self.base_user = base_user
self.password = password
self.country = country
super().__init__(proxy_urls=[])
async def new_url(self, *, session_id: str | None = None) -> str:
sid = session_id or "default"
# Kurze, stabile ID aus der Session-ID ableiten
token = hashlib.md5(sid.encode()).hexdigest()[:12]
username = f"{self.base_user}-country-{self.country}-session-{token}"
return f"http://{username}:{self.password}@gate.proxyhat.com:8080"
Diese Klasse übergeben Sie an jeden Crawler als proxy_configuration. Crawlee ruft new_url(session_id=session.id) automatisch auf — Sie müssen sich um nichts weiter kümmern.
Beispiel 3: SOCKS5 für latenzkritische Szenarien
proxy_config = ProxyConfiguration(
proxy_urls=[
f"socks5://{PROXYHAT_USER}-country-DE-session-xyz:"
f"{PROXYHAT_PASS}@gate.proxyhat.com:1080"
],
)
SOCKS5 auf Port 1080 ist nützlich, wenn HTTP-Header-Manipulation durch den Proxy vermieden werden soll oder wenn Sie WebSocket-Verbindungen tunneln.
Produktionsmuster: Blocks erkennen, Sessions retiren, Retries steuern
Session.retire() bei Blocks
Wenn eine Seite einen Block signalisiert (Status 403, CAPTCHA-Seite, leere Body), müssen Sie die Session verwerfen, damit der SessionPool eine neue IP und neue Cookies ausgibt:
@crawler.router.default_handler
async def handler(request, request_context):
session = request_context.session
response = await request_context.http_client.send(
request.as_http_request(), session=session
)
status = response.status_code
if status == 403 or "captcha" in response.read().decode().lower():
# Session verwerfen -> neue IP bei nächstem Request
session.retire()
raise RuntimeError(f"Blocked on {request.url}, retiring session {session.id}")
# Normale Verarbeitung ...
await request_context.enqueue_links()
session.retire() markiert die Session als blockiert. Der Pool gibt sie nicht wieder aus und erstellt eine neue. Da ProxyConfiguration.new_url(session_id=neue_id) aufgerufen wird, erhalten Sie automatisch eine neue IP.
max_request_retries und Fehlerbehandlung
Setzen Sie max_request_retries auf 3–5. Jeder Retry läuft mit einer frischen Session, wenn die vorherige retired wurde. Kombinieren Sie das mit max_session_rotations im SessionPool, um Endlosschleifen bei dauerhaft blockierten Zielen zu vermeiden:
crawler = BeautifulSoupCrawler(
proxy_configuration=proxy_config,
max_request_retries=4,
max_concurrency=20,
session_pool=SessionPool(
max_pool_size=50,
max_session_age=timedelta(hours=1),
),
)
Concurrency über den AutoscaledPool
Crawlee skaliert Concurrency automatisch. Für Residential Proxies empfiehlt sich ein bewusstes Limit, weil zu viele gleichzeitige Anfragen von verschiedenen IPs auf dasselbe Ziel verdächtig wirken können. Starten Sie mit max_concurrency=10 und beobachten Sie die Block-Rate. Ein guter Zielwert ist eine Erfolgsquote von über 95 % bei einer durchschnittlichen Latenz unter 800 ms.
Tiered Proxy-Fallback als Middleware-Pattern
Für stark geschützte Ziele lohnt sich ein gestaffeltes Fallback: Residential als erste Wahl, Mobile als Eskalation bei wiederholten Blocks. Sie können das als Custom-Middleware implementieren, die den Proxy-Typ anhand der Retry-Nummer wählt:
class TieredProxyConfiguration(ProxyConfiguration):
def __init__(self, user, pw):
self.user = user
self.pw = pw
self._retry_counts: dict[str, int] = {}
async def new_url(self, *, session_id=None):
sid = session_id or "default"
retries = self._retry_counts.get(sid, 0)
tier = "mobile" if retries >= 2 else "residential"
# ProxyHat unterstützt verschiedene Endpunkte je nach Tarif
return f"http://{self.user}-{tier}-session-{sid}:{self.pw}@gate.proxyhat.com:8080"
Wann Sie NICHT den Browser crawlen sollten
PlaywrightCrawler ist mächtig, aber teuer: Eine Headless-Browser-Session verbraucht 150–300 MB RAM pro Tab und ist 5–10× langsamer als eine reine HTTP-Anfrage. Greifen Sie nur dann zum Browser, wenn:
- Die Seite JavaScript zum Rendern von Inhalten benötigt (SPA, React/Vue).
- Ein Bot-Detection-System Browser-Fingerprints prüft und reine HTTP-Anfragen sofort blockiert.
- Sie Interaktionen wie Klicks, Scrolls oder Form-Submits ausführen müssen.
In allen anderen Fällen ist BeautifulSoupCrawler mit Residential Proxies die bessere Wahl — schneller, ressourcenschonender und einfacher zu skalieren. Auf einem einzelnen 4-Core-Server mit 16 GB RAM können Sie 50–100 gleichzeitige HTTP-Requests laufen, während Playwright bei 8–15 Tabs pro Maschine an seine Grenzen kommt.
Skalierung: Container, Headless Fleet, verteilte Queues
Für große Crawler-Jobs empfiehlt sich Containerisierung mit Docker. Crawlee unterstützt persistente Storage-Backends (SQLite lokal, oder Redis für verteilte Setups). Wenn Sie mehrere Worker-Container betreiben, verwenden Sie einen Redis-basierten RequestQueue- und KeyValueStore-Adapter, damit alle Worker dieselbe Queue teilen. Jeder Worker erhält seinen eigenen SessionPool mit eigenen ProxyHat-Sessions — Konflikte entstehen nicht, weil Session-IDs eindeutig sind.
Bei 100 gleichzeitigen Sessions und einer durchschnittlichen Lebensdauer von 30 Minuten pro Session benötigen Sie etwa 4.800 Sessions pro Tag. Residential Proxy-Traffic bei ProxyHat wird nach GB abgerechnet — planen Sie je nach Seitengröße 0,5–2 MB pro Request ein. Details finden Sie auf der ProxyHat-Preisseite.
Ethik und rechtliche Rahmenbedingungen
Proxy-Rotation ist eine technische Fähigkeit, kein Freifahrtschein. Beachten Sie:
- robots.txt: Crawlee respektiert
robots.txtstandardmäßig. Deaktivieren Sie das nur mit gutem Grund. - Nutzungsbedingungen: Viele Seiten verbieten automatisierte Abfrage in ihren ToS. Ein Verstoß kann zivilrechtliche Folgen haben.
- Rate Limits: Selbst mit Proxies sollten Sie die Server-Infrastruktur des Ziels nicht überlasten. Eine höfliche Rate ist 1 Request pro 2–5 Sekunden pro Ziel-Domain.
- Offizielle APIs: Wenn eine Seite eine API anbietet, verwenden Sie diese. Sie ist stabiler, legal sicherer und oft kostenlos.
- Personenbezogene Daten: Das Sammeln personenbezogener Daten fällt unter DSGVO/EU-GDPR. Vermeiden Sie es, außer Sie haben eine Rechtsgrundlage.
Weitere Hintergrundinformationen zu ethischen Scraping-Praktiken finden Sie in der ProxyHat-Dokumentation und in den ProxyHat Use-Cases für Web-Scraping.
Key Takeaways
- ProxyConfiguration ist die idiomatiche Schnittstelle — verwenden Sie
new_url(session_id=...)für Sticky Sessions, nicht manuelle URL-Listen.- SessionPool + ProxyConfiguration = automatische IP-Bindung — Crawlee koppelt Sessions und Proxies von Haus aus.
- Residential schlägt Datacenter bei Cloudflare/DataDome — die ASN macht den Unterschied, nicht die Geschwindigkeit.
- session.retire() bei Blocks — verwirft IP und Cookies gemeinsam, was genau das ist, was Anti-Bot-Systeme erwarten.
- BeautifulSoup vor Playwright — nur bei JS-rendering oder Bot-Detection-Fingerprints zum Browser greifen.
- Ethik beachten — robots.txt, ToS, Rate Limits und offizielle APIs haben Vorrang vor technischer Machbarkeit.
Fazit
Proxy-Rotation in Crawlee für Python ist kein nachträgliches Plugin, sondern eine integrierte Architekturschicht. Wenn Sie ProxyConfiguration mit session_id-basierten Sticky Sessions verwenden, Residential Proxies über gate.proxyhat.com:8080 anbinden und session.retire() bei Blocks konsistent einsetzen, erhalten Sie einen Crawler, der skaliert, ohne ständig manuell nachgeschärft werden zu müssen. Kombinieren Sie das mit bewusster Concurrency, vernünftigen Retries und ethischen Rate-Limits, haben Sie eine produktionsreife Pipeline. Weitere Details zu Proxy-Standorten finden Sie auf der ProxyHat-Locations-Seite, und für SERP-spezifische Muster siehe SERP-Tracking mit ProxyHat.






