Senza KYC — niente documento, selfie o altri documenti. Paga in crypto: BTC, USDT, USDC, ETH, SOL, LTC
Lingua: Italiano
Inizia ora

Docs · Scrapy 2.x · middleware · AutoThrottle

Middleware proxy Scrapy per proxy residenziali rotanti

Un downloader middleware di 30 linee che sceglie paese e sessione per richiesta, più le impostazioni che mantengono un crawl educato: robots.txt, concorrenza per dominio, AutoThrottle e retry.

Ultimo aggiornamento: · 6 minuti di lettura

Risposta rapida

Come uso i proxy rotanti con Scrapy?

Imposta request.meta["proxy"] a http://USERNAME:[email protected]:7777 in un piccolo downloader middleware che si esegue prima di HttpProxyMiddleware di Scrapy; Scrapy invia poi le credenziali per te. Con proxy residenziali CrawlRelay, il paese e la sessione sticky vanno nello username, quindi un solo middleware gestisce entrambi. Il traffico costa 0,50 USD/GB da 50 GB. Pagamento in crypto, senza KYC.

Configurazione Scrapy in sintesi

Dove va il proxyrequest.meta["proxy"], impostato dal tuo middleware
Ordine del middlewareIl tuo middleware sotto 750, la priorità di HttpProxyMiddleware incorporato
CredenzialiNell'URL del proxy; Scrapy lo trasforma in un header Proxy-Authorization
SOCKS5Non col gestore HTTP/1.1 di default; Scrapy 2.17+ supporta SOCKS tramite l'opzionale HttpxDownloadHandler. La porta HTTP 7777 è la più semplice
RotazioneNuova uscita per ogni nuova connessione; un ID sessione unico per richiesta normalmente fornisce IP diversi
CortesiaROBOTSTXT_OBEY, CONCURRENT_REQUESTS_PER_DOMAIN, AUTOTHROTTLE_ENABLED
Retry429 e 5xx sono nel RETRY_HTTP_CODES di default; Retry-After non è letto di default

Come scrivo il middleware proxy?

Metti la classe in middlewares.py. Costruisce lo username da settings e tre chiavi meta opzionali: proxy_country, proxy_session e proxy_ttl. Una richiesta senza proxy_session ruota; una richiesta con proxy_session="new" ottiene una nuova sessione, quindi un IP di uscita normalmente diverso; una richiesta con ID sessione fisso condivide un'uscita con le altre richieste dello stesso flusso. Il middleware ricalcola il proxy a ogni passaggio, compresi i retry, e lascia intatto un proxy impostato da altro codice. Se manca CRAWLRELAY_USER, il crawl si ferma all'avvio invece di partire con il tuo IP.

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

Quali impostazioni Scrapy mantengono un crawl proxyato educato e veloce?

I proxy distribuiscono le richieste su molti IP, ma il target vede comunque il rate totale. Queste impostazioni mantengono ogni dominio a un ritmo sostenibile e permettono ad AutoThrottle di rallentare autonomamente quando le risposte si fanno più lente. Mantieni ROBOTSTXT_OBEY = True, che i nuovi progetti Scrapy hanno già impostato.

settings.py (estratto)
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

Come faccio a mantenere un IP fisso per la paginazione e ruotare per le pagine degli articoli?

Passa un proxy_session fisso nelle richieste di un flusso che dipende dalla pagina precedente, e nessuno sulle pagine indipendenti. Lo spider qui sotto funziona su books.toscrape.com, un sito sandbox pubblicato per esercitarsi con lo scraping.

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,
        }

Scrapy riutilizza le connessioni, e questo impedisce la rotazione?

Sì, Scrapy mantiene vive le connessioni e le riutilizza per proxy e host di destinazione, quindi richieste consecutive su una connessione condividono la stessa uscita. Per la maggior parte dei crawl va bene: Scrapy apre fino a CONCURRENT_REQUESTS_PER_DOMAIN connessioni per sito, e ogni nuova connessione solitamente ottiene un proxy residenziale diverso.

Quando una richiesta deve uscire da un nuovo IP, ad esempio un ritentativo dopo un 502 da un'uscita fallita, rilanciala con response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True). Il middleware crea un nuovo nome utente di sessione, un nome utente diverso significa un pool di connessioni diverso, quindi Scrapy apre una nuova connessione e il gateway assegna normalmente un'uscita differente.

Come applico Retry-After sulle risposte 429 in Scrapy?

Il sistema di ritentativi integrato di Scrapy RetryMiddleware ritenta i 429 ma non aspetta il ritardo indicato da Retry-After. AutoThrottle rallenta il dominio progressivamente, di solito sufficiente. Per API che inviano valori lunghi in Retry-After, aggiungi un piccolo middleware che legge l'intestazione e pianifica il ritentativo con ritardo, o abbassa CONCURRENT_REQUESTS_PER_DOMAIN per quel dominio. La pagina 429 spiega cosa significa l'intestazione.

Come uso il proxy con scrapy-playwright?

Per pagine che necessitano JavaScript, scrapy-playwright passa le opzioni di lancio di Playwright tramite un settaggio. Inserisci le credenziali come campi separati: Chromium ignora le credenziali scritte nell'URL del server. Usa un contesto per ogni sessione sticky se rendi flussi disconnessi che coprono più pagine.

settings.py per 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 e proxy: domande

Serve scrapy-rotating-proxies o una lista di proxy?

No. Questi pacchetti ruotano una lista di indirizzi proxy individuali e bloccano quelli che falliscono. Con un gateway residenziale, la rotazione avviene dietro un solo indirizzo: ogni nuova connessione normalmente ottiene un proxy residenziale differente. Ti basta un middleware singolo che imposti request.meta["proxy"].

Perché Scrapy mostra TunnelError con il mio proxy?

TunnelError: Could not open CONNECT tunnel significa che il proxy ha rifiutato o fallito la richiesta CONNECT per un sito HTTPS. Lo stato nel messaggio indica il motivo: 407 è per le credenziali, 502 o 504 l'uscita o la destinazione. Vedi le pagine 407 e 502.

Quante richieste contemporanee per dominio dovrei usare?

Inizia con 2-4 per dominio e lascia che AutoThrottle regoli. Aumenta solo se la quota di risposte 429, 403 e 5xx resta stabile, e controlla i tempi di risposta del sito. Siti grandi tollerano di più rispetto a quelli piccoli; un sito piccolo potrebbe aver bisogno di un ritardo tra le richieste e una singola connessione.

Devo disabilitare ROBOTSTXT_OBEY per ottenere più dati?

No. robots.txt indica quali percorsi i client automatici possono scaricare, e RFC 9309 ha standardizzato il formato nel 2022. Ignorarlo aumenta il rischio legale ed etico e viola la nostra Acceptable Use Policy quando viene usato per accedere a contenuti esclusi dal sito.

Quanta banda usa un crawl di Scrapy?

Scrapy scarica solo HTML, quindi le pagine di solito pesano decine di kilobyte compresse. Controlla il valore downloader/response_bytes nelle statistiche che Scrapy stampa a fine esecuzione e dividilo per il numero di risposte. La guida alla banda trasforma questo dato in un costo.

Integra proxy residenziali nel tuo progetto Scrapy

5,94 USD per 10 GB in test, 0,50 USD/GB da 50 GB.

Da 0,50 USD/GB · senza KYC

Inizia ora