Ohne KYC — keine ID, kein Selfie, keine Dokumente. Mit Krypto bezahlen: BTC, USDT, USDC, ETH, SOL, LTC
Sprache: Deutsch
Jetzt starten

Docs · Scrapy 2.x · Middleware · AutoThrottle

Scrapy Proxy Middleware für rotierende Residential-Proxys

Eine 30-zeilige Downloader-Middleware, die pro Anfrage Land und Session wählt, plus Einstellungen, die einen Crawl höflich halten: robots.txt, pro-Domain-Konkurrenz, AutoThrottle und Retries.

Zuletzt aktualisiert: · 5 Min. Lesezeit

Kurze Antwort

Wie nutze ich rotierende Residential-Proxys mit Scrapy?

Setze request.meta["proxy"] auf http://USERNAME:[email protected]:7777 in einer kleinen Downloader-Middleware, die vor Scrapy's HttpProxyMiddleware läuft; Scrapy schickt dann die Credentials für dich. Mit CrawlRelay Residential-Proxys kommen Land und sticky Session in den Benutzernamen, so dass eine Middleware beide Aufgaben übernimmt. Datenverkehr kostet ab 50 GB 0,50 $/GB. Bezahlen kannst du mit Krypto, ohne KYC.

Scrapy Einrichtung auf einen Blick

Wo kommt der Proxy hinrequest.meta["proxy"], vom eigenen Middleware gesetzt
Middleware-ReihenfolgeDie eigene Middleware unter 750, der Priorität der eingebauten HttpProxyMiddleware
ZugangsdatenIn der Proxy-URL; Scrapy wandelt sie in einen Proxy-Authorization Header um
SOCKS5Nicht mit dem Standard-HTTP/1.1-Handler; Scrapy 2.17+ unterstützt SOCKS über den optionalen HttpxDownloadHandler. Der HTTP-Port 7777 ist der einfachste
RotationNeuer Exit pro neuer Verbindung; eine eindeutige Session-ID pro Anfrage liefert normalerweise eine andere IP
HöflichkeitROBOTSTXT_OBEY, CONCURRENT_REQUESTS_PER_DOMAIN, AUTOTHROTTLE_ENABLED
Retries429 und 5xx sind im Standard-RETRY_HTTP_CODES enthalten; Retry-After wird standardmäßig nicht beachtet

Wie schreibe ich die Proxy-Middleware?

Lege die Klasse in middlewares.py ab. Sie baut den Benutzernamen aus Einstellungen und drei optionalen meta-Schlüsseln: proxy_country, proxy_session und proxy_ttl. Eine Anfrage ohne proxy_session rotiert; eine mit proxy_session="new" erzeugt eine neue Session, also normalerweise eine andere Exit-IP; eine Anfrage mit fester Session-ID teilt sich den Exit mit anderen Anfragen desselben Flows. Die Middleware berechnet ihren Proxy bei jedem Durchlauf neu, auch bei Retries, und lässt einen von anderem Code gesetzten Proxy unangetastet. Wenn CRAWLRELAY_USER fehlt, stoppt der Crawl beim Start statt mit deiner eigenen IP zu laufen.

middlewares.py
import uuid
from urllib.parse import quote


class CrawlRelayProxyMiddleware:
    """Sets request.meta["proxy"]; Scrapy's HttpProxyMiddleware then sends the credentials."""

    def __init__(self, user, password, country):
        self.user, self.password, self.country = user, password, country

    @classmethod
    def from_crawler(cls, crawler):
        s = crawler.settings
        if not s.get("CRAWLRELAY_USER"):
            # Fail loudly: NotConfigured would only disable the middleware and crawl from your own IP
            raise ValueError("CRAWLRELAY_USER is not set: refusing to crawl without the proxy")
        return cls(s.get("CRAWLRELAY_USER"), s.get("CRAWLRELAY_PASSWORD", ""), s.get("CRAWLRELAY_COUNTRY", "us"))

    def process_request(self, request, spider=None):
        if "proxy" in request.meta and not request.meta.get("_crawlrelay"):
            return None  # a proxy set by other code: leave it alone
        parts = [self.user, "country", request.meta.get("proxy_country", self.country)]
        session = request.meta.get("proxy_session")
        if session == "new":  # a fresh session for this request (and for each of its retries)
            session = uuid.uuid4().hex[:12]
        if session:
            parts += ["session", session, "ttl", str(request.meta.get("proxy_ttl", 10))]
        username = "-".join(parts)
        request.meta["proxy"] = "http://%s:%[email protected]:7777" % (
            quote(username, safe=""), quote(self.password, safe=""))
        request.meta["_crawlrelay"] = True
        return None

Welche Scrapy-Einstellungen halten einen Crawling mit Residential-Proxys höflich und schnell?

Residential-Proxys verteilen deine Anfragen auf viele IPs, aber das Ziel sieht deine Gesamt-Rate. Diese Einstellungen halten jede Domain in einem nachhaltigen Tempo und lassen AutoThrottle bei langsameren Antworten selbst verlangsamen. Behalte ROBOTSTXT_OBEY = True, das neue Scrapy-Projekte schon setzen.

settings.py (Auszug)
import os

BOT_NAME = "myproject"
ROBOTSTXT_OBEY = True
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"

CRAWLRELAY_USER = os.environ.get("CRAWLRELAY_USER")  # e.g. the username from your dashboard
CRAWLRELAY_PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD")
CRAWLRELAY_COUNTRY = "us"

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.CrawlRelayProxyMiddleware": 350,  # runs before HttpProxyMiddleware (750)
}

CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 4
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 30.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 522, 524, 408]
DOWNLOAD_TIMEOUT = 45

Wie kann ich eine IP für die Paginierung beibehalten und für Artikelseiten rotieren?

Übergib eine feste proxy_session bei den Requests eines Ablaufs, der von der vorherigen Seite abhängt, und keine bei unabhängigen Seiten. Der folgende Spider läuft auf books.toscrape.com, einer Sandbox-Seite, die zum Üben von Scraping erstellt wurde.

spiders/catalog.py
import scrapy


class CatalogSpider(scrapy.Spider):
    name = "catalog"
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for href in response.css("article.product_pod h3 a::attr(href)").getall():
            # Product pages are independent: rotating exits
            yield response.follow(href, callback=self.parse_item)
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            # The listing flow keeps one sticky exit for 30 minutes
            yield response.follow(next_page, callback=self.parse,
                                  meta={"proxy_session": "listing1", "proxy_ttl": 30})

    def parse_item(self, response):
        yield {
            "title": response.css("div.product_main h1::text").get(),
            "price": response.css("p.price_color::text").get(),
            "url": response.url,
        }

Verwendet Scrapy Verbindungen wieder und verhindert das die Rotation?

Ja, Scrapy hält Verbindungen aktiv und nutzt sie pro Proxy und Zielhost wieder, sodass auf einer Verbindung aufeinanderfolgende Requests denselben Exit teilen. Für die meisten Crawls ist das kein Problem: Scrapy öffnet bis zu CONCURRENT_REQUESTS_PER_DOMAIN Verbindungen pro Site, und jede neue Verbindung erhält normalerweise eine andere Residential Proxy-IP.

Wenn ein Request über eine neue IP gehen muss, z. B. ein Retry nach einem 502 durch einen fehlerhaften Exit, sende ihn mit response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True) erneut. Die Middleware erstellt einen neuen Session-Benutzernamen, ein anderer Benutzername bedeutet einen anderen Connection Pool, sodass Scrapy eine neue Verbindung öffnet und das Gateway normalerweise einen anderen Exit zuweist.

Wie respektiere ich Retry-After bei 429-Antworten in Scrapy?

Scrapy's eingebaute RetryMiddleware versucht 429 erneut, wartet aber nicht den Retry-After-Verzögerungswert ab. AutoThrottle verlangsamt die Domain mit der Zeit, was meist ausreichend ist. Für APIs, die lange Retry-After-Werte senden, füge eine kleine Middleware hinzu, die den Header ausliest und den Retry mit Verzögerung plant, oder setze CONCURRENT_REQUESTS_PER_DOMAIN für diese Domain herunter. Die 429-Seite erklärt, was der Header bedeutet.

Wie nutze ich den Proxy mit scrapy-playwright?

Für Seiten, die JavaScript benötigen, übergibt scrapy-playwright Playwrights Launch-Optionen über eine Einstellung. Gib die Zugangsdaten als separate Felder an: Chromium ignoriert Zugangsdaten, die direkt in der Server-URL stehen. Verwende einen Kontext pro Sticky Session, wenn du ausgeloggte Abläufe über mehrere Seiten renderst.

settings.py für scrapy-playwright
import os

DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "proxy": {
        "server": "http://gw.crawlrelay.com:7777",
        "username": os.environ.get("CRAWLRELAY_USER", "USERNAME") + "-country-us",
        "password": os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD"),
    },
}

Scrapy und Proxys: Fragen

Brauche ich scrapy-rotating-proxies oder eine Proxy-Liste?

Nein. Diese Pakete rotieren eine Liste einzelner Proxy-Adressen und sperren fehlerhafte. Mit einem Residential Gateway rotiert die IP hinter einer einzelnen Adresse: Jede neue Verbindung erhält normalerweise eine andere Residential Proxy-IP. Eine einzelne Middleware, die request.meta["proxy"] setzt, reicht aus.

Warum zeigt Scrapy TunnelError mit meinem Proxy?

TunnelError: Could not open CONNECT tunnel bedeutet, dass der Proxy die CONNECT-Anfrage für eine HTTPS-Seite ablehnte oder fehlschlug. Der Status im Fehler gibt den Grund an: 407 steht für Zugangsdaten, 502 oder 504 für den Exit oder das Ziel. Siehe die 407 und 502 Seiten.

Wie viele gleichzeitige Requests pro Domain sollte ich nutzen?

Starte mit 2 bis 4 pro Domain und lass AutoThrottle anpassen. Erhöhe die Anzahl nur, solange der Anteil von 429, 403 und 5xx-Antworten stabil bleibt, und beobachte die Antwortzeiten der Seite. Große Sites vertragen mehr als kleine; kleine Sites benötigen eventuell Verzögerungen zwischen Requests und mehrere Verbindungen.

Soll ich ROBOTSTXT_OBEY deaktivieren, um mehr Daten zu bekommen?

Nein. robots.txt gibt an, welche Pfade automatisierte Clients abrufen dürfen, und wurde 2022 durch RFC 9309 standardisiert. Das Ignorieren erhöht dein rechtliches und ethisches Risiko und verstößt gegen unsere Acceptable Use Policy, wenn damit auf Inhalte zugegriffen wird, die die Seite ausgeschlossen hat.

Wie viel Traffic verbraucht ein Scrapy-Crawl?

Scrapy lädt nur HTML, Seiten kosten daher meist einige zehn Kilobyte, komprimiert. Prüfe den downloader/response_bytes-Wert in den von Scrapy am Ende eines Laufs ausgegebenen Statistiken und teile ihn durch die Anzahl der Antworten. Der Bandbreiten-Guide wandelt das in Kosten um.

Residential-Proxys in dein Scrapy-Projekt einfügen

5,94 $ für 10 GB zum Testen, 0,50 $/GB von 50 GB.

Ab 0,50 $/GB · ohne KYC

Jetzt starten