Skip to content

horizonfps/instagram-scraper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Instagram Reels Analyzer

Ferramenta web que recebe um username público do Instagram e exibe os 20 Reels mais recentes com métricas, análise por IA e visualizações de tendência.

Desafio técnico — VIEWX.COM.BR — Desenvolvedor Full-Stack

Como rodar

docker compose up

Abra http://localhost:8000 no browser. Nada mais.

O que faz

  1. Digite um @username público
  2. O scraper busca o perfil + 20 Reels mais recentes direto da API do Instagram
  3. Exibe: métricas por Reel (views, likes, comentários, engagement rate), KPIs agregados, gráfico de tendência, tabela com sorting e export CSV
  4. Bônus: legenda de cada Reel, detecção de cross-post Facebook, análise por IA (OpenAI)

Stack e decisões técnicas

Backend: Python (FastAPI) + curl_cffi para scraping Frontend: HTML/CSS/JS vanilla Infra: 1 container Docker, docker compose up e pronto

Por que Python?

FastAPI e asyncio são naturais pra I/O-bound (scraping). O ecossistema Python tem as melhores ferramentas pra HTTP/scraping.

Por que vanilla JS?

O desafio pede uma ferramenta funcional, não um SPA com framework. Vanilla JS elimina build step, bundle, node_modules. O docker compose up sobe direto sem npm install. Priorizei funcionalidade e usabilidade sobre complexidade de toolchain.

Decisão crítica: curl_cffi (curl-impersonate) em vez de httpx/requests

Essa foi a decisão técnica mais importante do projeto.

O Instagram bloqueia requests baseado no TLS fingerprint da conexão. Bibliotecas Python (httpx, requests, aiohttp) têm fingerprint TLS diferente de um browser real — o Instagram detecta isso e retorna 429 (rate limit) independente do IP, proxy, ou cookies.

Testamos várias abordagens antes de chegar nessa solução:

Abordagem Resultado
httpx sem auth 429 — bloqueado por TLS fingerprint
httpx + proxy residencial (iProyal) 429 — proxy muda IP, mas fingerprint continua Python
httpx + proxy + sessionid (login) Funciona, mas requer conta Instagram e cookie manual
httpx + headers extras (Sec-Fetch-*, X-ASBD-ID) 429 — headers não mudam fingerprint TLS
Scraping HTML da página de perfil Dados insuficientes (Instagram carrega via JS)
GraphQL endpoint com doc_ids 400/execution error sem auth
curl_cffi com impersonate="chrome" 200 — funciona sem proxy, sem login, sem nada

curl-impersonate é um fork do curl que replica exatamente o handshake TLS do Chrome (cipher suites, extensões, ALPN, ordem dos headers). O curl_cffi é o binding Python. Com isso, o Instagram vê a request como vinda de um Chrome real.

Trade-off: curl_cffi é sync (não async). Resolvemos rodando o scraper em run_in_executor() pra não bloquear o event loop do FastAPI.

Dados extraídos por Reel

  • URL (instagram.com/reel/{code})
  • Data de publicação (taken_at)
  • Views (play_count)
  • Likes (like_count)
  • Comentários (comment_count)
  • Engagement rate (calculado: (likes + comments) / views * 100)
  • Legenda (caption.text) — bônus
  • Duração (video_duration)
  • Cross-post Facebook (clips_metadata.is_shared_to_fb + has_shared_to_fb) — super bônus
  • Parceria paga (is_paid_partnership)
  • Música (título + artista)
  • Thumbnail e video URLs
  • Coautores (coauthor_producers)

Como funciona a detecção de cross-post Facebook

O Instagram expõe dois campos na API de Reels:

  • clips_metadata.is_shared_to_fb — flag booleana no metadata do clip
  • has_shared_to_fb — flag numérica no objeto media (0 ou 1)

Se qualquer um dos dois for truthy, marcamos cross_post_fb: true. Não é heurística — são campos reais da API.

Métricas agregadas

  • Views médio: média de play_count dos 20 Reels
  • Engagement rate médio: média dos ERs individuais
  • Frequência de posts: intervalo médio em dias entre publicações
  • Cross-posts FB: contagem de Reels compartilhados no Facebook
  • Top hashtags: hashtags mais usadas nas legendas (top 10)

Endpoints

Método Rota Body Retorno
POST /api/scrape { "username": "..." } { profile, reels, metrics }
POST /api/analyze { profile, reels, metrics, api_key } { nicho, engajamento, audiencia, parcerias, pontos_fortes, pontos_fracos, recomendacao, tags, nota_geral }
GET /api/proxy/image?url=... imagem (proxy)
GET /* arquivos estáticos

Estrutura do projeto

instagram-scraper/
  docker-compose.yml
  Dockerfile
  requirements.txt
  main.py              — FastAPI app, rotas, serve estáticos
  scraper.py           — scraping Instagram (curl_cffi + impersonate Chrome)
  analyzer.py          — integração OpenAI (análise por IA com gpt-4o-mini)
  frontend/
    index.html
    css/style.css
    js/app.js

O que priorizei e por quê

Priorizei o backend/scraper. O scraping do Instagram é a parte mais difícil e frágil do desafio — a API muda, bloqueia, exige workarounds. Um frontend bonito com dados mockados não demonstra competência técnica. Um scraper que funciona de verdade e entrega dados reais sim.

O frontend é funcional e usável — interface completa com perfil, KPIs, grid de Reels com thumbnails 9:16, modal com video player e métricas detalhadas, gráfico de tendência de views (CSS puro), tabela com sorting por coluna, export CSV, e seção de relatório IA. Tudo responsivo (mobile 2 colunas, tablet 3, desktop 4).

Processo com IA

Usei Claude Code com Opus 4.6 como par de programação durante todo o desenvolvimento.

O que a IA gerou

  • Estrutura inicial do projeto (Dockerfile, docker-compose, FastAPI boilerplate)
  • Parsing dos campos da API do Instagram (_parse_reel, calculate_metrics)
  • Template do frontend HTML/CSS/JS

O que eu mudei / direcionei

  • A decisão de usar curl_cffi — depois de ver o 429, eu sugeri testar curl-impersonate baseado no conhecimento de que Instagram faz TLS fingerprinting. A IA testou e confirmou
  • Abordagem de proxy (iProyal) — eu sugeri integrar proxy residencial como primeira tentativa. Quando não resolveu sozinho, a investigação revelou que o problema era TLS, não IP
  • Estrutura dos endpoints e campos — defini quais dados extrair e como organizar a resposta baseado no briefing do desafio
  • Trade-offs de arquitetura — decisão de usar sync curl_cffi com run_in_executor em vez de reescrever tudo async

Onde pesquisei por conta própria

  • Documentação do iProyal (via browser) pra formato de proxy
  • GitHub do curl-impersonate pra entender o mecanismo de TLS fingerprinting
  • Testes manuais com curl pra validar que o problema era fingerprint e não autenticação

Bônus implementados

  • Legenda de cada Reel (caption.text)
  • Cross-post Facebook (campos is_shared_to_fb + has_shared_to_fb)
  • Análise por IA (OpenAI gpt-4o-mini, prompt de social media manager sênior, relatório estruturado com nota, pontos fortes/fracos, recomendações)
  • Detecção de nicho (via análise IA — hashtags, música, conteúdo)
  • Gráfico de tendência (barras CSS puro, tooltips com views e data)
  • Tabela com sorting (clique no cabeçalho para ordenar por qualquer coluna)
  • Export CSV (download com todos os dados dos 20 Reels)
  • Modal detalhado (video player + todas as métricas por Reel)
  • Responsivo (mobile, tablet, desktop)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors