O que é Scrapy e como usar com proxy

Publicado:

19 min de leitura

Acar Diveroli
Autor: Acar Diveroli
Cinco paradas da cadeia de middlewares do Scrapy: aranha, robots.txt, nova tentativa, cubo azul de proxy e site-alvo

O script que você escreveu com Requests e BeautifulSoup roda sem problemas em cem páginas. Quando chega a dez mil, o trabalho muda: você precisa controlar qual endereço já foi rastreado, qual deu erro e será tentado de novo, quantas requisições vão ao mesmo site ao mesmo tempo e onde os dados serão gravados. O Scrapy é um framework Python que traz tudo isso pronto. A configuração de proxy também fica dentro dessa estrutura, em uma das camadas por onde toda requisição passa.

Neste artigo mostramos primeiro, de forma breve, o que é o Scrapy e como instalá-lo. Depois vamos ao assunto principal: as três formas de definir um proxy, a autenticação, a diferença entre um gateway rotativo com um único endpoint e a sua própria lista de proxies, o controle de velocidade com DOWNLOAD_DELAY e AutoThrottle, e o comportamento do Scrapy diante das respostas 429 e 503. Executamos todos os exemplos com Scrapy 2.19 e Python 3.13, nos sites de prática do toscrape.com e em um proxy de teste local.

O que é Scrapy e para que serve?

O Scrapy é um framework Python escrito para rastrear sites e extrair dados estruturados das páginas. A diferença em relação a uma biblioteca é esta: o Requests entrega uma ferramenta para enviar uma requisição, e o laço é você quem monta. No Scrapy, o laço é o próprio framework. Você só escreve uma classe que diz de qual endereço começar e o que retirar da página recebida; essa classe se chama spider.

Tudo o que fica fora do spider já vem pronto: um agendador que coloca as requisições na fila, um filtro de duplicatas que não pede o mesmo endereço duas vezes, um downloader assíncrono, novas tentativas, verificação do robots.txt, limite de velocidade e exportação para JSON ou CSV. Nos seletores, CSS e XPath são usados lado a lado; explicamos a diferença em Seletor CSS ou XPath. O mapa geral das ferramentas em Python e JavaScript está em Extração de dados: JavaScript ou Python?.

Como instalar o Scrapy?

Instale o Scrapy em um ambiente virtual próprio do projeto, e não no Python do sistema. No Windows, os comandos são:

bash
python -m venv venv
venv\Scripts\activate
pip install scrapy
scrapy startproject kitaplik
cd kitaplik
scrapy genspider kitaplar books.toscrape.com

No macOS e no Linux, a segunda linha passa a ser source venv/bin/activate. O comando startproject gera um esqueleto com settings.py, middlewares.py e a pasta spiders/. O arquivo settings.py do modelo atual já traz três configurações: ROBOTSTXT_OBEY = True, CONCURRENT_REQUESTS_PER_DOMAIN = 1 e DOWNLOAD_DELAY = 1. Ou seja, um projeto novo respeita o robots.txt por padrão e envia cerca de uma requisição por segundo ao mesmo site. Não apague essas linhas; construa as suas configurações sobre elas.

Preencha o arquivo criado pelo genspider como abaixo. O spider lê os cartões dos livros e segue o link de "próxima página":

python
import scrapy


class KitaplarSpider(scrapy.Spider):
    name = "kitaplar"
    allowed_domains = ["books.toscrape.com"]
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for kart in response.css("article.product_pod"):
            yield {
                "baslik": kart.css("h3 a::attr(title)").get(),
                "fiyat": kart.css("p.price_color::text").get(),
                "adres": response.urljoin(kart.css("h3 a::attr(href)").get()),
            }

        sonraki = response.css("li.next a::attr(href)").get()
        if sonraki:
            yield response.follow(sonraki, callback=self.parse)

O comando scrapy crawl kitaplar -O kitaplar.json executa o spider e grava o resultado em um arquivo. No nosso teste vieram 40 registros das duas primeiras páginas. Mantenha a linha FEED_EXPORT_ENCODING = "utf-8" do modelo para que caracteres acentuados e símbolos de moeda não saiam corrompidos no arquivo.

Como o Scrapy processa uma requisição?

Para entender onde a configuração de proxy deve ficar, é preciso conhecer o caminho que a requisição percorre:

  1. O spider gera um objeto Request (a partir de start_urls ou de response.follow).
  2. O motor entrega a requisição ao agendador; o filtro de duplicatas descarta endereços já pedidos.
  3. Quando chega a sua vez, a requisição atravessa a cadeia de downloader middleware. Cada middleware tem um número de ordem, e a requisição avança do número menor para o maior: a verificação do robots.txt é 100, a nova tentativa é 550, HttpProxyMiddleware é 750.
  4. O downloader envia a requisição à rede. Se ela tem meta["proxy"], a conexão é aberta com o proxy, e não com o destino; em endereços HTTPS, um túnel CONNECT é estabelecido por meio do proxy.
  5. A resposta volta pela mesma cadeia em ordem inversa e chega à função de callback do spider (parse).
  6. Os registros gerados pela função vão para o item pipeline, e as novas requisições voltam ao agendador.

A conclusão: o proxy pertence ao terceiro passo, não ao código de análise do spider. Seja quem for que preencha meta["proxy"], quem faz o trabalho é HttpProxyMiddleware. Esse middleware vem ativado por padrão; a seção HttpProxyMiddleware da documentação do Scrapy define o seu comportamento.

Como definir um proxy no Scrapy?

Há três formas, e as três acabam preenchendo o mesmo campo meta["proxy"].

MétodoOnde se escreveAlcanceQuando convém
meta["proxy"]No spider, dentro de cada RequestSó aquela requisiçãoEnviar algumas requisições por outra saída
Variável de ambienteNo shell, http_proxy / https_proxyTodas as requisições, inclusive robots.txtTeste rápido, execução sem mexer no código
Downloader middlewaremiddlewares.py + settings.pyTodas as requisições, inclusive robots.txtConfiguração permanente do projeto, rotação

Por requisição: o campo meta

python
import scrapy

PROXY = "http://user:pass@pr.proxynet.io:8000"


class KitaplarMetaSpider(scrapy.Spider):
    name = "kitaplar_meta"
    allowed_domains = ["books.toscrape.com"]

    async def start(self):
        yield scrapy.Request("https://books.toscrape.com/", meta={"proxy": PROXY})

    def parse(self, response):
        for kart in response.css("article.product_pod"):
            yield {"baslik": kart.css("h3 a::attr(title)").get()}

        sonraki = response.css("li.next a::attr(href)").get()
        if sonraki:
            # meta não passa sozinha para a nova requisição, é levada à mão
            yield response.follow(sonraki, callback=self.parse, meta={"proxy": PROXY})

Esse método tem duas consequências que passam despercebidas, e vimos as duas nos testes. A primeira: meta não é transmitida sozinha para a requisição seguinte. No teste em que não incluímos meta na chamada response.follow, a primeira página saiu pelo proxy e a segunda saiu direto do nosso próprio endereço IP. A segunda: a requisição de robots.txt do Scrapy não é uma requisição escrita por você, então não carrega meta e também sai sem proxy. Se você quer que todo o tráfego use a mesma saída, recorra a uma das duas formas a seguir.

Variável de ambiente

HttpProxyMiddleware lê, assim como a biblioteca padrão do Python, as variáveis http_proxy, https_proxy e no_proxy. Sem mudar nada no código:

bash
export https_proxy="http://user:pass@pr.proxynet.io:8000"
export http_proxy="$https_proxy"
scrapy crawl kitaplar

Se uma requisição também tiver meta["proxy"], esse valor prevalece sobre a variável de ambiente e ignora a lista no_proxy. Como as variáveis são definidas no Windows, no macOS e no Linux, e a diferença entre maiúsculas e minúsculas, está explicado em Uso de proxy com o wget; não repetimos aqui.

Para o projeto inteiro: um middleware pequeno

Em uma configuração permanente, basta um middleware de poucas linhas que leia o endereço do ambiente em vez de embuti-lo no código:

python
# kitaplik/middlewares.py
from scrapy.exceptions import NotConfigured


class TekProxyMiddleware:
    """Aplica a todas as requisições o único endereço de proxy das configurações."""

    def __init__(self, adres):
        self.adres = adres

    @classmethod
    def from_crawler(cls, crawler):
        adres = crawler.settings.get("PROXY_ADRESI")
        if not adres:
            raise NotConfigured
        return cls(adres)

    def process_request(self, request):
        request.meta.setdefault("proxy", self.adres)
        return None
python
# kitaplik/settings.py
import os

PROXY_ADRESI = os.environ.get("PROXY_ADRESI")
DOWNLOADER_MIDDLEWARES = {
    "kitaplik.middlewares.TekProxyMiddleware": 610,
}

Graças ao setdefault, o meta["proxy"] que você escreveu à mão em uma requisição é preservado. Se PROXY_ADRESI não estiver definida, o middleware se desativa e o spider roda sem proxy. O número de ordem precisa ser menor que 750; assim você escreve o endereço primeiro e as credenciais são separadas depois por HttpProxyMiddleware. Em exemplos escritos para versões antigas, você verá a assinatura como process_request(self, request, spider); na documentação atual o parâmetro spider não existe, e o exemplo funciona desse jeito no Scrapy 2.19.

Como funciona a autenticação?

HttpProxyMiddleware separa a parte user:pass do endereço, codifica em Base64 e adiciona à requisição como cabeçalho Proxy-Authorization: Basic …. Em meta["proxy"] fica o endereço sem credenciais; você não verá a sua senha no log. Se a senha contiver @, : ou /, escreva com codificação percentual (%40 no lugar de @); o middleware decodifica o valor antes de enviar.

No teste com uma senha errada, o Scrapy escreveu esta linha:

text
TunnelError: Could not open CONNECT tunnel with proxy 127.0.0.1:8120 [{'status': 407, 'reason': b'Proxy Authentication Required'}]

Em endereços HTTPS, o 407 não chega como resposta, e sim como exceção, porque o túnel não pôde ser aberto. Um ponto de atenção: o middleware de novas tentativas trata essa exceção como falha temporária e tenta a mesma requisição mais duas vezes. Senha errada não se corrige com repetição; se você vir TunnelError e 407 no log, pare o rastreamento e corrija as credenciais. Se, em vez de usuário e senha, você usa whitelist de IP, o endereço é escrito sem credenciais, na forma http://pr.proxynet.io:8000. A diferença entre os dois métodos está em Autenticação de proxy: user:pass ou whitelist de IP.

A rotação precisa de um middleware?

A resposta depende do tipo de proxy que você tem.

Com um gateway rotativo, não. Em um serviço de proxy rotativo você se conecta a um único endpoint e é o gateway que troca o IP de saída. Do lado do Scrapy, basta o TekProxyMiddleware acima ou uma única variável de ambiente; manter uma lista, escolher o próximo endereço e separar os que falham não é tarefa sua. Essa é a configuração típica nos rastreamentos feitos com Proxies residenciais. Como funcionam os modos de rotação está no nosso artigo sobre rotação de IP.

Se você tem uma lista de endereços fixos (por exemplo, alguns endereços de Proxies de datacenter ou de Proxies ISP), quem faz a distribuição é um middleware. O exemplo abaixo usa os endereços em sequência, deixa descansar por um tempo aquele que falha várias vezes seguidas e grava as contagens nas estatísticas do Scrapy:

python
# kitaplik/middlewares.py
import time
from urllib.parse import urlsplit

from scrapy.exceptions import IgnoreRequest, NotConfigured


def anahtar(proxy_adresi):
    parca = urlsplit(proxy_adresi)
    return f"{parca.hostname}:{parca.port}"


class ProxyHavuzuMiddleware:
    """Atribui a cada requisição o próximo proxy da lista e deixa descansar o que falha em sequência."""

    def __init__(self, adresler, hata_siniri, dinlenme, stats):
        self.adresler = adresler
        self.hata_siniri = hata_siniri
        self.dinlenme = dinlenme
        self.stats = stats
        self.sira = 0
        self.hatalar = {anahtar(a): 0 for a in adresler}
        self.kapali = {}  # chave -> momento em que volta a abrir

    @classmethod
    def from_crawler(cls, crawler):
        adresler = crawler.settings.getlist("PROXY_LISTESI")
        if not adresler:
            raise NotConfigured
        return cls(
            adresler,
            crawler.settings.getint("PROXY_HATA_SINIRI", 3),
            crawler.settings.getfloat("PROXY_DINLENME", 60.0),
            crawler.stats,
        )

    def sec(self):
        simdi = time.monotonic()
        for _ in self.adresler:
            adres = self.adresler[self.sira % len(self.adresler)]
            self.sira += 1
            if self.kapali.get(anahtar(adres), 0) <= simdi:
                return adres
        return None

    def process_request(self, request):
        adres = self.sec()
        if adres is None:
            self.stats.inc_value("proxy_havuzu/hepsi_dinleniyor")
            raise IgnoreRequest("Todos os proxies da lista estão descansando")
        request.meta["proxy"] = adres
        request.meta["proxy_anahtari"] = anahtar(adres)
        return None

    def process_response(self, request, response):
        kim = request.meta.get("proxy_anahtari")
        if kim:
            self.hatalar[kim] = 0
            self.stats.inc_value(f"proxy_havuzu/yanit/{kim}")
        return response

    def process_exception(self, request, exception):
        kim = request.meta.get("proxy_anahtari")
        if not kim:
            return None
        self.hatalar[kim] += 1
        self.stats.inc_value(f"proxy_havuzu/hata/{kim}")
        if self.hatalar[kim] >= self.hata_siniri:
            self.kapali[kim] = time.monotonic() + self.dinlenme
            self.hatalar[kim] = 0
        return None
python
# kitaplik/settings.py
PROXY_LISTESI = [
    "http://user:pass@203.0.113.10:8000",
    "http://user:pass@203.0.113.11:8000",
    "http://user:pass@203.0.113.12:8000",
]
DOWNLOADER_MIDDLEWARES = {
    "kitaplik.middlewares.ProxyHavuzuMiddleware": 610,
}

Testamos o exemplo com três proxies locais, dois em funcionamento e um desligado. Os cem registros chegaram completos; as requisições se dividiram em 6 e 5 entre os dois endereços que funcionavam, o endereço desligado saiu de circulação depois de três erros de conexão, e o Scrapy tentou de novo as requisições com falha pelos outros endereços.

Aqui o número de ordem não é arbitrário. Na primeira tentativa colocamos o middleware em 350 e o contador de erros nunca funcionou: as exceções percorrem a cadeia em ordem inversa, e quando o middleware de novas tentativas, em 550, captura a exceção e devolve uma nova requisição, os middlewares de número menor não ficam sabendo. Um valor entre 550 e 750 (610 no exemplo) atende às duas condições: você vê o erro antes da nova tentativa, e as credenciais são processadas por HttpProxyMiddleware depois de você. Como fazer o mesmo à mão com o Requests está em Como rotacionar proxies em Python.

Como ajustar DOWNLOAD_DELAY e AutoThrottle?

O proxy muda por onde a requisição sai; não muda a carga que o servidor de destino recebe. As configurações que definem essa carga são estas:

python
# kitaplik/settings.py
USER_AGENT = "kitaplik-bot/1.0 (+https://ornek.com/bot)"
ROBOTSTXT_OBEY = True

CONCURRENT_REQUESTS = 8
CONCURRENT_REQUESTS_PER_DOMAIN = 2
DOWNLOAD_DELAY = 1
DOWNLOAD_TIMEOUT = 30

AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 2
AUTOTHROTTLE_MAX_DELAY = 30
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
AUTOTHROTTLE_DEBUG = False

RETRY_ENABLED = True
RETRY_TIMES = 2
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 408]
  • CONCURRENT_REQUESTS_PER_DOMAIN é o número de requisições que podem estar abertas ao mesmo tempo para o mesmo domínio. O padrão do framework é 8, e o modelo de projeto reduz para 1. O que é concorrência está explicado em Concorrência e paralelismo.
  • DOWNLOAD_DELAY é a espera, em segundos, entre duas requisições ao mesmo domínio. Por padrão, o Scrapy acrescenta uma variação aleatória a esse valor; as requisições não saem em intervalos exatos como um relógio.
  • AutoThrottle ajusta a espera de acordo com o tempo de resposta do servidor. Segundo o algoritmo da documentação, a espera alvo é a latência da resposta dividida por AUTOTHROTTLE_TARGET_CONCURRENCY, e a nova espera é a média entre a espera anterior e esse alvo. Respostas diferentes de 200 não podem reduzir a espera. A espera nunca fica abaixo de DOWNLOAD_DELAY nem acima de AUTOTHROTTLE_MAX_DELAY.
  • DOWNLOAD_TIMEOUT vale 180 segundos por padrão. Ao trabalhar por meio de um proxy, reduza esse valor para que uma conexão que não responde não fique pendurada por três minutos.

Com AUTOTHROTTLE_DEBUG = True, você vê uma linha por resposta. No nosso teste, a espera começou em 2.000 ms, caiu para 1.072 ms quando o servidor respondeu em 145 ms, depois para 1.000 ms, o piso definido por DOWNLOAD_DELAY, e ficou ali:

text
slot: books.toscrape.com | conc: 1 | delay: 2000 ms (+0)   | latency: 446 ms
slot: books.toscrape.com | conc: 1 | delay: 1072 ms (-927) | latency: 145 ms
slot: books.toscrape.com | conc: 1 | delay: 1000 ms (-72)  | latency: 144 ms

Com ROBOTSTXT_OBEY ativado, o Scrapy baixa primeiro o arquivo /robots.txt de cada domínio e descarta os endereços não permitidos sem pedi-los. Como ler o arquivo está em O que é robots.txt e como ler o arquivo?, e o enquadramento jurídico do rastreamento em Web scraping é legal?. Colocar em USER_AGENT um endereço pelo qual você possa ser contatado permite que o administrador do site escreva para você ao notar um problema, em vez de bloquear o seu acesso.

O que o Scrapy faz diante das respostas 429 e 503?

Por padrão, o middleware de novas tentativas repete os códigos 500, 502, 503, 504, 522, 524, 408 e 429, além dos erros de conexão; RETRY_TIMES = 2 significa três tentativas no total, contando a primeira requisição. A requisição repetida volta para a fila com prioridade menor.

Vale conhecer dois limites. A nova tentativa do Scrapy não lê o cabeçalho Retry-After e não aplica espera exponencial entre as tentativas; o intervalo continua sendo definido por DOWNLOAD_DELAY e AutoThrottle. Como o AutoThrottle não reduz a espera em respostas diferentes de 200, a velocidade não sobe sozinha durante uma onda de 429, mas também pode não cair o suficiente de forma automática. Se a proporção de 429 está crescendo no log, a reação correta é reduzir CONCURRENT_REQUESTS_PER_DOMAIN e aumentar DOWNLOAD_DELAY. Tentar ultrapassar um limite de velocidade com mais IPs não resolve o problema: aumenta a carga sobre o site. O significado dos códigos e a espera correta com Retry-After estão em Códigos de status HTTP no web scraping, e a lógica do limite de velocidade do lado do site no nosso artigo sobre 429 Too Many Requests.

Dá para usar proxy SOCKS5 no Scrapy?

Com o downloader padrão, não. No teste em que escrevemos um endereço socks5:// em meta["proxy"], a requisição esperou até o tempo limite sem receber resposta alguma. Já o segundo downloader do Scrapy, baseado em httpx, HttpxDownloadHandler, oferece suporte a SOCKS5 desde a versão 2.17. Para configurar, execute pip install "scrapy[httpx]" e acrescente isto às configurações:

python
DOWNLOAD_HANDLERS = {
    "http": "scrapy.core.downloader.handlers._httpx.HttpxDownloadHandler",
    "https": "scrapy.core.downloader.handlers._httpx.HttpxDownloadHandler",
}

Com essa configuração, um endereço socks5://user:pass@… funcionou no nosso teste, inclusive com autenticação. A documentação do Scrapy marca esse downloader como experimental e ainda não o recomenda para produção; também observa que ele abre um pool de conexões separado para cada endereço de proxy. Se não for obrigatório, fique com um proxy HTTP. As diferenças entre os protocolos estão em Diferença entre SOCKS e HTTP proxy.

O que fazer com páginas carregadas por JavaScript?

O Scrapy baixa o HTML da página; não executa JavaScript. Se os dados são carregados depois no navegador, olhe primeiro o endpoint JSON que a página chama em segundo plano; na maioria das vezes esse endereço pode ser pedido diretamente com o Scrapy. Se isso não for possível, o plugin scrapy-playwright abre em um navegador real as requisições que você escolher. Atenção: nesse plugin o proxy não é informado por meta["proxy"], e sim pelas opções de inicialização do navegador ou do contexto (context). Os detalhes estão no nosso artigo sobre Playwright com proxy, e a forma de distinguir os tipos de página em Páginas estáticas e dinâmicas.

Scrapy, BeautifulSoup ou Selenium?

Os três são partes diferentes do mesmo trabalho; por isso a comparação costuma ser montada do jeito errado.

ScrapyRequests + BeautifulSoupSelenium
O que éFramework de rastreamentoCliente HTTP + analisador de HTMLAutomação de navegador
Fila de requisições e concorrênciaProntasVocê escreveVocê escreve
Novas tentativas, limite de velocidade, robots.txtAtivados por configuraçãoVocê escreveVocê escreve
Execução de JavaScriptNão (com plugin)NãoSim
Definição do proxymeta["proxy"] ou middlewareParâmetro proxies=Opção de inicialização do navegador
Curva de aprendizadoMédiaBaixaMédia
Trabalho adequadoMuitas páginas, rastreamento recorrentePontual, poucas páginasLogin, cliques, conteúdo dinâmico

O BeautifulSoup é apenas um analisador e também pode ser usado dentro do Scrapy. O Selenium abre um navegador completo para cada página e, por isso, processa muito menos páginas no mesmo hardware; faz sentido só nas etapas que realmente precisam de um navegador. A configuração de proxy no Selenium está em Selenium com proxy, e a comparação das duas ferramentas de navegador no nosso artigo sobre Playwright e Selenium.

Casos de uso

  • Acompanhamento de preços e estoque: A mesma lista de produtos é rastreada todos os dias; a estrutura do Scrapy, própria para execuções agendadas, se encaixa nesse trabalho. O desenho está na nossa página de monitoramento de preços, e um exemplo que funciona no nosso artigo sobre o acompanhamento de preços de concorrentes no e-commerce.
  • Rastreamento interno de um site e criação de índice: A classe CrawlSpider, que percorre todas as páginas seguindo os links, é um ponto de partida pronto para trabalhos de web crawler.
  • Coleta de catálogos de vários sites: Um spider por site, um pipeline comum e uma configuração de proxy comum. A arquitetura geral está na nossa página de extração de dados.
  • Listas paginadas: Os padrões de link "próxima", número de página e cursor estão no nosso artigo sobre paginação no web scraping.

Erros comuns

  • Escrever o proxy só na primeira requisição. meta não passa para as requisições seguintes; da segunda página em diante o rastreamento continua a partir do seu próprio endereço IP, e você não percebe isso no log.
  • Escolher ao acaso o número de ordem do middleware. Com um número maior que 750, as credenciais não são separadas e a requisição falha com o erro invalid hostname; com um número menor que 550, você não vê os erros de conexão.
  • Apagar do modelo as linhas de ROBOTSTXT_OBEY, DOWNLOAD_DELAY e concorrência. Os padrões crus do framework (robots.txt desligado, sem espera, 8 requisições por domínio) são rápidos o bastante para sobrecarregar um site pequeno.
  • Continuar o rastreamento recebendo 407. Cada requisição é tentada três vezes e nenhuma dá certo; corrija primeiro as credenciais.
  • Montar um middleware de rotação por cima de um gateway rotativo. O gateway já faz esse trabalho; uma segunda camada só dificulta a depuração.
  • Deixar DOWNLOAD_TIMEOUT no padrão. Uma única conexão que não responde ocupa um slot por três minutos.
  • Escrever a senha no settings.py e enviar para o repositório. Leia o endereço de uma variável de ambiente.

Guia de decisão

NecessidadeRecomendação
Trabalho pontual de poucas páginasRequests + BeautifulSoup bastam, o Scrapy não é obrigatório
Milhares de páginas, repetição regularProjeto Scrapy, mantendo as configurações do modelo
Todo o tráfego passando pelo proxyTekProxyMiddleware ou a variável de ambiente https_proxy
Só algumas requisições por outra saídameta["proxy"] nessas requisições
Distribuir a carga entre muitos IPsGateway rotativo, um único endereço, sem middleware
Você tem uma lista fixa de proxiesProxyHavuzuMiddleware, número de ordem entre 550 e 750
O mesmo IP durante toda a sessãoProxies de sessão fixa e um único endereço
Os 429 estão aumentando no logReduza a concorrência, aumente DOWNLOAD_DELAY, ative o AutoThrottle
SOCKS5 é obrigatórioHttpxDownloadHandler (experimental)
Os dados chegam por JavaScriptPrimeiro o endpoint JSON; se não der, scrapy-playwright

Perguntas frequentes

O que é um spider do Scrapy?

Um spider é uma classe Python derivada de scrapy.Spider que define duas coisas: de quais endereços o rastreamento começa, e quais dados e quais novos links são extraídos da resposta recebida. A fila, o download e o tratamento de erros são tarefa do framework, não do spider.

É preciso saber Python para usar o Scrapy?

Sim. Um spider é uma classe Python, e você vai usar seletores, laços e dicionários. Quem sabe o básico de Python consegue escrever o primeiro spider; para a parte de middleware e pipeline, convém ter familiaridade com classes.

Como sei que o proxy está mesmo sendo usado?

Envie com o Scrapy uma requisição a um serviço que devolva o seu endereço IP em JSON e compare o endereço da resposta com o seu próprio IP. Gravar no log o valor de response.meta.get("proxy") dentro de parse também mostra qual requisição saiu por qual proxy. Os métodos gerais estão em Seu proxy está funcionando? Como testar um proxy.

O que é mais rápido, Scrapy ou Selenium?

No mesmo hardware, o Scrapy processa bem mais páginas, porque não abre navegador e envia as requisições de forma assíncrona. A velocidade só importa se a página não precisa de JavaScript; se os dados são gerados no navegador, o Scrapy sozinho não consegue vê-los.

Com o AutoThrottle ativado, DOWNLOAD_DELAY fica desnecessário?

Não, é esse valor que define o piso. O AutoThrottle nunca leva a espera abaixo de DOWNLOAD_DELAY. Em um servidor que responde rápido, a espera cai até esse piso e fica ali; por isso, confiar no AutoThrottle com DOWNLOAD_DELAY = 0 equivale a enviar requisições quase sem espera a servidores rápidos.

Usar um IP diferente em cada requisição evita bloqueios?

Sozinho, não. Os sites medem a velocidade não só por IP, mas também por sessão, cookie e comportamento. A rotação distribui a carga entre muitas saídas; ela ganha sentido junto com o respeito ao robots.txt, a concorrência baixa e um User-Agent honesto. Reunimos os métodos legítimos em Como fazer web scraping sem ser bloqueado.

Em resumo

No Scrapy, o proxy é o campo meta["proxy"] da requisição, e você pode preenchê-lo de três formas: à mão em cada requisição, com uma variável de ambiente ou com um downloader middleware. Para que todo o tráfego, inclusive o robots.txt, saia pela mesma saída, é preciso uma das duas últimas. Com um gateway rotativo, um único endereço basta; se você tem uma lista fixa, um middleware numerado entre 550 e 750 assume a distribuição e a contagem de erros. Quem define a velocidade não é o proxy, e sim DOWNLOAD_DELAY, a concorrência por domínio e o AutoThrottle; mantenha as configurações cautelosas que o modelo traz. Você encontra os tipos de proxy adequados aos seus rastreamentos entre nossos serviços de proxy.