# Middleware proxy Scrapy per proxy residenziali rotanti

> Middleware proxy rotante Scrapy: proxy residenziali rotanti e sticky per ogni richiesta, AutoThrottle e impostazioni di retry, scrapy-playwright. Codice controllato per sintassi.

- URL: https://crawlrelay.com/it/docs/scrapy/
- Ultimo aggiornamento: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

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.

**Risposta rapida — Come uso i proxy rotanti con Scrapy?**

Imposta `request.meta["proxy"]` a `http://USERNAME:PASSWORD@gw.crawlrelay.com: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

| Attributo | Valore |
| --- | --- |
| Dove va il proxy | `request.meta["proxy"]`, impostato dal tuo middleware |
| Ordine del middleware | Il tuo middleware sotto 750, la priorità di `HttpProxyMiddleware` incorporato |
| Credenziali | Nell'URL del proxy; Scrapy lo trasforma in un header `Proxy-Authorization` |
| SOCKS5 | Non col gestore HTTP/1.1 di default; Scrapy 2.17+ supporta SOCKS tramite l'opzionale HttpxDownloadHandler. La porta HTTP 7777 è la più semplice |
| Rotazione | Nuova uscita per ogni nuova connessione; un ID sessione unico per richiesta normalmente fornisce IP diversi |
| Cortesia | `ROBOTSTXT_OBEY`, `CONCURRENT_REQUESTS_PER_DOMAIN`, `AUTOTHROTTLE_ENABLED` |
| Retry | 429 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*

```python
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:%s@gw.crawlrelay.com: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)*

```python
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*

```python
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](https://crawlrelay.com/it/docs/errors/502-bad-gateway/) 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](https://crawlrelay.com/it/docs/errors/429-too-many-requests/) 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*

```python
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](https://crawlrelay.com/it/docs/errors/407-proxy-authentication-required/) e [502](https://crawlrelay.com/it/docs/errors/502-bad-gateway/).

### 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](https://crawlrelay.com/it/acceptable-use/) 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](https://crawlrelay.com/it/guides/bandwidth-calculator/) 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. [Vedi i prezzi](https://crawlrelay.com/it/pricing/) · [Guida alla concorrenza](https://crawlrelay.com/it/docs/concurrency/)

---

CrawlRelay: CrawlRelay è un servizio di proxy residenziali rotanti e proxy mobili 4G/5G primo per sviluppatori: traffico pay-as-you-go senza scadenza da 0,50 USD/GB, pagato in crypto, senza caricamento documenti. Senza KYC — niente documento, selfie o altri documenti. Paga in crypto.
