Como não ser bloqueado no scraping: um guia prático com código
Ser bloqueado durante o scraping geralmente significa uma destas três coisas: você enviou muitas requisições rápido demais, seu cliente não se parecia com um navegador nem com um bot conhecido, ou seu endereço IP está em uma lista que o site desconfia. Você pode reduzir os três com alguns hábitos: perceber o bloqueio cedo, desacelerar quando o site reclama, manter uma identidade consistente e honesta e usar proxies apenas quando for apropriado. Este guia percorre cada um com código funcional. Use-o para sites que você tem permissão para raspar.
Reconheça um bloqueio
Um bloqueio nem sempre parece uma página de erro. Fique atento a:
- 403 Forbidden, que muitas vezes significa que o site recusou o seu cliente. Pare e verifique se você tem permissão para estar ali antes de tentar de novo.
- 429 Too Many Requests, que significa que você está enviando demais. Costuma vir com um cabeçalho
Retry-After. - 503 Service Unavailable, que pode significar que o site está sobrecarregado. Recue.
- Uma resposta 200 com quase nenhum conteúdo, um redirecionamento para uma página de login ou de desafio, ou uma página sem os dados que você esperava.
Registre o código de status e uma pequena amostra do corpo para cada falha. Um bloqueio que você não consegue ver nos logs é um bloqueio que você não consegue corrigir.
Recue com novas tentativas
Quando o site diz "diminua o ritmo", o seu código deve desacelerar mais do que um pouco. Backoff exponencial, com um limite de tentativas, é a abordagem padrão. Ele também respeita o Retry-After quando o site o envia:
import time
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "ExampleBot/1.0 (+https://example.com/bot)",
"Accept-Language": "en-US,en;q=0.9",
})
def polite_get(url: str, max_attempts: int = 4) -> requests.Response:
delay = 2.0
resp = None
for _ in range(max_attempts):
resp = session.get(url, timeout=30)
if resp.status_code not in (429, 503):
return resp
retry_after = resp.headers.get("Retry-After", "")
wait = float(retry_after) if retry_after.isdigit() else delay
time.sleep(wait)
delay *= 2
return resp # ainda bloqueado: devolve a última resposta para quem chamou tratar
resp = polite_get("https://example.com/products?page=1")
print(resp.status_code, len(resp.text))
Observe o que a função não faz. Ela não tenta de novo um 403, e não troca de identidade para passar por um bloqueio. Um 403 é uma decisão de parar, e não um quebra-cabeça para resolver.
Diagnostique um bloqueio passo a passo
Quando uma execução começa a falhar, percorra estas verificações antes de mudar qualquer outra coisa:
- Olhe a primeira URL que falhou e o código de status. Um bloqueio geralmente começa em um ponto específico, e não aleatoriamente.
- Compare a taxa de requisições nos minutos antes da primeira falha. Uma rajada é a causa mais comum.
- Verifique se a falha está ligada a um único caminho, a uma seção ou a um horário do dia.
- Acesse a mesma URL manualmente, uma vez, em um navegador normal. Se o navegador carregar a página, a diferença está no seu padrão de requisições. Se o navegador também estiver bloqueado, o site parou de servir a página para você por enquanto.
- Só então mude o código. Desacelere primeiro, adicione backoff em segundo lugar e revise a identidade por último.
Mudar várias coisas de uma vez torna impossível saber o que ajudou.
Mantenha uma identidade consistente e honesta
Scrapers são bloqueados quando a identidade muda entre as requisições ou quando não combina com o restante do tráfego. Uma única sessão com um User-Agent claro é a identidade estável mais simples. Identifique seu bot, adicione um link de contato e envie apenas os cabeçalhos que um cliente normal enviaria. Trocar User-Agents para parecer muitos visitantes diferentes é uma forma de evasão, e tende a tornar o bloqueio mais provável, e não menos.
Se um site entrega conteúdo diferente para navegadores do que para clientes HTTP simples, use um navegador real para essas páginas e mantenha o mesmo limite de taxa. Esse é um caminho legítimo, e mantém o padrão de requisições sensato.
Use proxies com cuidado
Um proxy muda o endereço de onde uma requisição sai. É uma ferramenta para necessidades legítimas, como testar a partir de uma região específica em um site que você tem permissão para testar, ou distribuir a carga pela sua própria infraestrutura. Não é uma forma de escapar de um limite que o site definiu para você. O formato de um proxy é o usado pelos tipos de tarefa de proxy:
proxies = {
"http": "http://user:pass@proxy.example.net:8000",
"https": "http://user:pass@proxy.example.net:8000",
}
resp = session.get("https://example.com/products", proxies=proxies, timeout=30)
A escolha do tipo de proxy é uma questão separada, tratada em proxies residenciais vs datacenter e em proxies rotativos para scraping.
Quando o bloqueio vira captcha
Alguns sites respondem a um problema de taxa com um captcha em vez de um bloqueio direto. A ordem sensata é: pare, desacelere, revise as regras de novo e só então considere uma resolução. Para widgets PopularCaptcha, a API ativa da Scarnox devolve um token que você envia no campo h-captcha-response. Para outros tipos de captcha, confira a lista atual, já que o suporte ao reCAPTCHA, ao Cloudflare Turnstile, ao GeeTest, ao AWS WAF e ao Arkose está em breve. O guia detalhado é web scraping e captchas.
Se você começar a ver bloqueios em um site, a causa quase sempre é o padrão de requisições. Como evitar captchas no scraping cobre os hábitos que previnem o problema.
Faça scraping com responsabilidade
- Verifique o
robots.txte os termos do site antes de começar, e siga os caminhos e atrasos que eles pedem. - Pare quando receber um 403 ou uma mensagem clara de "não faça scraping". Não continue tentando.
- Mantenha a sua taxa baixa o suficiente para que um visitante normal não perceba o seu tráfego.
- Não colete dados pessoais de que você não tem motivo para guardar, e trate os que coletar sob as regras de privacidade que se aplicam a você.
- Se você precisa de muito mais volume do que um site atende com conforto, peça ao dono um feed ou uma API.