Proteção contra bloqueios ao fazer solicitações em massa: técnicas e ferramentas
O bloqueio de contas e endereços IP é o principal problema ao fazer scraping, automação e operações em massa em redes sociais. Sistemas modernos anti-bot analisam dezenas de parâmetros: desde a frequência de solicitações até impressões digitais do navegador. Neste guia, analisaremos os mecanismos específicos de detecção de automação e maneiras práticas de contorná-los.
Mecanismos de detecção de automação
Sistemas modernos de proteção utilizam análise em múltiplos níveis para identificar bots. Compreender esses mecanismos é criticamente importante para escolher a estratégia correta de contorno.
Principais parâmetros de análise
Reputação do IP: Sistemas anti-bot verificam o histórico do endereço IP, pertencente a data centers, presença em listas negras. IPs de pools de proxies conhecidos são bloqueados com mais frequência.
Frequência de solicitações (Request Rate): Um humano fisicamente não pode enviar 100 solicitações por minuto. Os sistemas analisam não apenas o número total, mas também a distribuição ao longo do tempo — intervalos uniformes entre solicitações indicam um bot.
Padrões de comportamento: Sequência de ações, profundidade de rolagem, movimentos do mouse, tempo na página. Um bot que navega instantaneamente por links sem atrasos é facilmente reconhecido.
Impressões digitais técnicas: User-Agent, cabeçalhos HTTP, ordem dos cabeçalhos, impressão digital TLS, fingerprinting Canvas/WebGL. Inconsistências nesses parâmetros são um sinal vermelho para sistemas anti-bot.
| Parâmetro | O que é analisado | Risco de detecção |
|---|---|---|
| Endereço IP | Reputação, ASN, geolocalização | Alto |
| User-Agent | Versão do navegador, SO, dispositivo | Médio |
| Impressão digital TLS | Conjunto de cifras, extensões | Alto |
| Impressão digital HTTP/2 | Ordem dos cabeçalhos, configurações | Alto |
| Canvas/WebGL | Renderização gráfica | Médio |
| Comportamento | Cliques, rolagem, tempo | Alto |
Limitação de taxa e controle de frequência de solicitações
O controle da velocidade de envio de solicitações é a primeira linha de defesa contra bloqueios. Mesmo com rotação de proxies, um scraping muito agressivo levará ao banimento.
Atrasos dinâmicos
Intervalos fixos (por exemplo, exatamente 2 segundos entre solicitações) são facilmente reconhecíveis. Use atrasos aleatórios com distribuição normal:
import time
import random
import numpy as np
def human_delay(min_delay=1.5, max_delay=4.0, mean=2.5, std=0.8):
"""
Geração de atraso com distribuição normal
imitando o comportamento humano
"""
delay = np.random.normal(mean, std)
# Limitando o intervalo
delay = max(min_delay, min(delay, max_delay))
# Adicionando micro-atrasos para realismo
delay += random.uniform(0, 0.3)
time.sleep(delay)
# Uso
for url in urls:
response = session.get(url)
human_delay(min_delay=2, max_delay=5, mean=3, std=1)
Limitação de taxa adaptativa
Uma abordagem mais avançada é adaptar a velocidade com base nas respostas do servidor. Se você receber códigos 429 (Too Many Requests) ou 503, reduza automaticamente a velocidade:
class AdaptiveRateLimiter:
def __init__(self, initial_delay=2.0):
self.current_delay = initial_delay
self.min_delay = 1.0
self.max_delay = 30.0
self.error_count = 0
def wait(self):
time.sleep(self.current_delay + random.uniform(0, 0.5))
def on_success(self):
# Aceleramos gradualmente em solicitações bem-sucedidas
self.current_delay = max(
self.min_delay,
self.current_delay * 0.95
)
self.error_count = 0
def on_rate_limit(self):
# Diminuímos drasticamente em caso de bloqueio
self.error_count += 1
self.current_delay = min(
self.max_delay,
self.current_delay * (1.5 + self.error_count * 0.5)
)
print(f"Limite de taxa atingido. Novo atraso: {self.current_delay:.2f}s")
# Aplicação
limiter = AdaptiveRateLimiter(initial_delay=2.0)
for url in urls:
limiter.wait()
response = session.get(url)
if response.status_code == 429:
limiter.on_rate_limit()
time.sleep(60) # Pausa antes de repetir
elif response.status_code == 200:
limiter.on_success()
else:
# Tratamento de outros erros
pass
Dica prática: Para diferentes sites, a velocidade ideal varia. Grandes plataformas (Google, Facebook) toleram de 5 a 10 solicitações por minuto de um único IP. Sites menores podem bloquear já com 20 a 30 solicitações por hora. Sempre comece de forma conservadora e aumente gradualmente a carga, monitorando a porcentagem de erros.
Rotação de proxies e gerenciamento de endereços IP
Usar um único endereço IP para solicitações em massa garante bloqueio. A rotação de proxies distribui a carga e reduz o risco de detecção.
Estratégias de rotação
1. Rotação por solicitações: Trocar IP após cada ou a cada N solicitações. Adequado para scraping de motores de busca, onde a anonimidade de cada solicitação é importante.
2. Rotação por tempo: Trocar IP a cada 5-15 minutos. Eficaz para trabalhar com redes sociais, onde a estabilidade da sessão é importante.
3. Sessões fixas: Usar um único IP para toda a sessão do usuário (autenticação, sequência de ações). Crítico para sites com proteção contra CSRF.
import requests
from itertools import cycle
class ProxyRotator:
def __init__(self, proxy_list, rotation_type='request', rotation_interval=10):
"""
rotation_type: 'request' (cada solicitação) ou 'time' (por tempo)
rotation_interval: número de solicitações ou segundos
"""
self.proxies = cycle(proxy_list)
self.current_proxy = next(self.proxies)
self.rotation_type = rotation_type
self.rotation_interval = rotation_interval
self.request_count = 0
self.last_rotation = time.time()
def get_proxy(self):
if self.rotation_type == 'request':
self.request_count += 1
if self.request_count >= self.rotation_interval:
self.current_proxy = next(self.proxies)
self.request_count = 0
print(f"Rotacionado para: {self.current_proxy}")
elif self.rotation_type == 'time':
if time.time() - self.last_rotation >= self.rotation_interval:
self.current_proxy = next(self.proxies)
self.last_rotation = time.time()
print(f"Rotacionado para: {self.current_proxy}")
return {'http': self.current_proxy, 'https': self.current_proxy}
# Exemplo de uso
proxy_list = [
'http://user:pass@proxy1.example.com:8000',
'http://user:pass@proxy2.example.com:8000',
'http://user:pass@proxy3.example.com:8000',
]
rotator = ProxyRotator(proxy_list, rotation_type='request', rotation_interval=5)
for url in urls:
proxies = rotator.get_proxy()
response = requests.get(url, proxies=proxies, timeout=10)
Escolha do tipo de proxy
| Tipo de proxy | Nível de confiança | Velocidade | Aplicação |
|---|---|---|---|
| Data centers | Baixo | Alta | Scraping simples, API |
| Residenciais | Alto | Média | Redes sociais, sites protegidos |
| Móveis | Muito alto | Média | Instagram, TikTok, antifraude |
Para operações em massa em redes sociais e plataformas com proteção rigorosa, use proxies residenciais. Eles se parecem com conexões domésticas normais e raramente entram em listas negras. Data centers são adequados para recursos menos protegidos, onde a velocidade é importante.
Fingerprinting de navegador e impressões digitais TLS
Mesmo com a rotação de IP, você pode ser identificado por impressões digitais técnicas do navegador e da conexão TLS. Esses parâmetros são únicos para cada cliente e difíceis de falsificar.
Impressão digital TLS
Ao estabelecer uma conexão HTTPS, o cliente envia um ClientHello com um conjunto de cifras e extensões suportadas. Essa combinação é única para cada biblioteca. Por exemplo, o Python requests usa OpenSSL, cuja impressão digital é facilmente distinguível do Chrome.
Problema: Bibliotecas padrão (requests, urllib, curl) têm impressões digitais diferentes dos navegadores reais. Serviços como Cloudflare, Akamai, DataDome usam ativamente fingerprinting TLS para bloquear bots.
Solução: Use bibliotecas que imitam impressões digitais TLS de navegadores. Para Python, isso inclui curl_cffi, tls_client ou playwright/puppeteer para emulação completa do navegador.
# Instalação: pip install curl-cffi
from curl_cffi import requests
# Imitação do Chrome 110
response = requests.get(
'https://example.com',
impersonate="chrome110",
proxies={'https': 'http://proxy:port'}
)
# Alternativa: tls_client
import tls_client
session = tls_client.Session(
client_identifier="chrome_108",
random_tls_extension_order=True
)
response = session.get('https://example.com')
Impressão digital HTTP/2
Além do TLS, sistemas anti-bot analisam parâmetros HTTP/2: ordem dos cabeçalhos, configurações do quadro SETTINGS, prioridades de fluxo. Bibliotecas padrão não respeitam a ordem exata dos cabeçalhos do Chrome ou Firefox.
# Ordem correta dos cabeçalhos para Chrome
headers = {
':method': 'GET',
':authority': 'example.com',
':scheme': 'https',
':path': '/',
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
'accept': 'text/html,application/xhtml+xml...',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
}
Fingerprinting Canvas e WebGL
Os navegadores renderizam gráficos de maneira diferente, dependendo da GPU, drivers e SO. Sites usam isso para criar uma impressão digital única do dispositivo. Ao usar navegadores headless (Selenium, Puppeteer), é importante mascarar sinais de automação:
// Puppeteer: ocultando o modo headless
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
headless: true,
args: [
'--disable-blink-features=AutomationControlled',
'--no-sandbox',
'--disable-setuid-sandbox',
`--proxy-server=${proxyUrl}`
]
});
const page = await browser.newPage();
// Sobrescrevendo navigator.webdriver
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
});
});
Cabeçalhos, cookies e gerenciamento de sessões
O trabalho correto com cabeçalhos HTTP e cookies é crítico para imitar um usuário real. Erros nesses parâmetros são uma causa comum de bloqueios.
Cabeçalhos obrigatórios
O conjunto mínimo de cabeçalhos para imitar o navegador Chrome:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Cache-Control': 'max-age=0',
}
session = requests.Session()
session.headers.update(headers)
Gerenciamento de cookies
Muitos sites definem cookies de rastreamento na primeira visita e verificam sua presença em solicitações subsequentes. A ausência de cookies ou sua inconsistência é um sinal de bot.
import requests
import pickle
class SessionManager:
def __init__(self, session_file='session.pkl'):
self.session_file = session_file
self.session = requests.Session()
self.load_session()
def load_session(self):
"""Carregando sessão salva"""
try:
with open(self.session_file, 'rb') as f:
cookies = pickle.load(f)
self.session.cookies.update(cookies)
except FileNotFoundError:
pass
def save_session(self):
"""Salvando cookies para reutilização"""
with open(self.session_file, 'wb') as f:
pickle.dump(self.session.cookies, f)
def request(self, url, **kwargs):
response = self.session.get(url, **kwargs)
self.save_session()
return response
# Uso
manager = SessionManager('instagram_session.pkl')
response = manager.request('https://www.instagram.com/explore/')
Importante: Ao rotacionar proxies, não se esqueça de redefinir cookies se eles estiverem vinculados a um IP específico. Inconsistência entre IP e cookies (por exemplo, cookies com geolocalização dos EUA e IP da Alemanha) levantará suspeitas.
Referer e Origin
Os cabeçalhos Referer e Origin mostram de onde o usuário veio. Sua ausência ou valores incorretos são um sinal vermelho.
# Sequência correta: página inicial → categoria → produto
session = requests.Session()
# Passo 1: acessar a página inicial
response = session.get('https://example.com/')
# Passo 2: ir para a categoria
response = session.get(
'https://example.com/category/electronics',
headers={'Referer': 'https://example.com/'}
)
# Passo 3: visualizar o produto
response = session.get(
'https://example.com/product/12345',
headers={'Referer': 'https://example.com/category/electronics'}
)
Simulação de comportamento humano
Parâmetros técnicos são apenas metade da questão. Sistemas modernos anti-bot analisam padrões comportamentais: como o usuário interage com a página, quanto tempo passa, como move o mouse.
Rolagem e movimento do mouse
Ao usar Selenium ou Puppeteer, adicione movimentos aleatórios do mouse e rolagem da página:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import random
import time
def human_like_mouse_move(driver):
"""Movimento aleatório do mouse pela página"""
action = ActionChains(driver)
for _ in range(random.randint(3, 7)):
x = random.randint(0, 1000)
y = random.randint(0, 800)
action.move_by_offset(x, y)
action.pause(random.uniform(0.1, 0.3))
action.perform()
def human_like_scroll(driver):
"""Imitação de rolagem natural"""
total_height = driver.execute_script("return document.body.scrollHeight")
current_position = 0
while current_position < total_height:
# Passo de rolagem aleatório
scroll_step = random.randint(100, 400)
current_position += scroll_step
driver.execute_script(f"window.scrollTo(0, {current_position});")
# Pausa com variação
time.sleep(random.uniform(0.5, 1.5))
# Às vezes, role um pouco para trás (como as pessoas fazem)
if random.random() < 0.2:
back_scroll = random.randint(50, 150)
current_position -= back_scroll
driver.execute_script(f"window.scrollTo(0, {current_position});")
time.sleep(random.uniform(0.3, 0.8))
# Uso
driver = webdriver.Chrome()
driver.get('https://example.com')
human_like_mouse_move(driver)
time.sleep(random.uniform(2, 4))
human_like_scroll(driver)
Tempo na página
Usuários reais passam tempo na página: leem conteúdo, observam imagens. Um bot que navega instantaneamente por links é facilmente reconhecido.
def realistic_page_view(driver, url, min_time=5, max_time=15):
"""
Visualização realista da página com atividade
"""
driver.get(url)
# Atraso inicial (carregamento e "leitura")
time.sleep(random.uniform(2, 4))
# Rolagem
human_like_scroll(driver)
# Atividade adicional
total_time = random.uniform(min_time, max_time)
elapsed = 0
while elapsed < total_time:
action_choice = random.choice(['scroll', 'mouse_move', 'pause'])
if action_choice == 'scroll':
# Pequena rolagem para cima/baixo
scroll_amount = random.randint(-200, 300)
driver.execute_script(f"window.scrollBy(0, {scroll_amount});")
pause = random.uniform(1, 3)
elif action_choice == 'mouse_move':
human_like_mouse_move(driver)
pause = random.uniform(0.5, 2)
else: # pause
pause = random.uniform(2, 5)
time.sleep(pause)
elapsed += pause
Padrões de navegação
Evite padrões suspeitos: transições diretas para páginas profundas, ignorando a página inicial, percorrendo sequencialmente todos os elementos sem saltos.
Boas práticas:
- Comece pela página inicial ou seções populares
- Use a navegação interna do site, não URLs diretas
- Às vezes, volte ou vá para outras seções
- Varie a profundidade de visualização: nem sempre vá até o final
- Adicione "erros": transições por links inexistentes, retornos
Contornando Cloudflare, DataDome e outras proteções
Sistemas anti-bot especializados exigem uma abordagem abrangente. Eles utilizam desafios JavaScript, CAPTCHA, análise de comportamento em tempo real.
Cloudflare
Cloudflare utiliza vários níveis de proteção: Verificação de Integridade do Navegador, Desafio JavaScript, CAPTCHA. Para contornar a proteção básica, basta uma impressão digital TLS correta e a execução de JavaScript:
# Opção 1: cloudscraper (solução automática para desafios JS)
import cloudscraper
scraper = cloudscraper.create_scraper(
browser={
'browser': 'chrome',
'platform': 'windows',
'desktop': True
}
)
response = scraper.get('https://protected-site.com')
# Opção 2: undetected-chromedriver (para casos complexos)
import undetected_chromedriver as uc
options = uc.ChromeOptions()
options.add_argument('--proxy-server=http://proxy:port')
driver = uc.Chrome(options=options)
driver.get('https://protected-site.com')
# Aguardando a conclusão do desafio
time.sleep(5)
# Obtendo cookies para requests
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
session.cookies.set(cookie['name'], cookie['value'])
DataDome
DataDome analisa o comportamento do usuário em tempo real: movimentos do mouse, estilo de digitação, tempos. Para contornar, é necessário um navegador completo com simulação de atividade:
from playwright.sync_api import sync_playwright
import random
def bypass_datadome(url, proxy=None):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # DataDome detecta headless
proxy={'server': proxy} if proxy else None
)
context = browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
)
page = context.new_page()
# Injeção de scripts para mascarar automação
page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => false});
window.chrome = {runtime: {}};
""")
page.goto(url)
# Simulação de comportamento humano
time.sleep(random.uniform(2, 4))
# Movimentos aleatórios do mouse
for _ in range(random.randint(5, 10)):
page.mouse.move(
random.randint(100, 1800),
random.randint(100, 1000)
)
time.sleep(random.uniform(0.1, 0.3))
# Rolagem
page.evaluate(f"window.scrollTo(0, {random.randint(300, 800)})")
time.sleep(random.uniform(1, 2))
content = page.content()
browser.close()
return content
CAPTCHA
Para resolver CAPTCHA automaticamente, use serviços de reconhecimento (2captcha, Anti-Captcha) ou estratégias de evasão:
- Reduza a frequência de solicitações para um nível que não acione CAPTCHA
- Use IPs residenciais limpos com boa reputação
- Trabalhe através de contas autorizadas (elas têm um limite de CAPTCHA mais alto)
- Distribua a carga ao longo do tempo (evite horários de pico)
Monitoramento e tratamento de bloqueios
Mesmo com as melhores práticas, bloqueios são inevitáveis. É importante detectá-los rapidamente e tratá-los corretamente.
Indicadores de bloqueio
| Sinal | Descrição | Ação |
|---|---|---|
| HTTP 429 | Too Many Requests | Aumentar atrasos, trocar IP |
| HTTP 403 | Forbidden (banimento de IP) | Trocar proxy, verificar fingerprint |
| CAPTCHA | Verificação necessária | Resolver ou reduzir a atividade |
| Resposta vazia | Conteúdo não carregado | Verificar JavaScript, cookies |
| Redirecionamento para /blocked | Bloqueio explícito | Mudança completa de estratégia |
Sistema de tentativas
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_session_with_retries():
"""
Sessão com repetições automáticas e tratamento de erros
"""
session = requests.Session()
retry_strategy = Retry(
total=5,
backoff_factor=2, # 2, 4, 8, 16, 32 segundos
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
def safe_request(url, session, max_attempts=3):
"""
Solicitação com tratamento de bloqueios
"""
for attempt in range(max_attempts):
try:
response = session.get(url, timeout=15)
# Verificação de bloqueio
if response.status_code == 403:
print(f"IP bloqueado. Rotacionando proxy...")
# Lógica de troca de proxy
continue
elif response.status_code == 429:
wait_time = int(response.headers.get('Retry-After', 60))
print(f"Limite de taxa. Aguardando {wait_time}s...")
time.sleep(wait_time)
continue
elif 'captcha' in response.text.lower():
print("CAPTCHA detectado")
# Lógica de resolução de CAPTCHA ou bypass
return None
return response
except requests.exceptions.Timeout:
print(f"Timeout na tentativa {attempt + 1}")
time.sleep(5 * (attempt + 1))
except requests.exceptions.ProxyError:
print("Erro de proxy. Rotacionando...")
# Troca de proxy
continue
return None
Registro e análise
Monitore métricas para otimizar a estratégia:
import logging
from collections import defaultdict
from datetime import datetime
class ScraperMetrics:
def __init__(self):
self.stats = {
'total_requests': 0,
'successful': 0,
'rate_limited': 0,
'blocked': 0,
'captcha': 0,
'errors': 0,
'proxy_failures': defaultdict(int)
}
def log_request(self, status, proxy=None):
self.stats['total_requests'] += 1
if status == 200:
self.stats['successful'] += 1
elif status == 429:
self.stats['rate_limited'] += 1
elif status == 403:
self.stats['blocked'] += 1
if proxy:
self.stats['proxy_failures'][proxy] += 1
def get_success_rate(self):
if self.stats['total_requests'] == 0:
return 0
return (self.stats['successful'] / self.stats['total_requests']) * 100
def print_report(self):
print(f"\n=== Relatório de Scraping ===")
print(f"Total de solicitações: {self.stats['total_requests']}")
print(f"Taxa de sucesso: {self.get_success_rate():.2f}%")
print(f"Limite de taxa: {self.stats['rate_limited']}")
print(f"Bloqueados: {self.stats['blocked']}")
print(f"CAPTCHA: {self.stats['captcha']}")
if self.stats['proxy_failures']:
print(f"\nProxies problemáticos:")
for proxy, count in sorted(
self.stats['proxy_failures'].items(),
key=lambda x: x[1],
reverse=True
)[:5]:
print(f" {proxy}: {count} falhas")
# Uso
metrics = ScraperMetrics()
for url in urls:
response = safe_request(url, session)
if response:
metrics.log_request(response.status_code, current_proxy)
metrics.print_report()
Indicadores ideais: Taxa de sucesso acima de 95% é um ótimo resultado. 80-95% é aceitável, mas há espaço para melhorias. Abaixo de 80% — revise sua estratégia: pode ser limitação de taxa muito agressiva, proxies ruins ou problemas com fingerprinting.
Conclusão
A proteção contra bloqueios ao fazer solicitações em massa é um desafio contínuo que exige uma combinação de técnicas e ferramentas. Ao entender os mecanismos de detecção e implementar estratégias eficazes, você pode minimizar o risco de bloqueios e garantir uma experiência de scraping mais suave.