Use Cases

Coleta de dados de pesquisa em mídias sociais com tratamento de CAPTCHA

Uma coleta de dados sociais raramente quebra no parser. Ela quebra na terceira hora, quando a plataforma decide que aquele padrão de acesso merece um desafio e o script passa a receber HTML de CAPTCHA no lugar do perfil. A saída não é insistir mais rápido: é baixar o ritmo, alinhar a rota de saída ao tráfego esperado e resolver o desafio por API.

Este guia monta esse fluxo em Python, do escopo autorizado ao código que envia o desafio à CaptchaAI e devolve o token à página. Os tipos daqui — reCAPTCHA v2, reCAPTCHA v3 e Cloudflare Turnstile — são os que a CaptchaAI resolve; hCaptcha e FunCaptcha não são suportados.

Onde o CAPTCHA aparece na coleta de dados sociais

O desafio quase nunca é o primeiro obstáculo: ele entra depois de um gatilho — volume por IP, sessão longa demais, buscas repetidas com o mesmo cookie. O tipo exibido define se o coletor precisa do fluxo de token do reCAPTCHA, do Turnstile ou dos dois.

Plataforma Tipo mais comum Gatilho típico
Instagram reCAPTCHA v2 Login, busca, acesso a perfil
Facebook reCAPTCHA v2 Login, buscas repetidas
Twitter/X Cloudflare Turnstile Login, acesso à API
TikTok reCAPTCHA v3 Visualização de perfil, busca
LinkedIn Cloudflare Turnstile Coleta de perfis públicos
Reddit reCAPTCHA v2 Login, navegação intensa

Delimite o escopo autorizado antes de escrever o coletor

Dado público não é dado livre de obrigações: a LGPD trata perfis, biografias e posts identificáveis como dados pessoais mesmo abertos na web. Em Portugal, vale a mesma leitura sob o RGPD.

  • Escreva a finalidade antes do primeiro requests.get: pesquisa de mercado, monitoramento da marca própria, estudo acadêmico aprovado.
  • Colete só os campos que a finalidade exige, agregue cedo e defina prazo de retenção.
  • Respeite os Termos de Serviço e os limites de requisição da plataforma.
  • Registre o que foi coletado, quando e por qual rota: auditoria interna e um pedido da ANPD pedem o mesmo log.

Ritmo de requisições: o ajuste que mais reduz CAPTCHA

Resolver desafio por API é o plano B; o plano A é não provocar tantos. Comece conservador, meça a taxa de desafios por 100 requisições e só então aperte.

Plataforma Intervalo seguro Sessão máxima
Instagram 1 requisição / 10 s 5 min, depois pausa
Facebook 1 requisição / 5 s 10 min
Twitter/X 1 requisição / 3 s 15 min
TikTok 1 requisição / 5 s 5 min
LinkedIn 1 requisição / 10 s 5 min
Reddit 1 requisição / 2 s 30 min

Coletor de pesquisa social em Python

A classe abaixo faz três coisas: mantém a sessão, detecta que a resposta virou um desafio e o resolve antes de reenviar. solve_captcha envia a tarefa para in.php e consulta res.php até o token ficar pronto; o mesmo par de endpoints serve reCAPTCHA e Turnstile, mudando só o method.

import requests
import time
import re

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY,
        "method": method,
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Solve timeout")


class SocialMediaResearcher:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "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",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def authenticate(self, login_url, credentials, sitekey):
        """Login with CAPTCHA handling."""
        # Load login page
        self.session.get(login_url)

        # Solve CAPTCHA
        token = solve_captcha("userrecaptcha", sitekey, login_url)

        # Submit login
        resp = self.session.post(login_url, data={
            **credentials,
            "g-recaptcha-response": token,
        })
        return resp.status_code == 200

    def collect_profiles(self, profile_urls):
        """Collect public profile data with CAPTCHA handling."""
        profiles = []

        for url in profile_urls:
            try:
                resp = self.session.get(url, timeout=30)

                # Handle CAPTCHA if triggered
                if self._has_captcha(resp.text):
                    resp = self._handle_captcha(resp.text, url)

                profiles.append({
                    "url": url,
                    "data": self._parse_profile(resp.text),
                    "status": "success",
                })
                time.sleep(5)  # Slow down between profiles

            except Exception as e:
                profiles.append({
                    "url": url,
                    "error": str(e),
                    "status": "failed",
                })

        return profiles

    def _has_captcha(self, html):
        return any(tag in html.lower() for tag in [
            'data-sitekey', 'g-recaptcha', 'cf-turnstile',
            'challenge-platform', 'captcha',
        ])

    def _handle_captcha(self, html, url):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if not match:
            return self.session.get(url)

        sitekey = match.group(1)

        if 'cf-turnstile' in html:
            token = solve_captcha("turnstile", sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        else:
            token = solve_captcha("userrecaptcha", sitekey, url)
            return self.session.post(url, data={"g-recaptcha-response": token})

    def _parse_profile(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        return {
            "name": self._safe_text(soup, "h1, .profile-name"),
            "bio": self._safe_text(soup, ".bio, .profile-bio"),
            "followers": self._safe_text(soup, "[data-followers], .followers"),
            "posts": self._safe_text(soup, "[data-posts], .posts-count"),
        }

    def _safe_text(self, soup, selector):
        el = soup.select_one(selector)
        return el.get_text(strip=True) if el else ""

Pesquisa por hashtag e tendências

Na coleta por hashtag é a paginação que denuncia o robô: dezenas de páginas iguais, mesmo intervalo, mesmo cabeçalho. Mantenha o time.sleep entre as páginas e trate o desafio dentro do laço, sem reiniciar a sessão.

def research_hashtag(hashtag, platform_url, pages=5):
    """Collect posts for a specific hashtag."""
    researcher = SocialMediaResearcher(
        proxy="http://user:[email protected]:5000"
    )

    all_posts = []
    for page in range(pages):
        url = f"{platform_url}/explore/tags/{hashtag}?page={page}"
        resp = researcher.session.get(url, timeout=30)

        if researcher._has_captcha(resp.text):
            resp = researcher._handle_captcha(resp.text, url)

        from bs4 import BeautifulSoup
        soup = BeautifulSoup(resp.text, "html.parser")
        posts = soup.select(".post-item, article")
        for post in posts:
            all_posts.append({
                "text": post.get_text(strip=True)[:500],
                "hashtag": hashtag,
                "page": page,
            })

        time.sleep(5)

    return all_posts

Monitoramento de menções à marca

Cenário comum em agências de social listening no Brasil: um scan diário de madrugada cruzando palavras-chave em duas ou três plataformas, com relatório em JSON para o cliente. Rodar os workers em sa-east-1 (São Paulo) corta o RTT por desafio.

import json
from datetime import datetime


class BrandMonitor:
    def __init__(self, brand_name, keywords, proxy=None):
        self.brand = brand_name
        self.keywords = keywords
        self.researcher = SocialMediaResearcher(proxy=proxy)

    def daily_scan(self, platform_urls):
        """Run daily brand mention scan across platforms."""
        report = {
            "brand": self.brand,
            "date": datetime.now().isoformat(),
            "platforms": {},
        }

        for name, url in platform_urls.items():
            mentions = []
            for keyword in self.keywords:
                search_url = f"{url}/search?q={keyword}"
                try:
                    resp = self.researcher.session.get(search_url, timeout=30)

                    if self.researcher._has_captcha(resp.text):
                        resp = self.researcher._handle_captcha(
                            resp.text, search_url,
                        )

                    from bs4 import BeautifulSoup
                    soup = BeautifulSoup(resp.text, "html.parser")
                    results = soup.select(".search-result, .post")
                    mentions.append({
                        "keyword": keyword,
                        "count": len(results),
                    })
                    time.sleep(5)
                except Exception as e:
                    mentions.append({
                        "keyword": keyword,
                        "error": str(e),
                    })

            report["platforms"][name] = mentions

        return report


# Usage
monitor = BrandMonitor(
    brand_name="CaptchaAI",
    keywords=["captchaai", "captcha ai", "captcha solver"],
    proxy="http://user:[email protected]:5000",
)
report = monitor.daily_scan({
    "twitter": "https://twitter-alternative.example.com",
    "reddit": "https://www.reddit.com",
})
print(json.dumps(report, indent=2))

Alinhe a rota de saída ao tráfego que a plataforma espera

Não existe rota mágica; existe coerência. Um User-Agent de iPhone saindo de uma faixa de datacenter é uma contradição que qualquer detector lê em milissegundos. Use egress autorizado, com rotação dentro do seu pool.

  • Instagram e TikTok esperam acesso de app móvel: egress em rede celular autorizada destoa menos.
  • Facebook e Twitter/X marcam faixas de datacenter com rigor — prefira banda larga contratada.
  • LinkedIn espera IPs de desktop e corporativos, típicos de ISP contratado.
  • Reddit limita por IP: rotacione dentro do pool e mantenha o intervalo.

Quanto custa manter essa coleta de pé

A conta da CaptchaAI não é por desafio resolvido: os planos são por thread simultânea, com resoluções ilimitadas no mês. Uma thread é um desafio em andamento; quando ele termina, ela pega o próximo. O que dimensiona o plano é a concorrência do coletor.

  • BASIC (US$ 15/mês, 5 threads) — scan diário de marca, execução sequencial.
  • STANDARD (US$ 30/mês, 15 threads) — hashtags em várias plataformas em paralelo.
  • ADVANCE (US$ 90/mês, 50 threads) — pesquisa contínua com dezenas de workers.

Quando a coleta trava: diagnóstico rápido

Sintoma Causa provável O que fazer
CAPTCHA em toda requisição IP marcado Rotacione o egress, aumente o intervalo
Conta bloqueada Ações demais na sessão Reduza a frequência, distribua no dia
Página vazia Conteúdo atrás do login Autentique antes de percorrer a lista
Ciclo de desafio do Cloudflare Sinais de navegador inconsistentes Navegador real com Puppeteer em QA
Conteúdo diferente do navegador Idioma, região ou cookie divergentes Alinhe a rota e o Accept-Language

Perguntas frequentes

Quais tipos de CAPTCHA a CaptchaAI resolve nesse fluxo?

Os três das tabelas acima: reCAPTCHA v2, reCAPTCHA v3 e Cloudflare Turnstile. Há ainda GeeTest v3, imagem e OCR, grade de imagens e BLS. hCaptcha e FunCaptcha não são suportados; GeeTest v4 está como "em breve".

Como dimensiono o plano para um scan diário?

Conte a concorrência, não o volume. Com 5 workers em paralelo, 5 threads dão conta; com 40, o degrau é o ADVANCE. Como as resoluções são ilimitadas por thread, mais frequência não muda a fatura.

A LGPD se aplica a dados públicos de redes sociais?

Sim. Estar público não retira o dado da esfera da LGPD quando ele identifica alguém. Defina finalidade, minimize os campos e agregue cedo. Para o seu caso concreto, consulte o jurídico da empresa.

Preciso de um navegador headless ou o requests basta?

Na maioria dos casos requests com sessão persistente resolve e gasta menos CPU. O headless só se justifica quando a página monta o conteúdo por JavaScript ou o desafio depende de um callback.

E se a plataforma oferecer uma API oficial?

Use a API oficial: mais estável, mais barata em manutenção e alinhada aos Termos de Serviço. Reserve a coleta com CAPTCHA para o que a API não expõe.

Leituras relacionadas


Mantenha sua pesquisa em redes sociais rodando sem parar a cada desafio — obtenha sua chave da CaptchaAI e resolva o CAPTCHA dentro do próprio coletor.

Os comentários estão desativados para este artigo.