Ihr Scrapy-Spider liefert auf 100 Testseiten starke Ergebnisse, bricht aber bei 10.000 Seiten ein. Das ist meist kein klassischer Scraping-Fehler, sondern ein Netzwerkproblem, das sich als Scraping-Bug tarnt. Die naheliegende Lösung ist ein Proxy, und einer in request.meta zu hinterlegen, dauert ungefähr 30 Sekunden.
Das Problem: Genau dort hören viele Grundlagen-Tutorials auf. Sie zeigen meta={"proxy": "http://IP:PORT"}, vielleicht noch eine Middleware-Klasse, und damit ist Schluss. Was dabei oft fehlt, sind Fragen wie: Was passiert, wenn der Proxy mitten im Crawl ausfällt? Wie verhindern Sie, dass Zugangsdaten im Git-Verlauf landen? Warum verwendet ein Retry womöglich wieder denselben fehlgeschlagenen Proxy? Genau um diese Produktionsfragen geht es in diesem Leitfaden: Fail-Closed-Konfiguration, Secret-Management, bewusste Proxy-Auswahl beim Retry, Protokollgrenzen und messbare Kosten-Nutzen-Abwägungen.
Was ist eine Proxy-Middleware in Scrapy?
Eine Proxy-Middleware ist Code, der in Scrapys Downloader-Pipeline sitzt und entscheidet, von welcher IP-Adresse eine Anfrage scheinbar kommt, bevor sie tatsächlich gesendet wird. Scrapy bringt dafür bereits eine eingebaute Lösung mit, HttpProxyMiddleware. Diese liest den Schlüssel proxy aus request.meta, hängt bei Bedarf Proxy-Authentifizierung an und übergibt die Anfrage an den Download-Handler, der die Verbindung wirklich öffnet. Eine benutzerdefinierte Proxy-Middleware ersetzt diesen Transport-Schritt nicht — sie ist ein Selektor, der vor der eingebauten Mechanik festlegt, welcher Proxy verwendet wird. Dieser Unterschied ist wichtiger, als er zunächst klingt, und er erklärt einen Großteil der Meldungen nach dem Muster „Meine Custom Middleware funktioniert nicht“.
Warum Proxies für produktive Scrapy-Projekte wichtig sind
Ein Proxy verändert den Netzwerkweg und die scheinbare Quell-IP. Das kann bei legitimen, geografisch spezifischen Tests helfen, autorisierten Traffic verteilen und Netzwerkfehler isolieren. Er verleiht jedoch keine Berechtigung, umgeht keine Rate Limits und garantiert auch keinen Zugriff. Ob ein Crawl überhaupt einen Proxy braucht, hängt vom Ziel, dessen Nutzungsbedingungen, der Anfragefrequenz und den Zuverlässigkeitsanforderungen des Jobs ab.
Nicht jeder Proxy ist gleich, und die falsche Wahl kann selbst zum Produktionsvorfall werden:
| Proxy-Typ | Zuverlässigkeit | Geschwindigkeit | Erkennungsrisiko | Typische Kosten |
|---|---|---|---|---|
| Kostenlose öffentliche Proxies | Sehr unterschiedlich | Schwankend | Oft hoch | Keine Gebühr, aber erhebliches Sicherheits- und Betriebsrisiko |
| Rechenzentrums-Proxies | Abhängig von Anbieter und Ziel | Oft schnell | Abhängig vom Ziel | Häufig nach GB oder IP bepreist |
| Residential-Proxies | Abhängig von Anbieter und Ziel | Schwankend | Abhängig vom Ziel | Häufig nach GB bepreist |
| ISP-Proxies | Abhängig von Anbieter und Ziel | Schwankend | Abhängig vom Ziel | Anbieterabhängig |
Kostenlose Proxies verdienen einen eigenen Hinweis, weil die belegten Risiken erheblich sind. Die 30-monatige Studie „Free Proxies Unmasked“ untersuchte mehr als 640.000 Adressen von 11 Anbietern: 34,5 % waren mindestens einmal aktiv, und 16.923 manipulierten Inhalte. Diese Datenlage ist ein deutliches Sicherheitswarnsignal für öffentliche Listen; sie ist jedoch keine übertragbare Ausfallrate für jede aktuelle Liste, jeden bezahlten Pool, jedes Ziel oder jede Workload.
Proxies umgehen moderne Bot-Erkennung auch nicht automatisch. Dienste wie Cloudflare bewerten Anfragen anhand dutzender Signale — TLS-Fingerprints, Header-Konsistenz, JavaScript-Ausführung, Verhaltensmuster — wobei Ihre IP-Adresse nur ein Faktor unter vielen ist. Ein sauberer Residential-Proxy wird bei inkonsistenten Headern und ohne Cookie-Jar trotzdem auffallen. Behalten Sie diese Realität im Blick, bevor Sie Ihre Architektur komplett auf „einfach die IP rotieren“ ausrichten.
Bevor Sie anfangen
- Schwierigkeitsgrad: Mittel
- Zeitaufwand: ca. 30–45 Minuten für das vollständige Produktions-Setup, ca. 5 Minuten für den Schnelltest
- Was Sie brauchen: Python 3.9+, installiertes Scrapy (dieser Leitfaden wurde gegen Scrapy 2.17.0, veröffentlicht im Juli 2026, getestet), einen funktionierenden Spider aus
scrapy startprojectund mindestens einen Proxy-Endpunkt (ein kostenloser Testzugang eines beliebigen Rechenzentrums-Proxy-Anbieters reicht zum Testen aus)
Schritt 1: Einen Proxy mit dem Request-Meta-Parameter testen
Der schnellste Weg, um zu prüfen, ob ein Proxy funktioniert, ist, die gesamte Middleware-Architektur zu umgehen und ihn direkt zu testen.
Scrapys eingebaute HttpProxyMiddleware liest den Schlüssel proxy direkt aus request.meta und leitet die Anfrage darüber. Keine Einstellungen, keine eigene Middleware-Klasse — nur ein einziges Schlüsselwortargument.
import scrapy
class ProxyTestSpider(scrapy.Spider):
name = "proxy_test"
start_urls = ["https://httpbin.org/ip"]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
meta={"proxy": "http://203.0.113.10:8080"},
callback=self.parse,
)
def parse(self, response):
self.logger.info(response.text)
Starten Sie das Ganze mit scrapy runspider proxy_test.py. Wenn der Proxy funktioniert, gibt httpbin.org/ip die IP-Adresse des Proxys statt Ihrer eigenen zurück — das ist Ihr Beweis. Hängt die Antwort und endet schließlich mit TCP connection timed out, ist der Proxy tot oder nicht erreichbar. Und ja: Das passiert öfter, als Proxy-Anbieter gern zugeben.
Für einzelne Spider oder kurze Tests ist diese Methode völlig in Ordnung. Sie scheitert jedoch sofort, sobald Sie mehr als einen Spider haben, weil Sie dann dieselbe Proxy-Zeichenkette in fünf Dateien hart codieren müssten.
Schritt 2: Eine benutzerdefinierte Proxy-Middleware bauen
Für alles, was über einen einzelnen Spider hinausgeht, sollte die Proxy-Logik zentral an einer Stelle liegen. Legen Sie in middlewares.py eine ProxyMiddleware-Klasse an:
from scrapy.exceptions import NotConfigured
class ProxyMiddleware:
def __init__(self, proxy_url):
self.proxy_url = proxy_url
@classmethod
def from_crawler(cls, crawler):
proxy_url = crawler.settings.get("PROXY_URL")
if not proxy_url:
raise NotConfigured("PROXY_URL is required; refusing silent direct fallback")
return cls(proxy_url=proxy_url)
def process_request(self, request, spider):
if "proxy" not in request.meta:
request.meta["proxy"] = self.proxy_url
Und registrieren Sie sie in settings.py:
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Beachten Sie die from_crawler-Klasse statt eines normalen __init__. Genau dieses Muster empfiehlt Scrapy für das Lesen von Einstellungen, und wir werden denselben Hook in Schritt 4 noch einmal für Secrets verwenden. Der Vorteil ist simpel: Sie ändern einen Wert, und alle Spider im Projekt nutzen den neuen Proxy. Kein Suchen und Ersetzen mehr in fünf Dateien, nur um eine tote IP auszutauschen.
Schritt 3: Die Middleware-Reihenfolge verstehen, damit Ihr Proxy nicht heimlich wirkungslos bleibt
Hier ist der Teil, den fast alle anderen Tutorials mit einem lockeren „stell einfach Priorität 350 ein, passt schon“ überspringen. Downloader-Middleware in Scrapy läuft in einer klar definierten Reihenfolge. Wer sie nicht versteht, erzeugt Bugs, die auf den ersten Blick mit Proxies überhaupt nichts zu tun haben.
Hooks auf Anfrageebene (process_request) laufen in aufsteigender Priorität — also zuerst die kleinste Zahl. Hooks auf Antwortebene (process_response, process_exception) laufen in absteigender Reihenfolge — zuerst die größte Zahl, dann rückwärts durch die Kette.
Request-Flow (aufsteigend):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] IHRE PROXY-MIDDLEWARE
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
Response-Flow (absteigend):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] IHRE PROXY-MIDDLEWARE → [300] HttpAuth
→ [100] RobotsTxt → Spider
Hier ist die aktuelle Standard-Prioritätstabelle der eingebauten Scrapy-Middlewares (gegen 2.17.0 verifiziert):
| Middleware | Standard-Priorität |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
Wenn RetryMiddleware einen Retry plant, kopiert sie die fehlgeschlagene Anfrage — inklusive ihrer Metadaten — und diese neue Anfrage läuft erneut durch die Downloader-Middleware-Kette. Das Verhältnis Ihres Selektors zur Priorität 550 garantiert keine Rotation. Rotation findet nur dann statt, wenn der Selektor den Retry erkennt und den kopierten Wert meta["proxy"] bewusst überschreibt. Priorität 350 ist ein praktischer Ort für einen Selektor, weil er vor dem eingebauten Transport-Schritt bei 750 läuft, aber sie ist allein noch kein Rotationsmechanismus.

Häufige Fehler bei der Middleware-Reihenfolge
- Eigene Middleware mit derselben Priorität wie
HttpProxyMiddleware(750) setzen: Dadurch entsteht ein Rennen, bei dem Scrapys Dict-Reihenfolge — nicht Ihre Logik — entscheidet, welche Middleware die Anfrage zuerst verarbeitet. Symptom: sporadisches, nicht nachvollziehbares Proxy-Verhalten. - In einem rotierenden Selektor
setdefault()oderif "proxy" not in request.metaverwenden: Die kopierte Retry-Anfrage behält dann den alten Proxy. Symptom: Jeder Retry landet wieder auf derselben fehlgeschlagenen Route. Lösung: Einen Retry erkennen (z. B.retry_times > 0) und den vom Selektor verwalteten Proxy-Wert explizit ersetzen. HttpProxyMiddlewarekomplett deaktivieren: Manche Tutorials empfehlen"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": None, weil „die Custom Middleware das übernimmt“. Tut sie aber nicht — Ihre Custom Middleware ist ein Selektor, keine Transportschicht. Wenn Sie die eingebaute Middleware deaktivieren, werden Proxy-Authentifizierungs-Header nie gesetzt, und Ihre Anfragen laufen stillschweigend ohne Authentifizierung durch (oder gar nicht).
Schritt 4: Proxy-Zugangsdaten nicht hart codieren
Jedes Scrapy-Proxy-Tutorial auf den vorderen Suchergebnissen schreibt http://username:password@proxy.example.com:8080 direkt in eine Python-Datei. Das ist eine Zugangsdaten-Kombi, die dann ewig im Git-Verlauf bleibt — in jedem Clone, jedem Fork und in jedem Log-Dump, sobald jemand mit print() zu locker umgeht.
Dafür gibt es im Wesentlichen drei Stufen:
| Methode | Sicherheit | Flexibilität | Am besten geeignet für |
|---|---|---|---|
| Hart codiert in spider/settings.py | Schlecht — Secrets liegen im Repo | Gering | Nur schnelle lokale Tests |
http_proxy-Umgebungsvariable (Scrapy-nativ) | Besser — nicht im Code | Gering (ein Proxy) | CI/CD-Pipelines, Docker |
.env-Datei + python-dotenv + from_crawler | Am besten — nicht im Code, pro Umgebung | Hoch (mehrere Proxies, Rotation) | Produktions-Scraper |
Die dritte Variante lohnt sich, wenn man sie sauber umsetzt. Installieren Sie python-dotenv, legen Sie eine .env-Datei an und fügen Sie sie sofort zu .gitignore hinzu — wirklich, machen Sie das jetzt:
PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080
Laden Sie die Werte ganz oben in settings.py:
from dotenv import load_dotenv
import os
load_dotenv()
PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASSWORD = os.getenv("PROXY_PASSWORD")
PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")
Lesen Sie sie dann sicher in Ihrer Middleware per from_crawler ein — genau dieses Muster löst die typische Forenfrage „Wie setze ich das vor scrapy crawl, wenn sich die Zugangsdaten pro Lauf ändern?":
import os
from urllib.parse import quote
class SecureProxyMiddleware:
def __init__(self, user, password, host, port):
self.user = quote(user, safe="")
self.password = quote(password, safe="")
self.host = host
self.port = port
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(
user=settings.get("PROXY_USER"),
password=settings.get("PROXY_PASSWORD"),
host=settings.get("PROXY_HOST"),
port=settings.get("PROXY_PORT"),
)
def process_request(self, request, spider):
proxy_url = f"http://{self.user}:{self.password}@{self.host}:{self.port}"
request.meta["proxy"] = proxy_url
Beachten Sie urllib.parse.quote() für die Zugangsdaten. Wenn Ihr Passwort @, : oder / enthält — und Passwortgeneratoren lieben solche Zeichen —, bricht die URL-Analyse, sofern der Wert nicht vorher per Percent-Encoding kodiert wird. Diese eine Zeile spart Ihnen eine peinliche Stunde Fehlersuche wegen „invalid proxy URL“, obwohl am Proxy selbst gar nichts ungültig ist.
Halten Sie Zugangsdaten aus Logs heraus und testen Sie die Percent-Encoding-Logik mit repräsentativen — aber gefälschten — Werten. HTTPS-Tunneling, SOCKS5 und nicht-lateinische Zugangsdaten haben jeweils eigene Grenzen im Handler; gehen Sie nicht davon aus, dass ein Authentifizierungsmodell ohne separat abgesicherte Integrationstests überall gleich funktioniert.

Schritt 5: Proxy-Rotation hinzufügen
Ein einzelner Proxy — selbst ein guter — wird irgendwann auffallen, wenn er 5.000 Anfragen an dieselbe Website schickt. Sie brauchen also einen Pool.
Option A — selbst bauen. Das ist tatsächlich recht einfach:
import random
class RotatingProxyMiddleware:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def process_request(self, request, spider):
request.meta["proxy"] = random.choice(self.proxy_pool)
Das funktioniert für Basisfälle, weiß aber überhaupt nicht, welche Proxies noch leben. Sie würfeln bei jeder Anfrage neu.
Option B — scrapy-rotating-proxies verwenden. Dieses Drittanbieter-Paket bringt Ban-Erkennung und automatisches Backoff direkt mit:
pip install scrapy-rotating-proxies
Ein wichtiger Realitätscheck: Die aktuellste Veröffentlichung auf PyPI ist Version 0.6.2 aus dem Jahr 2019, und das Projekt ist als Alpha markiert. Es ist nicht zwingend kaputt mit modernem Scrapy, aber „seit 2019 nicht aktiv gepflegt“ ist etwas anderes als „für Produktionsverkehr 2026 erprobt“. Pinnen Sie die Version, testen Sie sie gegen Ihre echten Zielseiten und nehmen Sie nicht an, dass authentifizierte Proxy-Endpunkte sauber unterstützt werden — das ist eher nicht der Fall.
Schritt 6: Eine fehlertolerante Proxy-Middleware bauen (Dead-Proxy-Erkennung)
Dieser Abschnitt fehlt in vielen Konkurrenz-Tutorials komplett — und genau hier liegt der Unterschied zwischen einer Demo und etwas, das einen 6-stündigen Crawl unbeaufsichtigt übersteht.
| Szenario | Was die meisten Tutorials zeigen | Was diese Middleware ergänzt |
|---|---|---|
| Proxy liefert 407 | Nicht behandelt | Wird als Proxy-Authentifizierungsfehler gewertet |
| Ziel liefert 403/429 | Oft zusammengeworfen | Hält Policy-/Rate-Feedback von Proxy-Gesundheit getrennt |
| Proxy läuft in ein Timeout | Nicht behandelt | Konfigurierbare Timeout-Schwelle, Abbau des Gesundheitswerts |
| Alle Proxies tot | Nicht behandelt | Sauberes Fallback oder Crawl-Pause mit Log-Warnung |
| Proxy flapping (intermittierend) | Nicht behandelt | Cool-down-Phase, bevor der Proxy wieder in den Pool kommt |
import time
import random
from scrapy.exceptions import IgnoreRequest
class FaultTolerantProxyMiddleware:
MAX_FAILURES = 3
COOLDOWN_SECONDS = 300
def __init__(self, proxy_pool):
self.pool = {p: {"failures": 0, "banned_until": 0} for p in proxy_pool}
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def _healthy_proxies(self):
now = time.time()
return [p for p, s in self.pool.items() if s["banned_until"] < now]
def process_request(self, request, spider):
healthy = self._healthy_proxies()
if not healthy:
spider.logger.warning("All proxies unhealthy — pausing crawl")
raise IgnoreRequest("No healthy proxies available")
request.meta["proxy"] = random.choice(healthy)
def process_response(self, request, response, spider):
proxy = request.meta.get("proxy")
if proxy and response.status == 407:
self._mark_failure(proxy)
return response
def process_exception(self, request, exception, spider):
proxy = request.meta.get("proxy")
if proxy:
self._mark_failure(proxy)
def _mark_failure(self, proxy):
state = self.pool[proxy]
state["failures"] += 1
if state["failures"] >= self.MAX_FAILURES:
state["banned_until"] = time.time() + self.COOLDOWN_SECONDS
state["failures"] = 0
Ein paar praktische Hinweise aus der echten Umsetzung: Behandeln Sie nicht jeden 403 als Beweis dafür, dass der Proxy selbst tot ist — RFC 9110 definiert 403 als „verstanden, aber verweigert“. Das kann genauso gut an verdächtigen Headern oder einer auffälligen Session liegen, unabhängig vom Proxy. Ein 407 hingegen bedeutet, dass der Proxy selbst Ihre Authentifizierung ablehnt — das ist ein stärkeres und spezifischeres Signal. Wer diese Signale in einen Topf wirft, verbrennt gesunde Proxies völlig unnötig.
Machen Sie die Standardwerte von Scrapys transienten Retries in der Produktion explizit, damit Reviewer die Policy sofort sehen:
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2
Das sind die Standardwerte von Scrapy 2.17. Fügen Sie 403 oder 407 nicht pauschal hinzu: 403 ist eine Ablehnung durch das Ziel mit vielen möglichen Ursachen, während 407 ein Proxy-Authentifizierungsproblem ist, das ein generischer Request-Retry nicht behebt. Wenn ein bestimmter Zielvertrag das Retrien eines weiteren Statuscodes rechtfertigt, dokumentieren Sie diesen Grund und testen Sie ihn separat.

Schritt 7: Ein messbarer Proxy-bei-Retry-Ansatz
Einige autorisierte Ziele liefern Inhalte direkt und benötigen einen Proxy erst nach einer dokumentierten, vorübergehenden Antwort. Das kann den Anteil proxierter Daten reduzieren, aber es gibt keinen allgemein gültigen, belastbaren Einsparungsprozentsatz oder eine übertragbare Erfolgsquote direkter Anfragen. Messen Sie Ihren eigenen Workload, bevor Sie dieses Muster übernehmen.
Nutzen Sie Scrapys öffentlichen Helper get_retry_request() und beschränken Sie Eskalationen auf gezielt klassifizierte, zielspezifische Statuscodes. Dieses Beispiel behandelt 429 und 503 als Retry-Signale; 403 und 407 sind bewusst ausgeschlossen.
from scrapy.downloadermiddlewares.retry import get_retry_request
class CostAwareEscalationMiddleware:
def process_response(self, request, response, spider):
if response.status not in {429, 503}:
return response
retry = get_retry_request(
request,
spider=spider,
reason=f"proxy_escalation_{response.status}",
max_retry_times=2,
)
if retry is None:
return response
current_tier = request.meta.get("proxy_tier", "direct")
if current_tier == "direct":
retry.meta["proxy"] = spider.settings["DATACENTER_PROXY"]
retry.meta["proxy_tier"] = "datacenter"
elif current_tier == "datacenter":
retry.meta["proxy"] = spider.settings["RESIDENTIAL_PROXY"]
retry.meta["proxy_tier"] = "residential"
else:
return response
return retry
Protokollieren Sie die Erfolgsrate gültiger Inhalte bei Direktzugriff, die Erfolgsrate über Proxies, Bytes pro erfolgreichem Datensatz, Retries pro Erfolg und die zusätzliche Latenz. Ein Direct-first-Design ist nur dann vertretbar, wenn direkter Zugriff autorisiert ist und das System strikt fehlschlägt, sobald ein Proxy nötig wird. Die Einsparung ist die gemessene Reduktion des proxierten Traffics — kein angenommener Prozentwert.
Wann sich eigenes Proxy-Management nicht lohnt
Ich möchte das klar sagen, statt so zu tun, als wäre der Rest des Artikels umsonst gewesen: Alles oben ist echte, nützliche Technik. Für viele Projekte — große Crawls, individuelle Pipelines, überall dort, wo Sie volle Kontrolle über das Request-Scheduling brauchen — ist das genau der richtige Weg.
Wenn Ihr eigentliches Ziel aber lautet „strukturierte Daten von dieser Seite holen“ und nicht „Proxy-Infrastruktur betreiben“, dann ist eine Extraktions-API oft die bessere Grenze. Die aktuelle POST /extract-Dokumentation von Thunderbit akzeptiert eine Seiten-URL und optional ein JSON-Schema; wenn kein Schema angegeben ist, kann der Dienst eines auf Basis des Seiteninhalts erzeugen. Der Endpunkt bietet außerdem die Modi none, basic und full sowie Steuerung für Timeouts und Wartezeiten nach dem Laden. Reine Prompt-Extraktion gehört nicht zum aktuell unterstützten Request-Umfang, bauen Sie Produktionsintegrationen also auf dem dokumentierten Schema-Vertrag auf. Das verschiebt die Extraktionsschnittstelle hinter einen einzelnen Request; es ist aber keine universelle Zusage für jedes Anti-Bot- oder CAPTCHA-Ziel.
| Faktor | DIY Scrapy + Proxies | API-basierte Extraktion (z. B. Thunderbit) | |---|---|---|---| | Einrichtungsaufwand | Hoch — Middleware, Rotation, Retry-Logik | Gering — ein API-Call mit Schema | | Netzwerk-/Rendering-Verhalten | Sie konfigurieren Handler, Proxies, Header und Delays | Über dokumentierte API-Optionen steuerbar | | Wartung | Sie verantworten Selektoren, Pool-Zustand und Zieländerungen | Sie verantworten Schema-Qualität, Validierung und Integrationsverhalten | | Kostenmodell | Proxy-Gebühren + Rechenleistung + Engineering-Zeit | In den aktuellen Docs: 20 Units pro Seite (geprüft am 2026-08-10) | | Kontrolle | Voll — eigene Pipelines, Middleware-Kette | Begrenzt auf die API-Funktionen | | Am besten für | Komplexe Crawls, hohes Volumen, individuelle Logik | Gezielte Extraktion, Prototyping, Anreicherung |
Wenn Sie vor allem strukturierte Seiten für Lead-Listen, Produktdaten oder Recherchen extrahieren, fahren Sie einen repräsentativen Pilot mit der Thunderbit Chrome Extension oder der API und vergleichen Sie gültige Datensätze, Latenz, Units und Wartungsaufwand. Wenn Ihr Projekt eigene Crawl-Grafen und Pipeline-Kontrolle braucht, bleibt Scrapy die stärkere Wahl.
Mehr erfahren
Tipps & häufige Stolperfallen
- Tipp: Testen Sie Proxies zuerst gegen
httpbin.org/ip, bevor Sie sie auf ein echtes Ziel loslassen. So sehen Sie am schnellsten, ob das Routing funktioniert, bevor Sie zusätzliche Komplexität darauf aufbauen. - Stolperfalle: Einen Proxy in
request.headersstatt inrequest.metasetzen. Das ist ein erstaunlich häufiger Tippfehler-ähnlicher Bug —HttpProxyMiddlewareliest nurmeta["proxy"], und ein Header-Versuch scheitert still, ohne offensichtliche Fehlermeldung. - Stolperfalle: Annehmen, dass HTTP- und HTTPS-Proxies identisch konfiguriert werden. Eine HTTP-Proxy-URL für ein HTTPS-Ziel funktioniert in der Regel per CONNECT-Tunneling über einen kompatiblen Handler, aber die Verwendung des Schemas
https://für den Proxy-Endpunkt selbst ist eine andere, weniger unterstützte Konfiguration — nicht verwechseln. - Tipp: Wenn Sie SOCKS5-Unterstützung brauchen, prüfen Sie zuerst die Download-Handler-Funktionen Ihrer Scrapy-Version. Der experimentelle Httpx-Handler in Scrapy 2.17 unterstützt SOCKS5 via
httpx[socks], ist aber weiterhin als experimentell gekennzeichnet — bauen Sie darauf keine produktive Abhängigkeit ohne eigene Tests.
Alternative Methoden
Neben scrapy-rotating-proxies routen manche Teams ihre gesamte Proxy-Logik über ein Anbieter-Gateway — also eine einzige Proxy-URL, hinter der der Anbieter Rotation, Session-Stickiness und Geo-Targeting intern übernimmt. Das kostet ein wenig Kontrolle, spart aber viel Middleware-Code, und es lohnt sich, das gegen einen selbst verwalteten Pool preislich zu vergleichen, bevor Sie alles von Grund auf selbst bauen.
Fazit
Einen Proxy in Scrapy einzutragen dauert eine Zeile. Damit diese Konfiguration einen echten Produktions-Crawl überlebt, brauchen Sie eine klare Fehlerstrategie, geschützte Zugangsdaten, getestete Handler-Grenzen und Selektor-Code, der kopierte Proxy-Metadaten bei einem Retry bewusst ersetzt. Wenn Sie zwei Gewohnheiten mitnehmen, dann diese: Bei benötigtem Proxy fail-closed arbeiten und niemals ein Proxy-Passwort in eine Python-Datei committen.
FAQs
Wie richte ich in Scrapy einen benutzerdefinierten Proxy mit Authentifizierung ein?
Verwenden Sie das Format protocol://username:password@host:port, kodieren Sie Benutzername und Passwort aber vorher mit urllib.parse.quote(), falls Sonderzeichen enthalten sind. In der Produktion sollten diese Zugangsdaten über eine from_crawler-Klasse aus Umgebungsvariablen gelesen werden, statt sie hart zu codieren.
Welche Prioritätszahl sollte ich für meine benutzerdefinierte Proxy-Middleware in Scrapy verwenden?
350 ist eine gängige Priorität für einen Selektor, weil sie vor HttpProxyMiddleware mit 750 ausgeführt wird. Das garantiert aber keine Rotation. Ein Retry erhält nur dann einen neuen Proxy, wenn der Selektor die kopierte Retry-Anfrage erkennt und ihren bisherigen Wert meta["proxy"] überschreibt.
Wie gehe ich in Scrapy automatisch mit toten Proxies um?
Bauen Sie eine Middleware, die in process_response und process_exception Fehlversuche pro Proxy zählt, Proxies nach einer Schwelle aus dem aktiven Pool entfernt und sie nach einer Cool-down-Phase wieder zulässt, statt sie dauerhaft zu sperren.
Kann ich Scrapy mit SOCKS5-Proxies verwenden?
Der experimentelle HttpxDownloadHandler in Scrapy 2.17 dokumentiert SOCKS5-Unterstützung, wenn httpx[socks] installiert ist. Der Standard-HTTP/1.1-Handler unterstützt SOCKS-Proxies nicht. Pinnen Sie Handler und Version und führen Sie vor dem produktiven Einsatz einen Integrationstest durch.
Wie viel kann das Proxy-bei-Retry-Muster wirklich bei den Proxy-Kosten sparen? Es gibt keinen allgemein übertragbaren Prozentsatz. Messen Sie den Anteil autorisierter Anfragen, die direkt gültige Inhalte liefern, die Bytes pro Proxy-Stufe, Retries pro erfolgreichem Datensatz und die zusätzliche Latenz. Die beobachtete Reduktion des proxierten Traffics ist Ihre Ersparnis; wenn Direct-first-Zugriff nicht autorisiert oder nicht gültig ist, sollten Sie dieses Muster nicht verwenden.


