Use Cases

coleta autorizada de pesquisa acadêmica com resolução de CAPTCHA

Toda revisão de literatura em larga escala esbarra no mesmo obstáculo: depois de algumas dezenas de requisições, o Google Acadêmico, o PubMed ou o Web of Science passam a exigir um desafio CAPTCHA a cada página. Quem faz bibliometria, meta-análise ou mapeamento sistemático de citações não pode parar o pipeline para resolver isso manualmente. A saída é integrar a resolução automática ao próprio coletor: a API da CaptchaAI recebe o desafio, devolve o token e o script segue coletando.

Este guia mostra onde cada base acadêmica costuma acionar o CAPTCHA, como montar um coletor de dados de citação em Python e como manter os limites de requisição sob controle para não ser bloqueado.


Onde o CAPTCHA trava a coleta de dados acadêmicos

Cada base tem um gatilho diferente — algumas reagem a volume de consultas, outras a downloads em lote. Conhecer o gatilho ajuda a decidir onde investir no atraso entre requisições.

Fonte Tipo de CAPTCHA Gatilho Dados
Google Acadêmico reCAPTCHA v3 Consultas de alto volume Citações, artigos
PubMed reCAPTCHA v2 Pesquisas repetidas Literatura biomédica
Web of Science Cloudflare Turnstile Downloads em massa Métricas de citação
Scopus reCAPTCHA v2 Operações de exportação Dados bibliométricos
IEEE Xplore reCAPTCHA v2 Busca + download Artigos de engenharia
JSTOR reCAPTCHA v2 Acesso a páginas Humanidades/ciências sociais

Como montar um coletor de dados de citação em Python

O coletor abaixo faz três coisas: busca artigos por consulta, verifica se a resposta veio com um desafio CAPTCHA embutido e, se veio, resolve o desafio antes de reenviar a requisição. A função solve_captcha encapsula a chamada à API da CaptchaAI — envia o desafio para in.php e faz polling em res.php até o token voltar pronto ou o tempo limite estourar.

import requests
import time
import re
from bs4 import BeautifulSoup
import csv

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("Timeout")


class AcademicScraper:
    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 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def search_papers(self, search_url, query, max_pages=10):
        """Search academic database for papers matching query."""
        all_papers = []

        for page in range(max_pages):
            url = f"{search_url}?q={query}&start={page * 10}"
            resp = self.session.get(url, timeout=30)

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

            papers = self._parse_results(resp.text)
            if not papers:
                break  # No more results

            all_papers.extend(papers)
            print(f"Page {page + 1}: {len(papers)} papers")
            time.sleep(5)  # Respectful delay

        return all_papers

    def get_paper_details(self, paper_url):
        """Get detailed metadata for a single paper."""
        resp = self.session.get(paper_url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, paper_url)

        soup = BeautifulSoup(resp.text, "html.parser")
        return {
            "title": self._safe_text(soup, "h1, .article-title"),
            "authors": self._safe_text(soup, ".authors, .author-list"),
            "abstract": self._safe_text(soup, ".abstract, #abstract"),
            "doi": self._safe_text(soup, ".doi, [data-doi]"),
            "journal": self._safe_text(soup, ".journal-name, .publication"),
            "year": self._safe_text(soup, ".pub-date, .year"),
            "citations": self._safe_text(soup, ".citation-count, .cited-by"),
        }

    def export_to_csv(self, papers, filename):
        """Export collected papers to CSV."""
        if not papers:
            return
        keys = papers[0].keys()
        with open(filename, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=keys)
            writer.writeheader()
            writer.writerows(papers)
        print(f"Exported {len(papers)} papers to {filename}")

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

    def _solve_and_retry(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_results(self, html):
        soup = BeautifulSoup(html, "html.parser")
        papers = []
        for item in soup.select(".gs_r, .search-result, article.result"):
            title_el = item.select_one("h3 a, .result-title a")
            if title_el:
                papers.append({
                    "title": title_el.get_text(strip=True),
                    "url": title_el.get("href", ""),
                    "snippet": self._safe_text(item, ".gs_rs, .abstract-snippet"),
                    "authors": self._safe_text(item, ".gs_a, .author-info"),
                })
        return papers

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


# Usage — Literature review
scraper = AcademicScraper(
    proxy="http://user:[email protected]:5000"
)

papers = scraper.search_papers(
    "https://scholar.example.com/scholar",
    query="machine learning CAPTCHA solving",
    max_pages=5,
)

# Get details for top papers
detailed = []
for paper in papers[:20]:
    if paper["url"]:
        detail = scraper.get_paper_details(paper["url"])
        detailed.append(detail)
        time.sleep(3)

scraper.export_to_csv(detailed, "literature_review.csv")

Repare que _has_captcha só olha para três marcadores no HTML (data-sitekey, g-recaptcha, cf-turnstile). Isso é suficiente para as seis fontes da tabela acima, porque nenhuma delas usa hCaptcha ou FunCaptcha — tipos que a CaptchaAI hoje não suporta. Se você adaptar o coletor para outra base acadêmica, confirme o tipo de desafio antes de assumir que o mesmo fluxo funciona.


Mapeando a rede de citações com análise bibliométrica

Depois de coletar o primeiro lote de artigos, um passo comum em bibliometria é seguir os links "citado por" para reconstruir a rede de citações ao redor de um tema. A função abaixo reaproveita o mesmo AcademicScraper e faz a busca em profundidade, resolvendo CAPTCHA novamente sempre que a página de citações aciona um desafio.

def bibliometric_analysis(scraper, seed_papers, depth=2):
    """Follow citations to build a citation network."""
    visited = set()
    network = []

    def _crawl(paper_url, current_depth):
        if current_depth > depth or paper_url in visited:
            return
        visited.add(paper_url)

        try:
            details = scraper.get_paper_details(paper_url)
            network.append(details)

            # Follow "cited by" links
            resp = scraper.session.get(f"{paper_url}/citations", timeout=30)
            if scraper._has_captcha(resp.text):
                resp = scraper._solve_and_retry(resp.text, f"{paper_url}/citations")

            citations = scraper._parse_results(resp.text)
            for cite in citations[:5]:  # Limit breadth
                if cite["url"]:
                    _crawl(cite["url"], current_depth + 1)
                    time.sleep(3)

        except Exception as e:
            print(f"Error crawling {paper_url}: {e}")

    for paper in seed_papers:
        _crawl(paper["url"], 0)

    return network

Mantenha depth baixo (2 já cobre a maioria dos mapeamentos de área) — cada nível multiplica o número de páginas visitadas e, com ele, a chance de acionar um novo CAPTCHA a cada salto.


Quanto esperar de cada fonte: limites de requisição recomendados

Fonte Atraso recomendado Máximo de páginas/hora
Google Acadêmico 10–15 s 40–50
PubMed 3–5 s 100
Web of Science 5–10 s 60
Scopus 5–10 s 60
IEEE 3–5 s 100
JSTOR 5–10 s 60

Sites acadêmicos banem IPs rapidamente — em geral mais rápido que um site de e-commerce comum. Trate esses números como teto, não como meta: comece mais conservador e só reduza o atraso depois de confirmar que a fonte não está sinalizando o seu IP.

No Brasil, boa parte do acesso a essas bases acontece via VPN institucional — o caso mais comum é o Portal de Periódicos da CAPES, que centraliza o login para dezenas de editoras. A técnica descrita aqui não muda nesse cenário: o proxy institucional cuida da sessão autenticada e a CaptchaAI resolve o desafio, de forma independente uma da outra. Vale também lembrar que, se a coleta envolver dados de autores, e-mails ou afiliações institucionais, as obrigações da LGPD sobre tratamento de dados pessoais se aplicam da mesma forma que em qualquer outro projeto de coleta — trate isso como parte do desenho do pipeline, não como detalhe posterior.


Problemas comuns na coleta acadêmica

Antes de ajustar código, vale checar se o problema é de fato o CAPTCHA ou algo anterior a ele — os dois sintomas mais comuns (nenhum resultado retornado e resumo faltando) têm causas diferentes.

Problema Causa Correção
CAPTCHA em toda pesquisa IP sinalizado pela base acadêmica Trocar de proxy, aumentar o atraso para 15 s ou mais
Nenhum resultado retornado A resposta é a própria página de CAPTCHA Verificar CAPTCHA antes de fazer o parsing
Resumo (abstract) faltando Conteúdo atrás de paywall Usar proxy institucional ou fonte de acesso aberto
Base bloqueia o IP Limite de requisições excedido Aguardar 30 minutos, trocar de IP
Exportação limitada O site limita downloads em massa Baixar em lotes menores

Perguntas frequentes

Posso coletar dados de bases acadêmicas de forma automatizada?

Metadados públicos (títulos, autores, resumos) geralmente são acessíveis. O acesso ao texto completo depende do licenciamento de cada editora. O PubMed, por exemplo, oferece explicitamente acesso programático pela API E-utilities — prefira sempre uma API oficial quando ela existir.

O reCAPTCHA v3 do Google Acadêmico exige alguma configuração diferente do v2?

Sim. O reCAPTCHA v3 não mostra um quadro de seleção para o usuário clicar — ele roda em segundo plano e retorna uma pontuação de risco. Na prática, para o coletor isso significa passar method="userrecaptcha" com a sitekey correta e tratar o token da mesma forma; a diferença fica na frequência das requisições, porque pontuações baixas tendem a aparecer quando o padrão de acesso foge do de um usuário humano.

O que fazer quando o resumo do artigo não aparece mesmo depois de resolver o CAPTCHA?

Normalmente é paywall, não CAPTCHA. Resolver o desafio te devolve o acesso à página; se o conteúdo completo ainda estiver bloqueado, o problema é licenciamento — use acesso institucional ou a versão em acesso aberto do artigo, quando existir.

O proxy institucional da minha universidade funciona junto com a CaptchaAI?

Sim. Configure o proxy institucional na sessão de navegação e a chave da CaptchaAI na resolução de CAPTCHA — as duas coisas funcionam de forma independente, uma não interfere na outra.

Existe limite de quantas requisições por hora eu posso enviar à API da CaptchaAI?

O limite prático não vem da CaptchaAI, e sim da base acadêmica que você está consultando (veja a tabela de atrasos recomendados acima). A API processa cada desafio enviado; quem define o ritmo seguro de requisições é o comportamento anti-bot do site de origem.


Leia também


Acelere sua revisão de literatura — crie sua chave da CaptchaAI e automatize a coleta de dados acadêmicos.

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