# Middleware de proxy Scrapy para proxies residenciais rotativos

> Middleware rotativo para Scrapy: IPs residenciais rotativos e fixos por requisição, AutoThrottle e configurações de retry, scrapy-playwright. Código com sintaxe verificada.

- URL: https://crawlrelay.com/pt/docs/scrapy/
- Última atualização: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Middleware de downloader com 30 linhas que escolhe país e sessão por requisição, mais configurações para manter um crawl educado: robots.txt, concorrência por domínio, AutoThrottle e retries.

**Resposta rápida — Como usar proxies rotativos com Scrapy?**

Defina `request.meta["proxy"]` para `http://USERNAME:PASSWORD@gw.crawlrelay.com:7777` num middleware enxuto que roda antes do `HttpProxyMiddleware` do Scrapy; Scrapy então envia as credenciais para você. Com proxies residenciais **CrawlRelay**, o país e sessão fixa vão no nome de usuário, assim um middleware trata os dois casos. O tráfego custa US$ 0,50/GB a partir de 50 GB. Pague com cripto, sem KYC.

## Configuração Scrapy resumida

| Atributo | Valor |
| --- | --- |
| Onde colocar o proxy | `request.meta["proxy"]`, definido pelo seu middleware |
| Ordem do middleware | Seu middleware abaixo de 750, a prioridade do `HttpProxyMiddleware` builtin |
| Credenciais | Na URL do proxy; Scrapy converte para um header `Proxy-Authorization` |
| SOCKS5 | Não com o handler HTTP/1.1 padrão; Scrapy 2.17+ suporta SOCKS usando o HttpxDownloadHandler opcional. A porta HTTP 7777 é a mais simples |
| Rotação | Nova saída a cada conexão nova; um ID de sessão único por requisição normalmente gera IP diferente |
| Etiqueta | `ROBOTSTXT_OBEY`, `CONCURRENT_REQUESTS_PER_DOMAIN`, `AUTOTHROTTLE_ENABLED` |
| Retries | 429 e erros 5xx estão no `RETRY_HTTP_CODES` padrão; Retry-After não é lido por padrão |

## Como escrever o middleware de proxy?

Coloque a classe em `middlewares.py`. Ela monta o nome de usuário a partir das configurações e de três chaves opcionais `meta`: `proxy_country`, `proxy_session` e `proxy_ttl`. Uma requisição sem `proxy_session` rotaciona; com `proxy_session="new"` cria uma sessão nova, logo IP de saída normalmente diferente; com ID fixo de sessão, compartilha saída com outras do mesmo fluxo. O middleware recalcula seu proxy a cada passagem, incluindo retries, e não altera um proxy definido por outro código. Se `CRAWLRELAY_USER` faltar, o crawl para no início ao invés de rodar com seu próprio 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
```

## Quais configurações do Scrapy mantêm um crawl via proxy educado e rápido?

Proxies distribuem suas requisições por muitos IPs, mas o alvo ainda vê a taxa total. Essas configurações mantêm o ritmo sustentável em cada domínio e deixam o AutoThrottle desacelerar sozinho quando respostas atrasam. Mantenha `ROBOTSTXT_OBEY = True`, que novos projetos Scrapy já definem.

*settings.py (trecho)*

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

## Como mantenho um IP fixo para paginação e rotaciono para páginas de itens?

Passe um `proxy_session` fixo nas requisições de um fluxo que depende da página anterior, e nenhum nas páginas independentes. O spider abaixo roda em books.toscrape.com, um site sandbox publicado para prática de 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,
        }
```

## O Scrapy reutiliza conexões e isso impede a rotação?

Sim, o Scrapy mantém conexões ativas e as reutiliza por proxy e host alvo, então requisições consecutivas numa conexão compartilham uma saída. Para a maioria dos crawls isso é ótimo: o Scrapy abre até `CONCURRENT_REQUESTS_PER_DOMAIN` conexões por site, e cada nova conexão geralmente recebe um IP residencial diferente.

Quando uma requisição deve sair por um novo IP, por exemplo, uma nova tentativa após um erro [502](https://crawlrelay.com/pt/docs/errors/502-bad-gateway/) de saída com problema, reemita usando `response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True)`. O middleware constrói um novo nome de usuário de sessão, nomes diferentes significam pools diferentes de conexão, assim o Scrapy abre uma conexão nova e o gateway normalmente atribui uma saída diferente.

## Como respeitar Retry-After em respostas 429 no Scrapy?

O `RetryMiddleware` interno do Scrapy tenta novamente no 429, mas não espera o delay `Retry-After`. AutoThrottle desacelera o domínio com o tempo, o que geralmente basta. Para APIs que enviam valores `Retry-After` longos, adicione um pequeno middleware que lê o header e agenda a nova tentativa com atraso, ou diminua `CONCURRENT_REQUESTS_PER_DOMAIN` para esse domínio. A [página 429](https://crawlrelay.com/pt/docs/errors/429-too-many-requests/) explica o que o header significa.

## Como usar o proxy com scrapy-playwright?

Para páginas que precisam de JavaScript, scrapy-playwright passa as opções de launch do Playwright por uma configuração. Forneça as credenciais em campos separados: Chromium ignora credenciais embutidas na URL do servidor. Use um contexto por sessão fixa se renderizar fluxos desconectados em várias páginas.

*settings.py para 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 proxies: perguntas

### Preciso de scrapy-rotating-proxies ou uma lista de proxies?

Não. Esses pacotes rotacionam uma lista de endereços proxy individuais e bloqueiam os que falham. Com um gateway residencial, a rotação ocorre atrás de um único endereço: cada nova conexão geralmente obtém um IP residencial diferente. Um único middleware que seta `request.meta["proxy"]` é tudo que você precisa.

### Por que o Scrapy mostra TunnelError com meu proxy?

`TunnelError: Could not open CONNECT tunnel` significa que o proxy recusou ou falhou no pedido CONNECT para um site HTTPS. O status na mensagem informa a causa: 407 é problema de credenciais, 502 ou 504 é o exit ou o alvo. Veja as páginas [407](https://crawlrelay.com/pt/docs/errors/407-proxy-authentication-required/) e [502](https://crawlrelay.com/pt/docs/errors/502-bad-gateway/) para mais.

### Quantas requisições concorrentes por domínio devo usar?

Comece com 2 a 4 por domínio e deixe o AutoThrottle ajustar. Aumente o número apenas enquanto a porcentagem de respostas 429, 403 e 5xx permanecer constante e observe os tempos de resposta do site. Sites grandes toleram mais requisições que sites pequenos; um site pequeno pode precisar de atraso entre requisições e uma única conexão.

### Devo desabilitar ROBOTSTXT_OBEY para pegar mais dados?

Não. O robots.txt é como o site informa quais caminhos clientes automatizados podem acessar, e o formato foi padronizado pelo RFC 9309 em 2022. Ignorar isso aumenta seu risco legal e ético e viola nossa [Política de Uso Aceitável](https://crawlrelay.com/pt/acceptable-use/) quando usado para acessar conteúdo que o site excluiu.

### Quanto tráfego um crawl Scrapy consome?

O Scrapy baixa só HTML, então páginas costumam custar dezenas de kilobytes cada, comprimidos. Verifique o valor `downloader/response_bytes` nas estatísticas que o Scrapy imprime ao final da execução e divida pelo número de respostas. O [guia de banda](https://crawlrelay.com/pt/guides/bandwidth-calculator/) transforma isso num custo.

**Conecte IPs residenciais ao seu projeto Scrapy** US$ 5,94 para 10 GB para testar, US$ 0,50/GB a partir de 50 GB. [Veja os preços](https://crawlrelay.com/pt/pricing/) · [Guia de concorrência](https://crawlrelay.com/pt/docs/concurrency/)

---

CrawlRelay: CrawlRelay é um serviço proxy residencial rotativo e proxies móveis 4G/5G para desenvolvedores, para scrapers e pipelines de dados de IA: tráfego pay-as-you-go que não expira a partir de US$ 0,50/GB, pago com cripto, sem upload de documento. Sem KYC — sem ID, sem selfie, sem documentos. Pague com cripto.
