Aviso legal: Este artigo aborda apenas o acesso a dados públicos disponíveis sem autenticação. Raspar dados do Twitter/X pode violar seus Termos de Serviço. Consulte um advogado antes de coletar dados em escala. Respeite o robots.txt, a Lei CFAA (EUA), o RGPD (UE) e leis locais. Considere sempre a API oficial quando disponível.
Por que Desenvolvedores Estão Voltando ao Web Scraping do Twitter/X
Em 2023, o Twitter (agora X) eliminou sua camada de API gratuita e restringiu drasticamente os níveis pagos. O que antes custava $100/mês para acesso básico agora exige $5.000–$60.000/mês para volumes comparáveis. Para equipes de growth, análise de sentimento e monitoramento de marcas, isso transformou uma ferramenta acessível em um luxo proibitivo.
O resultado? Milhares de desenvolvedores retornaram ao web scraping — a técnica que a API originalmente pretendia substituir. Mas raspar o X em 2024/2025 é fundamentalmente diferente de 2015. A plataforma agora é uma Single Page Application (SPA) que carrega dados via GraphQL, implementa fingerprinting agressivo e bloqueia ranges de IP de datacenters com precisão cirúrgica.
Este guia explica como acessar dados públicos do X de forma responsável, usando proxies residenciais para contornar bloqueios sem violar os limites técnicos da plataforma.
O Que Está Disponível Publicamente (Sem Login)
Nem tudo no X requer autenticação. A plataforma ainda serve conteúdo público para usuários anônimos — bots, scrapers e visitantes sem conta. Entender o que é acessível determina sua estratégia técnica.
Dados Acessíveis Sem Autenticação
- Perfis públicos: Nome, bio, localização, link, data de criação, contagem de seguidores/seguindo.
- Tweets públicos: Texto, timestamps, contagem de likes/retweets/replies, anexos de mídia.
- Threads e respostas: Respostas a tweets públicos (se o perfil do respondente também for público).
- Trending topics: Tópicos em alta por localização geográfica.
- Resultados de busca: Buscas por palavra-chave, hashtag ou menção (limitados e sujeitos a throttling agressivo).
Dados Que Exigem Login
- Timeline personalizada ("For You")
- Tweets de contas protegidas/privadas
- Buscas avançadas com filtros complexos
- Lista de seguidores/seguindo completa (apenas parcial sem login)
- Direct messages e interações privadas
- Bookmarks e listas do usuário
Regra prática: Se você consegue ver em uma janela anônima do navegador, pode ser raspado. Se requer login, você está acessando dados que a API paga provavelmente autoriza — considere pagar em vez de raspar.
Por Que Proxies Residenciais São Essenciais para o X
O X opera um dos sistemas anti-bot mais sofisticados da web. Diferente de sites de e-commerce que bloqueiam baseados em comportamento, o X bloqueia baseados em identidade de rede — especificamente, o tipo de IP que você usa.
O Problema com IPs de Datacenter
IPs de datacenter são fáceis de identificar e bloquear em massa. O X mantém listas atualizadas de ranges ASN pertencentes a AWS, Google Cloud, Azure, DigitalOcean e outros provedores de cloud. Requisições vindas desses ranges recebem tratamento diferente:
- Rate limits 5–10x mais restritos
- CAPTCHAs em cada página
- Blocos temporários (HTTP 429) após poucas requisições
- Blocos permanentes (HTTP 403) para ranges inteiros
Testei isso pessoalmente: um script simples fazendo 50 requisições de um IP AWS foi bloqueado em menos de 2 minutos. O mesmo script usando um proxy residencial rodou por 4 horas sem interrupção.
Por Que IPs Residenciais Funcionam
Proxies residenciais usam IPs atribuídos a residências reais por ISPs legítimos. Para o X, o tráfego parece vir de um usuário doméstico comum — exatamente o que a plataforma espera. Isso não é sobre "enganar" o sistema; é sobre evitar falsos positivos que afetam usuários legítimos.
| Tipo de Proxy | Detecção pelo X | Rate Limit Típico | Risco de Bloqueio |
|---|---|---|---|
| Datacenter | Imediata | ~10 req/min | Alto |
| Datacenter rotativo | Rápida | ~20 req/min | Médio-Alto |
| Residencial rotativo | Difícil | ~100 req/min/IP | Baixo |
| Mobile (4G/5G) | Muito difícil | ~200 req/min/IP | Mínimo |
Para scraping sustentado do X, proxies residenciais rotativos oferecem o melhor equilíbrio entre custo e confiabilidade. Proxies móveis são ainda mais confiáveis, mas significativamente mais caros.
Implementação Prática: Python + Playwright com Proxies Residenciais
O X moderno é uma SPA React que carrega dados via endpoints GraphQL internos. Raspar HTML parseado é possível, mas ineficiente — os dados que você quer já estão embutidos como JSON nas respostas GraphQL.
Abordagem 1: Interceptando Payloads GraphQL
A estratégia mais robusta é deixar o navegador carregar a página normalmente, interceptar as respostas GraphQL e extrair o JSON diretamente. Isso evita parsing de HTML e funciona mesmo quando o X muda a estrutura do DOM.
import asyncio
from playwright.async_api import async_playwright
import json
PROXY_URL = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
class TwitterScraper:
def __init__(self, proxy_url=None):
self.proxy_url = proxy_url
self.graphql_data = []
async def intercept_response(self, response):
"""Captura respostas GraphQL do X."""
if "graphql" in response.url and ".json" in response.url:
try:
data = await response.json()
self.graphql_data.append({
"url": response.url,
"data": data
})
except Exception as e:
print(f"Erro ao processar resposta: {e}")
async def scrape_profile(self, username: str):
"""Raspa perfil público do usuário."""
async with async_playwright() as p:
launch_args = {"headless": True}
if self.proxy_url:
launch_args["proxy"] = {"server": self.proxy_url}
browser = await p.chromium.launch(**launch_args)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
)
page = await context.new_page()
page.on("response", self.intercept_response)
try:
await page.goto(
f"https://x.com/{username}",
wait_until="networkidle",
timeout=30000
)
await page.wait_for_timeout(3000)
# Extrai dados do perfil do GraphQL capturado
profile_data = self.extract_profile_data()
return profile_data
except Exception as e:
print(f"Erro: {e}")
return None
finally:
await browser.close()
def extract_profile_data(self):
"""Processa payloads GraphQL capturados."""
for item in self.graphql_data:
data = item["data"]
if "user" in str(data):
# Estrutura varia por endpoint
try:
user_result = data.get("data", {}).get("user", {}).get("result", {})
legacy = user_result.get("legacy", {})
return {
"id": user_result.get("rest_id"),
"name": legacy.get("name"),
"screen_name": legacy.get("screen_name"),
"description": legacy.get("description"),
"followers_count": legacy.get("followers_count"),
"friends_count": legacy.get("friends_count"),
"statuses_count": legacy.get("statuses_count"),
"location": legacy.get("location"),
"created_at": legacy.get("created_at"),
}
except (KeyError, TypeError):
continue
return None
# Uso
async def main():
scraper = TwitterScraper(proxy_url=PROXY_URL)
profile = await scraper.scrape_profile("elonmusk")
print(json.dumps(profile, indent=2, ensure_ascii=False))
asyncio.run(main())
Abordagem 2: Rotação de IPs com Pool Residencial
Para scraping em escala, você precisa rotacionar IPs automaticamente. A maioria dos provedores de proxies residenciais suporta rotação por requisição ou sessões sticky.
import requests
from urllib.parse import quote
import time
import random
class TwitterAPIScraper:
"""
Scraper usando requests puro + proxies residenciais.
Menos robusto que Playwright, mas mais rápido para buscas simples.
"""
BASE_URL = "https://x.com"
def __init__(self, proxy_user, proxy_pass):
self.proxy_user = proxy_user
self.proxy_pass = proxy_pass
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "application/json",
"Accept-Language": "en-US,en;q=0.9",
})
def get_rotating_proxy(self, country="US"):
"""Gera URL de proxy com IP rotativo por requisição."""
# ProxyHat suporta flags de país no username
username = f"{self.proxy_user}-country-{country}"
return f"http://{quote(username)}:{self.proxy_pass}@gate.proxyhat.com:8080"
def get_sticky_proxy(self, session_id, country="US"):
"""Gera proxy com sessão sticky (mesmo IP por N minutos)."""
username = f"{self.proxy_user}-country-{country}-session-{session_id}"
return f"http://{quote(username)}:{self.proxy_pass}@gate.proxyhat.com:8080"
def search_tweets(self, query, max_results=100):
"""Busca tweets públicos (requer tratamento de rate limits)."""
proxies = {"http": self.get_rotating_proxy(), "https": self.get_rotating_proxy()}
# Nota: endpoint real requer headers específicos e tokens
# Este é um exemplo simplificado
try:
response = self.session.get(
f"{self.BASE_URL}/search?q={quote(query)}",
proxies=proxies,
timeout=30
)
if response.status_code == 429:
print("Rate limit atingido. Aguardando...")
time.sleep(60)
return self.search_tweets(query, max_results)
return response.text
except requests.exceptions.ProxyError:
print("Erro de proxy. Tentando novo IP...")
time.sleep(random.uniform(2, 5))
return self.search_tweets(query, max_results)
# Uso
scraper = TwitterAPIScraper(
proxy_user="seu_usuario",
proxy_pass="sua_senha"
)
result = scraper.search_tweets("python programming")
print(result[:500])
Exemplo em Node.js
const { chromium } = require('playwright');
const PROXY_URL = 'http://user-country-US:PASSWORD@gate.proxyhat.com:8080';
async function scrapeTweet(tweetId) {
const browser = await chromium.launch({
headless: true,
proxy: { server: PROXY_URL }
});
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
const graphqlResponses = [];
// Intercepta respostas GraphQL
page.on('response', async (response) => {
if (response.url().includes('graphql') && response.url().includes('.json')) {
try {
const data = await response.json();
graphqlResponses.push(data);
} catch (e) {}
}
});
await page.goto(`https://x.com/i/status/${tweetId}`, {
waitUntil: 'networkidle'
});
await page.waitForTimeout(3000);
await browser.close();
// Processa respostas capturadas
return graphqlResponses;
}
// Uso
scrapeTweet('1234567890123456789')
.then(data => console.log(JSON.stringify(data, null, 2)))
.catch(console.error);
Gerenciando Rate Limits e Throttling
O X implementa múltiplas camadas de rate limiting. Entender cada uma é essencial para scraping sustentável.
Tipos de Rate Limit
- IP-level (não logado): ~500 requisições/hora para usuários anônimos. Reset em janelas de 15 minutos.
- IP-level (logado): ~2.000 requisições/hora, mas rastreado por conta também.
- Account-level: Limites por ação (follow, like, tweet) — não aplicável sem login.
- Sliding window: O X usa janelas deslizantes, não resets em hora fixa.
Estratégias de Mitigação
import time
import random
from functools import wraps
def rate_limit_handler(max_retries=3, base_delay=60):
"""Decorador para tratamento automático de rate limits."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
retries = 0
while retries < max_retries:
result = func(*args, **kwargs)
if result and result.get('status_code') == 429:
delay = base_delay * (2 ** retries) + random.uniform(0, 10)
print(f"Rate limit. Aguardando {delay:.0f}s...")
time.sleep(delay)
retries += 1
continue
return result
raise Exception("Rate limit excedido após todas tentativas")
return wrapper
return decorator
class SmartScraper:
def __init__(self, requests_per_minute=30):
self.rpm = requests_per_minute
self.last_request = 0
self.min_interval = 60 / self.rpm
def wait_if_needed(self):
"""Garante intervalo mínimo entre requisições."""
elapsed = time.time() - self.last_request
if elapsed < self.min_interval:
sleep_time = self.min_interval - elapsed
sleep_time += random.uniform(0, 1) # Jitter
time.sleep(sleep_time)
self.last_request = time.time()
# Configuração recomendada para X
scraper = SmartScraper(requests_per_minute=20) # Conservador
Sinais de Que Você Está Sendo Throttlado
- HTTP 429 (Too Many Requests) — óbvio, mas não o único sinal
- Respostas 200 com dados vazios ou parciais
- Redirecionamentos para página de login
- CAPTCHAs frequentes
- Timeouts crescentes
Dica: Monitore a taxa de sucesso, não apenas erros. Uma queda de 95% para 80% de sucesso indica throttling silencioso antes dos 429s aparecerem.
Aspectos Legais e Éticos
O scraping de dados públicos existe em uma zona cinzenta legal. Casos recentes esclarecem alguns limites, mas a situação continua evoluindo.
Casos Relevantes
- hiQ Labs v. LinkedIn (2022): A 9ª Circuito decidiu que raspar dados públicos não viola a CFAA (Computer Fraud and Abuse Act). Porém, isso não significa que violar ToS é legal — apenas que não é automaticamente um crime federal.
- Meta v. Bright Data (2024): Meta processou Bright Data por scraping do Facebook. Caso ainda em andamento, mas reforça que ToS violations podem ter consequências civis.
- X Corp v. Unknown Defendants: X moveu várias ações contra scrapers. A plataforma consistentemente argumenta que scraping viola seus ToS e pode constituir "tortious interference" com contratos.
O Que Isso Significa na Prática
- Raspar dados públicos não é crime nos EUA (pós-hiQ), mas pode gerar processos civis.
- Violar ToS pode ter consequências — desde banimento de conta até ações legais em escala.
- Use dados apenas para fins legítimos. Análise de sentimento, pesquisa acadêmica e monitoramento de marca são usos defensáveis.
- Nunca venda os dados. Revenda de dados raspados é um risco legal significativamente maior.
Quando Usar a API Oficial
A API paga do X ($100/mês nível básico, $5.000+/mês para escala) é cara, mas oferece:
- Proteção legal clara (ToS permite o uso)
- Dados estruturados e confiáveis
- Suporte oficial e SLA
- Acesso a dados não disponíveis publicamente
Para projetos comerciais sérios, calcule o custo-benefício: horas de desenvolvimento + manutenção + risco legal vs. custo da API.
Melhores Práticas para Scraping Responsável
Técnicas Recomendadas
- Respeite robots.txt: O X permite alguns paths, bloqueia outros. Verifique regularmente.
- Rate limiting conservador: 20–30 RPM por IP é seguro. Mais que isso arrisca blocos.
- Horários de pico: Evite horários de alto tráfego se possível.
- User-agent realista: Use strings de browsers reais, atualize periodicamente.
- Headers completos: Accept, Accept-Language, Accept-Encoding, DNT, Connection.
- Rotação de proxies: Use pool residencial rotativo, não um único IP.
O Que Evitar
- Não use contas reais para scraping — risco de banimento permanente
- Não ignore CAPTCHAs repetidos — você está sendo sinalizado
- Não raspe dados de contas privadas
- Não tente burlar login para acessar dados restritos
- Não venda ou redistribua dados raspados
Key Takeaways
- O X bloqueia agressivamente IPs de datacenter. Proxies residenciais são essenciais para scraping sustentável.
- Dados públicos são acessíveis sem login, mas com rate limits significativamente mais restritos.
- Interceptar GraphQL é mais robusto que parsear HTML. A SPA do X carrega dados como JSON embutido.
- Rate limits são por IP e usam janelas deslizantes. Monitore sucesso, não apenas erros.
- Scraping de dados públicos não é crime nos EUA, mas violar ToS pode ter consequências civis.
- Para projetos comerciais sérios, considere a API paga. O custo pode ser menor que o risco legal e de manutenção.
Conclusão
Raspar dados públicos do X permanece tecnicamente viável em 2025, mas exige mais sofisticação que no passado. Proxies residenciais rotativos, interceptação de GraphQL e rate limiting conservador são componentes essenciais de qualquer pipeline de coleta.
Para equipes de growth e desenvolvedores construindo dashboards de sentimento ou monitoramento de marca, a decisão entre scraping e API oficial depende do volume necessário, orçamento e tolerância a risco. Para volumes modestos (<10.000 tweets/mês), scraping com proxies residenciais como os da ProxyHat oferece custo-benefício atrativo. Para escala empresarial, a API paga pode ser mais econômica no longo prazo.
Independente da abordagem, mantenha sempre conformidade com leis de privacidade (RGPD, LGPD, CCPA) e use os dados de forma ética e responsável.






