Sem KYC — sem ID, sem selfie, sem documentos. Pague com cripto: BTC, USDT, USDC, ETH, SOL, LTC
Idioma: Português
Comece agora

Docs · Scrapy 2.x · middleware · AutoThrottle

Middleware de proxy Scrapy para proxies residenciais rotativos

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.

Última atualização: · 6 min de leitura

Resposta rápida

Como usar proxies rotativos com Scrapy?

Defina request.meta["proxy"] para http://USERNAME:[email protected]: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

Onde colocar o proxyrequest.meta["proxy"], definido pelo seu middleware
Ordem do middlewareSeu middleware abaixo de 750, a prioridade do HttpProxyMiddleware builtin
CredenciaisNa URL do proxy; Scrapy converte para um header Proxy-Authorization
SOCKS5Nã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çãoNova saída a cada conexão nova; um ID de sessão único por requisição normalmente gera IP diferente
EtiquetaROBOTSTXT_OBEY, CONCURRENT_REQUESTS_PER_DOMAIN, AUTOTHROTTLE_ENABLED
Retries429 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
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

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)
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
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 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 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
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 e 502 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 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 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.

A partir de US$ 0,50/GB · sem KYC

Comece agora