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:

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.

Flussdiagramm der Scrapy-Proxy-Middleware-Kette: Spider-Anfrage, benutzerdefinierte Middleware setzt Meta-Proxy, HttpProxyMiddleware fügt Auth bei Priorität 750 hinzu, Downloader verlässt über rotierendes Gateway
Ihre Middleware muss nur meta['proxy'] setzen; die eingebaute bei Priorität 750 verarbeitet Anmeldeinformationen, und Wiederholungen treten mit einer neuen IP erneut in die Kette ein.

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
Vergleich einer DIY Scrapy Proxy-Listen-Middleware gegenüber einem rotierenden Wohnanschluss-Gateway für Proxy-Rotation
Eine DIY-Liste bedeutet Gesundheitschecks, Ausweisungslogik und veraltete IPs. Ein Gateway ist ein Endpunkt, der serverseitig rotiert — Wiederholungen landen standardmäßig auf frischen IPs.

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.

Rotierende Proxys in Ihr Scrapy-Projekt integrieren