Raspar dados do Instagram é um dos desafios técnicos mais comuns para equipes de inteligência de mercado, análise de sentimentos e monitoramento de marcas. A plataforma possui algumas das defesas anti-bot mais sofisticadas da web moderna — e ignorá-las significa contas banidas, IPs bloqueados e pipelines de dados que falham silenciosamente.
Este guia explica como acessar dados públicos do Instagram de forma ética e técnica, usando proxies residenciais para contornar bloqueios sem violar os termos de serviço. Mostramos código Python real, headers corretos, e estratégias de rotação que funcionam em 2024.
Aviso Legal e Ético: Este artigo aborda apenas o acesso a dados públicos disponíveis sem autenticação. Não incentivamos nem ensinamos automação de login, bypass de paywalls, ou violação de Termos de Serviço. Respeite sempre o robots.txt, a Lei Geral de Proteção de Dados (LGPD no Brasil, GDPR na UE), e considere usar APIs oficiais quando disponíveis. O scraping irresponsável pode violar a CFAA (EUA) e legislações similares em outros países.
Por Que o Instagram é Difícil de Raspar em Escala
O Instagram não é um site estático com HTML simples. É uma aplicação web complexa que emprega múltiplas camadas de defesa contra automação:
Rate Limits Agressivos
O Instagram limita requisições por IP e por conta. Para usuários não logados, os limites são ainda mais restritos — tipicamente algumas centenas de requisições por hora antes de receber HTTP 429 (Too Many Requests) ou ser redirecionado para uma página de login.
Login Wall Progressivo
Muitas páginas que antes eram públicas agora exigem login após algumas visualizações. O Instagram detecta padrões de navegação automatizada e progressivamente restringe o acesso, servindo uma página de login onde antes havia conteúdo.
Anti-Bot e Device Fingerprinting
O Instagram coleta fingerprints do navegador via JavaScript — canvas fingerprint, WebGL renderer, fontes instaladas, resolução de tela, timezone, e dezenas de outras características. Esses dados são usados para identificar bots mesmo que eles rotacionem IPs.
HTTPS Pinning e API Privada
O Instagram usa certificate pinning em seu app móvel e a maioria dos dados vem de APIs GraphQL privadas que requerem headers específicos e tokens criptografados. A engenharia reversa dessas APIs é um jogo de gato e rato constantemente perdido.
O Que Está Acessível Sem Login
Apesar das restrições, uma quantidade significativa de dados públicos permanece acessível para usuários anônimos:
- Páginas de perfil público: username, bio, contagem de seguidores, posts recentes (limitado)
- Páginas de hashtag: posts recentes com a hashtag, contagem total
- Páginas de localização: posts geotagueados em locais públicos
- Reels feeds: vídeos públicos em perfis abertos
- Posts individuais: quando você tem a URL direta (shortcode)
O que não está acessível sem login:
- Stories (sempre requer login)
- DMs e mensagens
- Perfis privados
- Comentários completos em posts
- Seguidores/seguindo lists completas
- Feed principal do usuário
Por Que Proxies Residenciais São Essenciais para Instagram
O Instagram mantém listas de IPs de datacenter conhecidos — AWS, DigitalOcean, Hetzner, e outros provedores de VPS. Requisições vindas desses IPs são automaticamente marcadas como suspeitas.
| Característica | Proxy Datacenter | Proxy Residencial |
|---|---|---|
| Origem do IP | IPs de servidores em datacenters | IPs de dispositivos residenciais reais |
| Detecção pelo Instagram | Alta — frequentemente bloqueado | Baixa — parece tráfego legítimo |
| Custo | Menor ($1-3/GB) | Maior ($5-15/GB) |
| Velocidade | Muito rápida | Variável (depende do peer) |
| Taxa de sucesso em IG | 30-50% | 85-95% |
| Risco de banimento | Alto | Baixo (com rotação adequada) |
Proxies residenciais funcionam porque o Instagram não pode bloquear todos os IPs residenciais sem afetar usuários legítimos. Cada IP vem de um dispositivo real — modem doméstico, celular, ou IoT device — tornando a distinção entre scraper e usuário humano praticamente impossível.
Proxies Móveis: A Opção Premium
Para scraping de Instagram em escala máxima, proxies móveis (4G/5G) oferecem a melhor taxa de sucesso. IPs móveis rotacionam naturalmente conforme os dispositivos se conectam a diferentes torres, e o Instagram hesita em bloqueá-los pois muitos usuários legítimos acessam via dados móveis.
Implementação em Python: Requests + Proxy Residencial Rotativo
Vamos implementar um scraper básico que acessa perfis públicos do Instagram usando proxies residenciais rotativos da ProxyHat.
import requests
import time
import random
from fake_useragent import UserAgent
# Configuração do proxy residencial ProxyHat
PROXY_HOST = 'gate.proxyhat.com'
PROXY_PORT = 8080
PROXY_USER = 'SEU_USERNAME_AQUI'
PROXY_PASS = 'SUA_SENHA_AQUI'
# Para targeting por país (opcional):
# PROXY_USER = 'user-country-BR'
# Para sessão sticky (manter mesmo IP por um período):
# PROXY_USER = 'user-session-abc123-country-BR'
def get_proxy_url(session_id=None):
"""Gera URL do proxy com rotação opcional de sessão"""
username = PROXY_USER
if session_id:
username = f'{PROXY_USER}-session-{session_id}'
return f'http://{username}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}'
def get_rotating_session():
"""Gera ID de sessão aleatório para rotação de IP"""
return f'scr{int(time.time())}-{random.randint(1000,9999)}'
# Headers realistas para parecer um navegador legítimo
BASE_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate, br',
'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',
}
# User agents rotativos
ua = UserAgent()
def scrape_instagram_profile(username):
"""
Raspa dados públicos de um perfil do Instagram.
Retorna dict com informações do perfil ou None se falhar.
"""
session_id = get_rotating_session()
proxy_url = get_proxy_url(session_id)
proxies = {
'http': proxy_url,
'https': proxy_url
}
# Rotacionar user-agent a cada requisição
headers = BASE_HEADERS.copy()
headers['User-Agent'] = ua.chrome
# URL do perfil
url = f'https://www.instagram.com/{username}/'
try:
response = requests.get(
url,
headers=headers,
proxies=proxies,
timeout=30,
allow_redirects=True
)
if response.status_code == 429:
print(f'Rate limit atingido para {username}')
return None
if response.status_code != 200:
print(f'Erro {response.status_code} para {username}')
return None
# Verificar se fomos redirecionados para login
if 'login' in response.url:
print(f'Login wall detectado para {username}')
return None
# Parsear dados da página
return parse_profile_page(response.text, username)
except requests.exceptions.RequestException as e:
print(f'Erro de conexão: {e}')
return None
def parse_profile_page(html, username):
"""
Extrai dados do HTML do perfil.
Nota: O Instagram muda a estrutura frequentemente.
"""
import re
import json
# Tentar extrair dados do JSON embutido
# O Instagram inclui dados em uma tag script
pattern = r'window\._sharedData\s*=\s*({.+?});'
match = re.search(pattern, html)
if match:
try:
data = json.loads(match.group(1))
user_data = data.get('entry_data', {}).get('ProfilePage', [{}])[0]
user = user_data.get('graphql', {}).get('user', {})
return {
'username': username,
'full_name': user.get('full_name'),
'biography': user.get('biography'),
'followers': user.get('edge_followed_by', {}).get('count'),
'following': user.get('edge_follow', {}).get('count'),
'posts_count': user.get('edge_owner_to_timeline_media', {}).get('count'),
'is_private': user.get('is_private'),
'is_verified': user.get('is_verified'),
'profile_pic_url': user.get('profile_pic_url'),
}
except (json.JSONDecodeError, KeyError, IndexError) as e:
print(f'Erro ao parsear JSON: {e}')
return None
# Exemplo de uso com rate limiting ético
def scrape_multiple_profiles(usernames, delay_seconds=3):
"""Raspa múltiplos perfis com delay entre requisições"""
results = []
for username in usernames:
print(f'Raspando @{username}...')
data = scrape_instagram_profile(username)
if data:
results.append(data)
print(f' ✓ Sucesso: {data["followers"]} seguidores')
else:
print(f' ✗ Falha')
# Rate limiting ético - NUNCA pule isso
time.sleep(delay_seconds + random.uniform(0.5, 2.0))
return results
if __name__ == '__main__':
profiles = ['instagram', 'cristiano', 'nike', 'natgeo']
data = scrape_multiple_profiles(profiles)
import json
print(json.dumps(data, indent=2, ensure_ascii=False))
Endpoints e Headers Específicos do Instagram
O Instagram oferece alguns endpoints que retornam JSON diretamente, mas eles requerem headers específicos e estão sujeitos a mudanças frequentes.
O Endpoint ?__a=1
Historicamente, adicionar ?__a=1 a qualquer URL do Instagram retornava JSON estruturado. Este endpoint foi progressivamente restrito e agora requer autenticação na maioria dos casos.
# Este endpoint pode não funcionar mais sem login
url = f'https://www.instagram.com/{username}/?__a=1'
# Headers necessários para API do Instagram
API_HEADERS = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15',
'x-ig-app-id': '936619743392459', # App ID público do Instagram Web
'x-requested-with': 'XMLHttpRequest',
'x-csrftoken': 'obtido_de_cookie',
}
GraphQL Queries
O Instagram usa GraphQL internamente. Fazer engenharia reversa dessas queries é possível, mas instável:
import requests
def query_hashtag_posts(hashtag, proxy_url):
"""
Query GraphQL para posts de hashtag.
AVISO: Este endpoint pode mudar a qualquer momento.
"""
# Query ID obtido via engenharia reversa
query_hash = '9dcf6e1a98bc7f6e92953d5a61027b98'
variables = {
'tag_name': hashtag,
'first': 50,
'after': None
}
url = 'https://www.instagram.com/graphql/query/'
params = {
'query_hash': query_hash,
'variables': json.dumps(variables)
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'x-ig-app-id': '936619743392459',
}
proxies = {'http': proxy_url, 'https': proxy_url}
response = requests.get(url, params=params, headers=headers, proxies=proxies)
if response.status_code == 200:
return response.json()
return None
Exemplo em Node.js com Axios
Para equipes que usam JavaScript/TypeScript, aqui está uma implementação equivalente:
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
// Configuração ProxyHat
const PROXY_CONFIG = {
host: 'gate.proxyhat.com',
port: 8080,
auth: {
username: 'SEU_USERNAME',
password: 'SUA_SENHA'
}
};
// User agents rotativos
const USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
];
function getRandomUserAgent() {
return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
}
function generateSessionId() {
return `node-${Date.now()}-${uuidv4().slice(0, 8)}`;
}
async function scrapeInstagramProfile(username) {
const sessionId = generateSessionId();
// Proxy com sessão rotativa
const proxy = {
host: PROXY_CONFIG.host,
port: PROXY_CONFIG.port,
auth: {
username: `${PROXY_CONFIG.auth.username}-session-${sessionId}`,
password: PROXY_CONFIG.auth.password
}
};
const headers = {
'User-Agent': getRandomUserAgent(),
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Sec-Fetch-Mode': 'navigate',
};
try {
const response = await axios.get(`https://www.instagram.com/${username}/`, {
proxy,
headers,
timeout: 30000,
maxRedirects: 5
});
if (response.status === 429) {
console.log(`Rate limit para ${username}`);
return null;
}
// Extrair dados do HTML
const match = response.data.match(/window\._sharedData\s*=\s*({.+?});/);
if (match) {
const data = JSON.parse(match[1]);
const user = data?.entry_data?.ProfilePage?.[0]?.graphql?.user;
if (user) {
return {
username,
fullName: user.full_name,
followers: user.edge_followed_by?.count,
following: user.edge_follow?.count,
posts: user.edge_owner_to_timeline_media?.count,
isPrivate: user.is_private,
isVerified: user.is_verified,
};
}
}
return null;
} catch (error) {
if (error.response?.status === 429) {
console.log(`Rate limit para ${username}`);
} else {
console.error(`Erro: ${error.message}`);
}
return null;
}
}
// Rate limiting entre requisições
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function scrapeProfiles(usernames) {
const results = [];
for (const username of usernames) {
console.log(`Raspando @${username}...`);
const data = await scrapeInstagramProfile(username);
if (data) {
results.push(data);
console.log(` ✓ ${data.followers} seguidores`);
}
// Delay ético
await delay(3000 + Math.random() * 2000);
}
return results;
}
// Executar
(async () => {
const profiles = ['instagram', 'facebook', 'meta'];
const data = await scrapeProfiles(profiles);
console.log(JSON.stringify(data, null, 2));
})();
Estratégias de Rotação de IP e Sessão
A rotação de IPs é crítica para scraping de Instagram em escala. Existem duas abordagens principais:
Rotação Por Requisição
Cada requisição usa um IP diferente. Ideal para scraping distribuído de múltiplos perfis, mas pode parecer suspeito se você fizer muitas requisições para o mesmo conteúdo.
# Com ProxyHat, cada sessão = IP diferente
# Sem especificar sessão, você recebe um IP aleatório a cada requisição
proxy_url = f'http://{username}:{password}@gate.proxyhat.com:8080'
# Cada request.get() com este proxy pode usar IP diferente
Sessões Sticky
Manter o mesmo IP por um período (ex: 5-15 minutos) permite navegar como um usuário real. Útil para raspar múltiplas páginas de um mesmo perfil.
import random
import string
def generate_session_id():
"""Gera ID de sessão único para manter IP consistente"""
random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
return f'sess_{int(time.time())}_{random_str}'
# Use este username no proxy para manter o mesmo IP
session_id = generate_session_id()
sticky_username = f'user-session-{session_id}'
proxy_url = f'http://{sticky_username}:{password}@gate.proxyhat.com:8080'
# Todas as requisições com este proxy usarão o mesmo IP
# até que a sessão expire (tipicamente 5-30 minutos)
Geo-Targeting para Dados Locais
Se você precisa de dados específicos de uma região (ex: posts de uma localização em São Paulo), use geo-targeting no proxy:
# Proxy com IP do Brasil
proxy_url = 'http://user-country-BR:password@gate.proxyhat.com:8080'
# Proxy com IP de São Paulo (cidade)
proxy_url = 'http://user-country-BR-city-sao_paulo:password@gate.proxyhat.com:8080'
# Proxy com IP de Portugal
proxy_url = 'http://user-country-PT:password@gate.proxyhat.com:8080'
Tratamento de Erros e Retry Logic
O Instagram retornará vários tipos de erro. Aqui está como tratá-los:
import time
from functools import wraps
def retry_with_backoff(max_retries=3, backoff_factor=2):
"""
Decorator para retry com backoff exponencial.
Útil para erros transitórios.
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
retries = 0
while retries < max_retries:
try:
result = func(*args, **kwargs)
if result is not None:
return result
# Resultado None pode indicar erro tratado
retries += 1
except Exception as e:
retries += 1
# Backoff exponencial
wait_time = backoff_factor ** retries
print(f'Tentativa {retries}/{max_retries}. Aguardando {wait_time}s...')
time.sleep(wait_time)
return None
return wrapper
return decorator
@retry_with_backoff(max_retries=3)
def scrape_with_retry(username):
"""Scraping com retry automático"""
return scrape_instagram_profile(username)
def handle_instagram_response(status_code, url):
"""Trata diferentes códigos de resposta do Instagram"""
if status_code == 200:
return 'success'
elif status_code == 302:
# Redirecionado para login
return 'login_wall'
elif status_code == 429:
# Rate limit - espere mais tempo
return 'rate_limited'
elif status_code == 403:
# Bloqueado - IP ou fingerprint
return 'blocked'
elif status_code == 404:
# Perfil não existe
return 'not_found'
elif status_code == 503:
# Serviço indisponível
return 'server_error'
else:
return 'unknown_error'
Melhores Práticas de Scraping Ético
Scraping ético não é apenas sobre evitar bloqueios — é sobre respeitar a plataforma e outros usuários:
Respeite robots.txt
O Instagram permite alguns bots em robots.txt. Verifique sempre:
# robots.txt do Instagram (verifique periodicamente)
# User-agent: *
# Disallow: /accounts/
# Disallow: /p/\n# Disallow: /explore/
# ... (consulte o arquivo real)
Rate Limiting Auto-Imposto
Nunca faça requisições em paralelo máximo. Limite-se a 1-2 requisições por segundo no máximo, com delays aleatórios:
import random
import time
def ethical_delay():
"""Delay humanizado entre requisições"""
base_delay = 2.0 # segundos
jitter = random.uniform(0.5, 3.0) # aleatoriedade
time.sleep(base_delay + jitter)
Nunca Automatize Login
Automação de login viola os Termos de Serviço do Instagram e pode resultar em ação legal. Além disso, suas credenciais serão banidas rapidamente.
Use Dados Publicamente Disponíveis Apenas
Não tente acessar perfis privados, mensagens diretas, ou qualquer dado que requeira autenticação.
Considere APIs Oficiais
O Instagram oferece a Basic Display API e a Graph API para casos de uso legítimos. Se você precisa de dados autenticados, use as APIs oficiais.
Quando Usar APIs Oficiais em Vez de Scraping
Scraping deve ser o último recurso. Considere APIs oficiais quando:
- Você precisa de dados de perfis autenticados
- Está construindo uma aplicação comercial
- Precisa de dados em tempo real com alta confiabilidade
- Seu caso de uso é suportado pela API
Use scraping quando:
- A API oficial não suporta seu caso de uso
- Você precisa apenas de dados públicos básicos
- O volume de dados é pequeno a moderado
- Você pode tolerar alguma instabilidade
Key Takeaways
- Use proxies residenciais: Proxies datacenter são bloqueados imediatamente pelo Instagram. IPs residenciais parecem tráfego legítimo.
- Rotacione sessões, não apenas IPs: Use sessões sticky para manter consistência durante navegação, e rotacione entre sessões para distribuir carga.
- Headers realistas são obrigatórios: User-Agent, Accept-Language, e headers Sec-Fetch devem parecer um navegador real.
- Rate limiting ético: Nunca exceda 1-2 requisições por segundo. Adicione jitter aleatório para parecer humano.
- Não automatize login: Isso viola ToS e resulta em banimento. Permaneça em dados públicos apenas.
- Monitore mudanças: O Instagram muda sua estrutura frequentemente. Tenha testes que alertem quando o scraper quebrar.
- Considere APIs oficiais primeiro: Se disponíveis para seu caso de uso, APIs são mais estáveis e legais.
Conclusão
Raspar dados públicos do Instagram é tecnicamente desafiador, mas viável com as ferramentas certas. Proxies residenciais são essenciais para evitar bloqueios, e uma abordagem ética — com rate limiting auto-imposto e respeito aos Termos de Serviço — garante sustentabilidade a longo prazo.
Para pipelines de dados em escala, considere usar soluções de proxy gerenciadas que oferecem rotação automática, geo-targeting, e alta disponibilidade. A ProxyHat oferece proxies residenciais e móveis otimizados para plataformas de mídia social com IPs em mais de 195 países.
Se você está construindo uma ferramenta de social listening, análise de influenciadores, ou monitoramento de marca, comece com dados públicos, use proxies de qualidade, e sempre priorize a ética sobre a velocidade.






