Scrapy Proxy Middleware: Rotierende Proxies ohne Sperren
Scrapy unterstützt seit Version 0.8 Proxys, aber die Dokumentation erklärt nie, wie man rotiert, erneut versucht und Sperren im großen Maßstab überlebt. Hier ist die Anatomie der Middleware, der Code und die Einstellungen, die entscheiden, ob Ihr Crawl abgeschlossen wird.
Scrapy hat seit Version 0.8 eine Proxy-Middleware, also wurde die Frage 'Unterstützt Scrapy Proxys?' vor fünfzehn Jahren geklärt. Was die Dokumentation nie ganz zusammenfügt, ist das Produktionsbild: wie die eingebaute Scrapy-Proxy-Middleware tatsächlich Anmeldeinformationen verarbeitet, wann Proxys pro Anfrage im Vergleich zu global gesetzt werden sollten und wie man rotiert und sich erholt, wenn ein Ziel beginnt, Ausgänge mitten im Crawl zu sperren. Dieser Leitfaden führt durch die gesamte Kette — die eingebaute HttpProxyMiddleware, pro Anfrage meta, eine benutzerdefinierte Rotations-Middleware mit Sperrbehandlung und die Einstellungen, die entscheiden, ob ein Crawl über 100.000 Seiten abgeschlossen wird oder um 3 Uhr morgens stirbt.
Wie die eingebaute Scrapy-Proxy-Middleware funktioniert
scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware ist standardmäßig mit Priorität 750 in DOWNLOADER_MIDDLEWARES_BASE aktiviert. Das Lesen des Quellcodes (etwa 100 Zeilen in Scrapy 2.17) verrät Ihnen alles, was Sie zum Debuggen benötigen:
- Beim Start ruft es
urllib.request.getproxies()auf, sodasshttp_proxy/https_proxyUmgebungsvariablen einen Spider ohne Code konfigurieren.no_proxywird nur für http- und https-Schemata berücksichtigt. - Pro Anfrage gewinnt
request.meta['proxy']immer über die Umgebung. Setzen Sie es aufNone, um eine direkte Verbindung für diese Anfrage zu erzwingen. - In der Proxy-URL eingebettete Anmeldeinformationen (
http://user:pass@host:port) werden entfernt, Base64-codiert und alsProxy-Authorization: BasicHeader gesendet. Die Kodierung erfolgt standardmäßig in latin-1 und ist überHTTPPROXY_AUTH_ENCODINGkonfigurierbar. - Die Middleware dekodiert Anmeldeinformationen vor der Kodierung, daher sollten Sie Sonderzeichen wie
@in Passwörtern URL-kodieren — sie werden korrekt durchgereicht. - Wenn ein erneuter Versuch den Proxy ändert, verwirft die Middleware den veralteten Auth-Header, anstatt ihn an den neuen Proxy zu leaken — eine Maßnahme zur Anmeldeinformationen-Hygiene in modernem Scrapy und ein guter Grund, keine alten Versionen zu verwenden.
Praktische Konsequenz: Sie benötigen fast nie ein Drittanbieter-Proxy-Paket. Alles, was eine gültige Proxy-URL in request.meta['proxy'] vor Priorität 750 einfügt, erhält Authentifizierung und Header-Verarbeitung kostenlos.
Pro-Anfrage-Proxys mit Meta
Die leichteste Integration besteht darin, meta direkt im Spider zu setzen — nützlich, wenn nur einige Anfragen einen Proxy benötigen oder verschiedene Ziele unterschiedliche Ausgangsländer benötigen:
import scrapy
class PricesSpider(scrapy.Spider):
name = "prices"
def start_requests(self):
yield scrapy.Request(
"https://example.com/product/1",
meta={"proxy": "http://USER:PASS@gate.quantumproxies.io:PORT"},
)
Pro-Anfrage-Meta glänzt, wenn die Proxy-Wahl selbst datengetrieben ist: leiten Sie deutsche Produktseiten über einen auf Deutschland ausgerichteten Benutzernamen, senden Sie Bilddownloads über günstige Rechenzentrumsausgänge, während HTML über Wohnanschlüsse geht, oder eskalieren Sie eine hartnäckige URL bei ihrem zweiten Versuch auf eine stabilere Sitzung. Da Meta Wiederholungen und Weiterleitungen überlebt, folgt die Entscheidung, die Sie beim Erstellen der Anfrage treffen, dem gesamten Download-Zyklus. Das Setzen von Meta auf Tausenden von Anfragen von Hand skaliert jedoch nicht — dafür sind Downloader-Middlewares da.
Das einfachste Produktionssetup: ein rotierendes Gateway
Mit rotierenden Proxys hinter einem einzigen Gateway-Endpunkt erfolgt die Rotation serverseitig: jede Anfrage über dieselbe URL verlässt über eine andere IP in einem 90M+ Wohnanschlusspool, der über 200+ Länder spannt. Ihre Middleware schrumpft auf drei Zeilen, und es gibt keine Liste, die überprüft, bereinigt oder aktualisiert werden muss:
# middlewares.py
class RotatingGatewayMiddleware:
PROXY = "http://USER:PASS@gate.quantumproxies.io:PORT"
def process_request(self, request, spider):
request.meta["proxy"] = self.PROXY
# settings.py
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.RotatingGatewayMiddleware": 350,
}
Priorität 350 ist wichtig: Ihre Middleware muss vor der eingebauten bei 750 laufen, damit HttpProxyMiddleware die Anmeldeinformationen noch in den Auth-Header umwandeln kann. Diese Paarung — Gateway-Rotation plus Scrapy's Standard-Wiederholungsmechanismus — bietet die höchste Zuverlässigkeit pro Codezeile, da jeder erneute Versuch automatisch über eine frische Ausgangs-IP erfolgt.

Eine benutzerdefinierte rotierende Proxy-Middleware mit Sperrbehandlung
Wenn Sie Ihre eigene Proxy-Liste betreiben — statische ISP-Adressen oder einen gemischten Pool — ist das klassische Muster (popularisiert durch das scrapy-proxies-Paket, das RETRY_TIMES = 10 empfahl, weil kostenlose Listen-Proxys so oft versagen): wählen Sie zufällig pro Anfrage, entfernen Sie bei Sperrsignalen, stellen Sie die Anfrage erneut in die Warteschlange:
import random
class ProxyListMiddleware:
BAN_CODES = {403, 429}
def __init__(self, proxies):
self.proxies = list(proxies)
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings.getlist("PROXY_LIST"))
def process_request(self, request, spider):
if self.proxies and "proxy" not in request.meta:
request.meta["proxy"] = random.choice(self.proxies)
def process_response(self, request, response, spider):
if response.status in self.BAN_CODES:
bad = request.meta.get("proxy")
if bad in self.proxies and len(self.proxies) > 1:
self.proxies.remove(bad)
spider.logger.warning("Evicted %s (%d left)", bad, len(self.proxies))
return request.replace(dont_filter=True) # re-queue on a new proxy
return response
Beachten Sie alles, was diese Middleware tun muss, was ein Gateway kostenlos erledigt: Pool-Gesundheit verfolgen, verbrannte IPs entfernen, Anfragen erneut in die Warteschlange stellen. Es schrumpft auch unter Beschuss — ein Pool von 20 statischen IPs kann in Minuten auf einem aggressiven Ziel verdampfen. Das breitere Anti-Sperrspielbuch (Tempo, Header, Sitzungsdisziplin) finden Sie in unserer IP-Sperrvermeidungs-Checkliste.
Scrapy-Einstellungen, die das Ergebnis bestimmen
Middleware platziert den Proxy; Einstellungen entscheiden, ob der Crawl sich verhält. Diese sind am wichtigsten:
# settings.py — a sane baseline for proxied crawls
RETRY_TIMES = 5
RETRY_HTTP_CODES = [429, 403, 500, 502, 503, 504, 408]
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 8
DOWNLOAD_TIMEOUT = 30
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 4.0
- RETRY_HTTP_CODES — fügen Sie 429 und 403 hinzu, damit weiche Sperren einen erneuten Versuch auslösen (und, mit Rotation, eine neue IP) anstelle eines fehlgeschlagenen Elements.
- RETRY_TIMES — 3-5 ist richtig für einen Qualitäts-Pool; 10 zu benötigen ist ein Zeichen dafür, dass der Pool selbst ungesund ist.
- CONCURRENT_REQUESTS_PER_DOMAIN — der echte Höflichkeitsregler. Rotation verteilt die Last über IPs, aber das Ziel sieht immer noch das Gesamtvolumen.
- DOWNLOAD_TIMEOUT — die Standard-180 Sekunden lassen einen langsamen Ausgang einen Konkurrenz-Slot für drei Minuten besetzen. 30 ist ausreichend.
- AUTOTHROTTLE — passt das Tempo an die beobachtete Latenz an; passt gut zu Rotation bei zielgerichteten Zielen. Immer noch in 429s ertrinken? Unser Rate-Limit-Leitfaden behandelt Konkurrenzbudgets ausführlich.

Sperrerkennung jenseits von Statuscodes
Fortschrittliche Ziele senden keine ehrlichen 403s. Sie liefern 200s, die eine CAPTCHA-Seite, ein leeres Produktgitter oder eine reduzierte Vorlage enthalten. Robuste Spider überprüfen den Inhalt, nicht nur den Status — suchen Sie nach einem Element, das nur auf der echten Seite existiert, und stellen Sie es erneut in die Warteschlange, wenn es fehlt. Wenn Seiten strukturell leer zurückkommen, selbst durch gute Proxys, wird der Inhalt wahrscheinlich clientseitig gerendert; siehe warum Scraper leere Seiten erhalten. Und wenn eine Domain plain HTTP unabhängig von der IP besiegt, übergeben Sie diese Domain an eine Scraper API, die JavaScript rendert und sauberes HTML oder Markdown zurückgibt — Scrapy konsumiert es wie jede andere Antwort, und Sie behalten Ihre Pipeline.
def parse(self, response):
if not response.css("div.product-grid"):
# 200 OK but the real content is missing: soft ban or JS wall
yield response.request.replace(dont_filter=True)
return
for product in response.css("div.product-grid article"):
yield {"name": product.css("h2::text").get()}
Häufig gestellte Fragen
Unterstützt Scrapy Proxys von Haus aus?
Ja. HttpProxyMiddleware wird mit Priorität 750 aktiviert geliefert: es liest http_proxy / https_proxy Umgebungsvariablen und berücksichtigt request.meta['proxy'] pro Anfrage, einschließlich user:pass@ Anmeldeinformationen, die es in einen Proxy-Authorization-Header umwandelt. Sie schreiben nur Code, um zu entscheiden, welcher Proxy jede Anfrage erhält.
Wie setze ich einen Proxy für eine einzelne Anfrage in Scrapy?
Übergeben Sie ihn im Meta der Anfrage: scrapy.Request(url, meta={'proxy': 'http://user:pass@host:port'}). Meta überschreibt immer umgebungsweite Proxys, und das Setzen des Werts auf None zwingt diese eine Anfrage, direkt zu gehen — nützlich, um in einem Spider sowohl proxied als auch unproxied Traffic zu mischen.
Wie rotiere ich Proxys in Scrapy?
Entweder schreiben Sie eine Downloader-Middleware, die pro Anfrage einen Proxy aus einer Liste auswählt und gesperrte entfernt, oder Sie richten jede Anfrage an einen rotierenden Gateway-Endpunkt, der serverseitig eine frische Ausgangs-IP zuweist. Der Gateway-Ansatz benötigt eine dreizeilige Middleware, keine Gesundheitschecks, und verwandelt jeden Scrapy-Wiederholungsversuch kostenlos in eine Rotation.
Warum gibt mein Scrapy-Proxy 407 zurück?
Der Proxy hat die Authentifizierung abgelehnt. Überprüfen Sie, ob Anmeldeinformationen in der Proxy-URL im Meta enthalten sind, dass Sonderzeichen URL-kodiert sind und dass Ihre Server-IP auf der Whitelist steht, wenn Ihr Plan IP-Auth verwendet. Wenn Anmeldeinformationen nicht-ASCII-Zeichen enthalten, setzen Sie HTTPPROXY_AUTH_ENCODING, um mit Ihrem Anbieter übereinzustimmen.
Das Muster, das Sie sich merken sollten: setzen Sie meta['proxy'] früh, lassen Sie die eingebaute Middleware bei 750 die Authentifizierung durchführen, fügen Sie 429 und 403 zu Ihren Wiederholungscodes hinzu und bevorzugen Sie serverseitige Rotation gegenüber Listenpflege. Wenn Teile Ihres Crawls JavaScript benötigen, wägen Sie die Kosten von Headless-Browsern gegenüber HTTP-Anfragen ab, bevor Sie einen verwenden — die meisten Scrapy-Projekte benötigen bessere IPs, nicht einen Browser.