Guia Completo: Como Raspar Dados Públicos do Instagram com Proxies Residenciais

Aprenda a raspar dados públicos do Instagram em escala usando proxies residenciais, headers realistas e boas práticas — sem violar ToS nem arriscar bans em massa.

How to Scrape Public Instagram Data with Residential Proxies

Se você já tentou construir um pipeline de dados para social listening no Instagram, sabe a frustração: os primeiros requests funcionam, e minutos depois seu IP está bloqueado. O Instagram investe pesado em anti-bot, e raspar dados públicos em escala sem uma estratégia sólida de proxies e fingerprints é praticamente impossível.

Este guia mostra como raspar dados públicos do Instagram de forma estável e responsável, usando proxies residenciais rotativos, headers realistas e técnicas que respeitam os limites da plataforma.

Aviso legal: Raspar dados do Instagram pode violar os Termos de Serviço da plataforma. Este artigo aborda apenas o acesso a dados públicos, sem autenticação, em conformidade com leis aplicáveis (CFAA nos EUA, GDPR na UE, LGPD no Brasil). Sempre consulte o robots.txt, os ToS e um advogado antes de implementar scraping em produção. Nunca automatize logins.

Por que o Instagram é difícil de raspar em escala

O Instagram não é um site estático comum. Ele é uma Single Page Application (SPA) que carrega dados via APIs internas protegidas por múltiplas camadas de segurança:

  • Rate limits agressivos: Requests não autenticados a partir de um único IP são limitados a poucas dezenas por hora. Exceder isso resulta em HTTP 429 ou redirecionamento para página de login.
  • Login wall: Cada vez mais conteúdo requer autenticação para ser visualizado, mesmo perfis públicos em alguns casos.
  • Anti-bot e device fingerprinting: O Instagram coleta fingerprints do navegador — canvas, WebGL, fontes, resolução, timezone — e os usa para identificar bots. Requests que não correspondem a um navegador real são bloqueados.
  • HTTPS pinning no mobile: O app mobile faz certificate pinning, impedindo interceptação fácil das chamadas de API via MITM proxies.
  • Shadow banning: IPs suspeitos não recebem erros explícitos; recebem respostas vazias ou dados incompletos, tornando o debug difícil.

Quais dados são acessíveis sem login

Nem tudo requer autenticação. O Instagram ainda expõe publicamente:

Tipo de dadoAcessível sem login?Notas
Perfil público (bio, seguidores, posts)SimLimitado a ~12 posts recentes via HTML/JSON
Páginas de hashtagParcialTop posts visíveis; recentes podem exigir login
Páginas de localizaçãoParcialDepende da configuração de privacidade do local
Feeds de ReelsLimitadoMetadados básicos via embed endpoints
StoriesNãoExigem autenticação
DMs e mensagensNãoRequer login — nunca automatize isso
Comentários completosParcialAlguns visíveis via embed; completos exigem login

A regra de ouro: se o dado exige login para ser visto manualmente, não tente raspar via automação. Limite-se ao que um usuário anônimo pode ver no navegador.

Por que proxies residenciais são essenciais para o Instagram

O Instagram mantém listas atualizadas de IPs de datacenters (AWS, DigitalOcean, Hetzner, etc.). Requests vindos desses IPs são marcados como suspeitos imediatamente. A diferença de comportamento é dramática:

CaracterísticaProxies DatacenterProxies Residenciais
Risco de bloqueio no InstagramAltíssimo (90%+ em poucos requests)Baixo com rotação adequada
VelocidadeMuito rápidaModerada (latência de ISP residencial)
CustoBaixo ($1-3/GB)Médio ($5-15/GB)
Fingerprint de IPASN de datacenter — óbvioASN de ISP residencial — natural
Confiabilidade para IGPéssimaBoa a excelente
Geo-targeting precisoLimitadoPaís, cidade e até ISP

Com proxies residenciais, cada request vem de um IP de um ISP real (Vivo, Claro, Comcast, Deutsche Telekom). O Instagram não consegue distinguir seu request de um usuário real navegando pelo app. Para scraping de Instagram, proxies residenciais não são luxo — são requisito.

Proxies mobile: a camada extra de realismo

Proxies mobile vão além: usam IPs de redes móveis reais (4G/5G). O Instagram confia ainda mais nesses IPs porque a maioria dos usuários acessa via mobile. Se o seu use case exige máxima confiabilidade, proxies mobile são a melhor opção — embora mais caros.

Implementação em Python: requests + proxy residencial rotativo

Vamos construir um scraper que raspa perfis públicos do Instagram usando proxies residenciais do ProxyHat com rotação por request.

1. Configuração base com headers realistas

import requests
import random
import time
from urllib.parse import quote

# ProxyHat residential proxy — rotation per request
PROXY_URL = "http://user-country-BR:PASSWORD@gate.proxyhat.com:8080"

PROXIES = {
    "http": PROXY_URL,
    "https": PROXY_URL,
}

# Realistic user agents (mobile-focused, since most IG traffic is mobile)
USER_AGENTS = [
    "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) "
    "AppleWebKit/605.1.15 (KHTML, like Gecko) "
    "Version/17.5 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (Linux; Android 14; Pixel 8) "
    "AppleWebKit/537.36 (KHTML, like Gecko) "
    "Chrome/125.0.6422.113 Mobile Safari/537.36",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) "
    "AppleWebKit/605.1.15 (KHTML, like Gecko) "
    "CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1",
]

def get_session():
    """Create an isolated session with realistic headers."""
    session = requests.Session()
    session.headers.update({
        "User-Agent": random.choice(USER_AGENTS),
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
        "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",
    })
    session.proxies = PROXIES
    return session

2. Raspando perfil público via embed endpoint

O endpoint ?__a=1 que retornava JSON limpo foi desativado para usuários não autenticados. Uma alternativa estável é usar o embed endpoint, que retorna HTML com dados estruturados:

import re
import json

def scrape_profile(username: str) -> dict:
    """Scrape public profile data using the embed endpoint."""
    session = get_session()
    embed_url = f"https://www.instagram.com/{username}/embed/"

    try:
        resp = session.get(embed_url, timeout=15)
        resp.raise_for_status()
    except requests.RequestException as e:
        return {"error": str(e), "username": username}

    html = resp.text

    # Extract JSON data from the embed page script
    match = re.search(
        r'window\.__additionalDataLoaded\(undefined,(.+?)\);',
        html
    )
    if not match:
        # Fallback: try to find window._sharedData
        match = re.search(
            r'window\._sharedData\s*=\s*({.+?});',
            html
        )

    if match:
        try:
            data = json.loads(match.group(1))
            user_data = (
                data
                .get("entry_data", {})
                .get("ProfilePage", [{}])[0]
                .get("graphql", {})
                .get("user", {})
            )
            return {
                "username": user_data.get("username", username),
                "full_name": user_data.get("full_name"),
                "bio": user_data.get("biography"),
                "followers": user_data.get("edge_followed_by", {}).get("count"),
                "following": user_data.get("edge_follow", {}).get("count"),
                "posts": user_data.get("edge_owner_to_timeline_media", {}).get("count"),
                "is_private": user_data.get("is_private"),
                "is_verified": user_data.get("is_verified"),
            }
        except (json.JSONDecodeError, KeyError, IndexError) as e:
            return {"error": f"Parse error: {e}", "username": username}

    return {"error": "No data found in page", "username": username}

# Usage
result = scrape_profile("nasa")
print(json.dumps(result, indent=2, ensure_ascii=False))

# Be polite — wait between requests
time.sleep(random.uniform(3, 7))

3. Rotação de sessão com sticky sessions

Para requisições que precisam manter o mesmo IP (ex: paginação), use sticky sessions do ProxyHat:

def get_sticky_session(session_id: str) -> requests.Session:
    """Create a session that keeps the same IP for its duration."""
    sticky_proxy = f"http://user-session-{session_id}-country-BR:PASSWORD@gate.proxyhat.com:8080"
    session = requests.Session()
    session.headers.update({
        "User-Agent": random.choice(USER_AGENTS),
        "Accept-Language": "pt-BR,pt;q=0.9",
    })
    session.proxies = {
        "http": sticky_proxy,
        "https": sticky_proxy,
    }
    return session

# Use the same IP for a batch of related requests
import uuid
session_id = str(uuid.uuid4())[:8]
session = get_sticky_session(session_id)

# Multiple requests share the same exit IP
profile = scrape_profile_with_session("nasa", session)
# ... more requests using same session

# After the batch, discard the session
del session

Quirks específicos do Instagram que você precisa conhecer

O fim do ?__a=1 e a era do GraphQL

Até 2020, adicionar ?__a=1 a qualquer URL do Instagram retornava JSON limpo com todos os dados da página. Isso foi desativado para requests não autenticados. Hoje, o Instagram usa GraphQL queries internas que exigem headers específicos:

  • x-ig-app-id: O ID do app do Instagram (geralmente 936619743392459 para a versão web).
  • x-csrftoken: Token CSRF obtido do cookie csrftoken — só disponível após uma sessão autenticada.
  • x-requested-with: XMLHttpRequest para indicar chamada AJAX.

Problema: Esses headers são projetados para requests autenticados. Usá-los sem login pode funcionar temporariamente, mas quebra frequentemente e pode ser interpretado como tentativa de bypass de autenticação.

Reverse engineering da API mobile

Muitos scrapers avançados fazem reverse engineering do app mobile do Instagram para replicar suas chamadas de API. Isso envolve:

  1. Descompilar o APK/IPA para encontrar endpoints e chaves.
  2. Replicar assinaturas de request (User-Agent específico, headers customizados).
  3. Lidar com HTTPS pinning para capturar o tráfego.

Isso é frágil por natureza: qualquer atualização do app pode quebrar seu scraper. Além disso, replicar o comportamento do app mobile sem autenticação pode ser considerado violação de ToS. Para pipelines de produção, prefira endpoints estáveis (embed, oEmbed) e dados públicos.

HTTPS pinning

O app do Instagram faz certificate pinning, o que significa que ele só confia em certificados SSL específicos do Meta. Isso impede que você use um proxy MITM (como mitmproxy) para interceptar chamadas de API do app. Para contornar, seria necessário usar ferramentas como Frida ou Objection para desabilitar o pinning em tempo de execução — algo que vai além do escopo de scraping ético de dados públicos.

Padrões de rate limiting e gestão de fingerprints

Rate limiting responsável

Mesmo com proxies residenciais rotativos, você precisa ser gentil:

  • Delay entre requests: 3–8 segundos por IP, imitando navegação humana.
  • Limite por IP: No máximo 30–50 requests por IP antes de rotar.
  • Limite global: Mesmo com rotação, não exceda centenas de requests por minuto ao total.
  • Backoff exponencial: Se receber 429 ou respostas vazias, espere 60s+ antes de tentar novamente.
def scrape_with_backoff(username: str, max_retries: int = 3) -> dict:
    """Scrape with exponential backoff on failures."""
    for attempt in range(max_retries):
        session = get_session()  # New session = new IP via rotation
        try:
            result = scrape_profile(username)
            if "error" not in result:
                return result
            # Empty data might mean shadow ban
            if result.get("error") == "No data found in page":
                wait = (2 ** attempt) * 15 + random.uniform(0, 10)
                print(f"Shadow ban suspected, waiting {wait:.0f}s...")
                time.sleep(wait)
                continue
        except requests.exceptions.HTTPError as e:
            if e.response is not None and e.response.status_code == 429:
                wait = (2 ** attempt) * 30 + random.uniform(0, 30)
                print(f"Rate limited, waiting {wait:.0f}s...")
                time.sleep(wait)
                continue
            raise
        finally:
            time.sleep(random.uniform(3, 7))  # Polite delay
    return {"error": "Max retries exceeded", "username": username}

Gestão de fingerprints

Além do IP, o Instagram verifica consistência entre headers, cookies e comportamento:

  • Não misture User-Agents na mesma sessão. Um request com UA de iPhone seguido por UA de Android é obviamente suspeito.
  • Mantenha cookies de sessão — não os descarte entre requests da mesma "navegação".
  • Varie o Accept-Language de forma consistente com o geo-targeting do proxy.
  • Ordem dos headers importa — navegadores enviam headers em ordem específica. O requests do Python não preserva ordem por padrão; considere usar urllib3 ou httpx para mais controle.

Exemplo em Node.js com axios

const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');

const PROXY_URL = 'http://user-country-BR:PASSWORD@gate.proxyhat.com:8080';
const proxyAgent = new HttpsProxyAgent(PROXY_URL);

const USER_AGENTS = [
  'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) '
  + 'AppleWebKit/605.1.15 (KHTML, like Gecko) '
  + 'Version/17.5 Mobile/15E148 Safari/604.1',
  'Mozilla/5.0 (Linux; Android 14; Pixel 8) '
  + 'AppleWebKit/537.36 (KHTML, like Gecko) '
  + 'Chrome/125.0.6422.113 Mobile Safari/537.36',
];

async function scrapeInstagramProfile(username) {
  const url = `https://www.instagram.com/${username}/embed/`;
  const ua = USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];

  try {
    const response = await axios.get(url, {
      httpsAgent: proxyAgent,
      headers: {
        'User-Agent': ua,
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'pt-BR,pt;q=0.9',
        'Sec-Fetch-Dest': 'document',
        'Sec-Fetch-Mode': 'navigate',
        'Sec-Fetch-Site': 'none',
      },
      timeout: 15000,
    });

    // Parse embedded data from HTML
    const match = response.data.match(
      /window\.__additionalDataLoaded\(undefined,(.+?)\);/
    );
    if (match) {
      const data = JSON.parse(match[1]);
      const user = data?.entry_data?.ProfilePage?.[0]?.graphql?.user;
      return {
        username: user?.username,
        full_name: user?.full_name,
        bio: user?.biography,
        followers: user?.edge_followed_by?.count,
        posts: user?.edge_owner_to_timeline_media?.count,
      };
    }
    return { error: 'No data found', username };
  } catch (err) {
    return { error: err.message, username };
  }
}

// Usage with polite delay
(async () => {
  const result = await scrapeInstagramProfile('nasa');
  console.log(JSON.stringify(result, null, 2));
})();

Scraping ético e quando usar APIs oficiais

Scraping não é ilegal por si só, mas existem limites claros:

Princípios éticos fundamentais

  1. Respeite o robots.txt. O Instagram bloqueia crawling em /robots.txt. Dados públicos acessíveis manualmente são uma área cinzenta, mas automação em escala é claramente contrária à intenção da plataforma.
  2. Rate-limite a si mesmo. Mesmo que seu proxy suporte milhares de RPM, limite-se a velocidades humanas. Seu scraper não deve degradar a experiência de outros usuários.
  3. Nunca automatize logins. Criar contas fake ou automatizar login em contas reais é violação clara de ToS e pode ter consequências legais.
  4. Não raspe dados privados. Se um dado requer login para ser visto, não tente acessá-lo via scraping.
  5. Respeite GDPR/LGPD. Dados pessoais (mesmo públicos) estão sujeitos a regulamentações de privacidade. Ofereça mecanismos de opt-out.

Quando usar APIs oficiais em vez de scraping

CenárioScrapingAPI Oficial (Meta Graph API)
Monitoramento de marca em tempo realArriscado e frágilIdeal — webhooks estáveis
Análise de sentimento em escalaPossível com cuidadoMelhor — dados estruturados
Coleta de dados para IA/MLViola ToSRequer aprovação do Meta
Monitoramento de concorrentesComum mas arriscadoDados limitados via API
Pesquisa acadêmicaÁrea cinzentaMeta Academic — melhor opção

A Meta Graph API (Instagram Graph API) é a forma oficial de acessar dados de business accounts e creators. Requer um Facebook App aprovado, mas oferece dados estáveis, documentados e legais. Para qualquer pipeline de produção, avalie a API oficial antes de construir scrapers.

Se a API oficial não cobre seus dados, e o scraping é a única opção, use-o como último recurso — com proxies residenciais, rate limiting agressivo e monitoramento constante de falhas.

Key Takeaways

  • Proxies residenciais são obrigatórios para scraping de Instagram — proxies datacenter são bloqueados quase instantaneamente.
  • Limite-se a dados públicos acessíveis sem login. Nunca automatize autenticação.
  • O endpoint ?__a=1 está morto para usuários não autenticados. Use embed endpoints ou oEmbed como alternativa estável.
  • Headers e fingerprints importam tanto quanto o IP. Mantenha consistência entre UA, Accept-Language e geo do proxy.
  • Rate-limite agressivamente — 3–8 segundos entre requests, no máximo 30–50 por IP.
  • Use backoff exponencial ao encontrar 429s ou respostas vazias (shadow bans).
  • Avalie a API oficial do Meta antes de construir scrapers. É mais estável, legal e sustentável a longo prazo.

Pronto para construir seu pipeline de dados do Instagram com proxies residenciais confiáveis? Confira os planos do ProxyHat e comece com um pool de IPs residenciais que o Instagram não consegue distinguir de tráfego real.

Pronto para começar?

Acesse mais de 50M de IPs residenciais em mais de 148 países com filtragem por IA.

Ver preçosProxies residenciais
← Voltar ao Blog