A falha de scraping que todo mundo planeja é a óbvia: um 403, um 429, uma conexão que expira. Você consegue ver isso, contar isso e tentar de novo. A falha que realmente envenena seu conjunto de dados é o oposto: um 200 OK que parece completamente bem-sucedido e não contém nada do que você queria. Uma página de bloqueio disfarçada de resposta normal. Uma tela de desafio. Uma casca vazia. Uma página de dados fabricada especificamente para alimentar um bot com informações erradas.
Um scraper que retorna erro é chato. Um scraper que “tem sucesso” com lixo é perigoso, porque você só descobre isso depois que os dados ruins já estão no seu data warehouse, em um relatório ou em um modelo. Esta é a outra metade de por que scrapers são bloqueados: sistemas anti-bot modernos preferem cada vez mais enganar silenciosamente em vez de rejeitar abertamente, justamente porque uma falha silenciosa vale mais para eles do que uma falha visível. Veja como identificar isso.
O código de status não é a verdade
O primeiro hábito a abandonar é confiar em códigos de status HTTP como sinal de sucesso. Um 200 significa que o servidor enviou uma resposta, não que enviou a resposta que você pediu. Sites rotineiramente retornam páginas de bloqueio, CAPTCHAs e barreiras de consentimento com um 200, porque isso mantém scrapers ingênuos satisfeitos e quietos enquanto não entrega nada a eles. Trate o código de status como um sinal fraco e valide o corpo em cada requisição.
Vale nomear as categorias de 200 enganosos, porque cada uma tem um sinal diferente:
- Bloqueios suaves e interstitials. Uma página de desafio ou de “verifique que você é humano” retornada com um
200, geralmente muito menor que uma página real. - Telas de desafio e CAPTCHA. Servidas exatamente onde o conteúdo deveria estar. Relacionado a como as defesas anti-bot evoluíram.
- Conteúdo degradado ou removido. Uma barreira de login, um stub de “ative o JavaScript” ou um esqueleto vazio onde os dados deveriam ser renderizados.
- Falhas suaves de limite de taxa. Resultados obsoletos, em cache ou vazios retornados em vez de um erro assim que você cruza um limite.
- Geolocalização ou personalização erradas. A página certa para o país, moeda ou estado deslogado errado, tecnicamente válida, silenciosamente inútil.
- Honeypots e dados envenenados. Conteúdo servido deliberadamente para bots: preços falsos, links armadilha ou registros com aparência plausível mas valores impossíveis.
Valide o conteúdo, não apenas a entrega
A defesa mais eficaz é uma verificação de conteúdo em cada resposta: uma checagem barata de que a página contém o que uma página real deve conter. Escolha um invariante que um resultado genuíno sempre tem e uma página de bloqueio nunca tem, um elemento específico, um campo obrigatório, um comprimento mínimo plausível, e falhe a requisição se estiver ausente.
def is_valid_product_page(html, parsed): # Uma página de produto real sempre tem isso. Uma página de bloqueio não tem nenhum deles. if len(html) < 2000: # páginas de bloqueio geralmente são minúsculas return False if parsed.select_one("h1.product-title") is None: return False # o elemento âncora sumiu if parsed.select_one('[data-price]') is None: return False # o campo que viemos buscar está faltando return TrueA mudança fundamental é que um elemento âncora ausente é uma falha, não um resultado vazio. Se o seletor no qual você confia está ausente, não registre uma linha em branco e siga em frente, trate a resposta como um bloqueio suave e tente novamente com uma identidade nova, da mesma forma que trataria um 403. Registrar linhas em branco é como um bloqueio silenciosamente se transforma em milhares de linhas vazias que ninguém percebe até muito depois.
Observe a forma das suas respostas, não apenas as individuais
Verificações individuais capturam lixo óbvio. Verificações distribucionais capturam a deriva sutil, e são elas que separam um pipeline robusto de um frágil.
- Tamanho da resposta. Páginas de bloqueio e desafio tendem a ser pequenas e uniformes. Um colapso repentino no tamanho médio das páginas em um lote, ou um pico de respostas concentradas em uma contagem exata de bytes, é uma assinatura de bloqueio mesmo que cada uma retorne
200. - Hashing de resposta. Faça o hash de uma versão normalizada de cada corpo de resposta. Quando o mesmo hash de repente se repete em muitas URLs diferentes, você está recebendo a mesma página de bloqueio repetidamente, não conteúdo real e variado.
- Taxa de preenchimento de campo. Acompanhe a porcentagem de registros em que cada campo realmente foi preenchido. Um campo que estava 98 por cento preenchido ontem e 4 por cento preenchido hoje não ficou menos comum, você começou a receber páginas descaracterizadas.
- Taxa de sucesso por host. Uma queda em um alvo específico, enquanto outros se mantêm estáveis, é esse alvo mudando sua postura em relação a você, vale a pena identificar isso antes que desperdice uma execução inteira. Este é o mesmo sinal por alvo que importa ao monitorar um pipeline de scraping em escala.
Nenhum desses precisa de machine learning. São contadores e baselines simples, e fazem a diferença entre perceber um bloqueio nas primeiras cem requisições e perceber depois de um milhão.
Assinaturas conhecidas de páginas de bloqueio
Mantenha uma pequena biblioteca de frases e marcadores que só aparecem em páginas de bloqueio, desafio ou erro, e sinalize qualquer resposta que os contenha, independentemente do código de status.
BLOCK_MARKERS = ( "verify you are human", "unusual traffic", "access denied", "enable javascript to continue", "request blocked",)
def looks_blocked(text): low = text.lower() return any(marker in low for marker in BLOCK_MARKERS)Mantenha essa lista curta e específica para que não gere falsos positivos em conteúdo real que por acaso discuta esses tópicos, e amplie-a conforme você encontrar novas defesas. Um desafio que você consegue reconhecer é um desafio que você pode tentar novamente em vez de armazenar.
Honeypots e dados envenenados
A categoria mais desagradável é o conteúdo projetado para parecer real. Duas defesas importam aqui.
Primeiro, não siga links armadilha. Links honeypot são comumente escondidos de humanos com display:none, visibility:hidden, tamanho zero, posicionamento fora da tela ou aria-hidden, e existem apenas para capturar um bot que segue todas as âncoras. Um crawler que respeita a visibilidade, e ignora links que um humano jamais clicaria, evita a maioria deles.
Segundo, faça uma verificação de sanidade nos próprios dados. Registros envenenados são construídos para passar por um parser ingênuo, mas tendem a falhar em regras básicas de domínio: preços que são zero ou absurdamente altos, datas no futuro, quantidades que não podem existir, valores fora de qualquer faixa plausível. Valide contra as faixas que seu domínio realmente permite, e coloque em quarentena os registros que as violam em vez de confiar neles só porque o HTML foi analisado corretamente.
Requisições canário
O alerta precoce mais confiável é um canário: buscar periodicamente uma página cujo conteúdo correto você já conhece, e verificar se ela ainda corresponde. Quando seu canário começa a retornar uma página de bloqueio ou dados errados, você sabe que o alvo mudou de postura, de forma imediata e inequívoca, sem precisar inferir isso a partir de um declínio lento na qualidade dos dados. Execute canários por alvo e por geolocalização, já que um site pode bloquear os IPs de saída de um país enquanto deixa outro intocado.
Onde os proxies se encaixam
IPs limpos reduzem a frequência com que você é bloqueado de forma suave, para começar, um pool com boa reputação passa direto onde endereços sinalizados recebem silenciosamente uma página de desafio. Isso reduz a taxa de respostas enganosas que você precisa detectar, mas não elimina a necessidade de detectá-las, porque os bloqueios são cada vez mais invisíveis por design e nenhum IP é imune. Os dois trabalham juntos: a detecção informa que uma resposta foi um bloqueio suave, e um pool residencial rotativo dá a você uma identidade nova para tentar novamente, exatamente como você trataria uma falha grave. Alimente cada bloqueio suave detectado de volta na sua lógica de retentativa e rotação (sticky vs rotativo), e trate um pico persistente na taxa de bloqueio por alvo como um sinal para recuar em vez de insistir (evitando bloqueios e fazendo scraping de forma responsável se aplicam aqui).
Conclusão
Assuma que a resposta está mentindo até que prove o contrário. Valide o conteúdo em cada requisição contra um invariante que uma página real sempre tem, e trate um elemento âncora ausente como uma falha a ser tentada novamente, não como uma linha vazia a ser armazenada. Observe a distribuição de tamanhos de resposta, hashes e taxas de preenchimento para que um bloqueio que retorna 200 ainda apareça como uma anomalia. Mantenha uma lista curta de assinaturas para páginas de bloqueio conhecidas, recuse-se a seguir links armadilha ocultos, faça verificações de sanidade nos dados contra regras de domínio, e execute canários para saber no instante em que um alvo se volta contra você.
Faça isso, e a falha silenciosa, aquela que corrompe silenciosamente um conjunto de dados por semanas, deixa de ser silenciosa. Combine isso com um pool residencial limpo para ser bloqueado de forma suave com menos frequência desde o início, e os planos por GB permitem validar isso contra seus próprios alvos sem compromisso prévio.