Extraindo artigos, autores e contagens de citação do Google Scholar com Python

Busque um tópico no Google Scholar e receba artigos, anos e contagens de citação como JSON, construído em torno de um endpoint lento o bastante para precisar rodar como job em lote. Testado em agosto de 2026.

Você está montando uma revisão de literatura, ou um dashboard que acompanha um tópico de pesquisa ao longo do tempo, e precisa dos artigos: título, autores, ano, quantas vezes cada um foi citado. O jeito padrão de conseguir isso é uma aba do navegador no Google Scholar e uma planilha que você preenche à mão, uma página de resultados por vez. O Google Scholar não tem uma API pública oficial, então a maioria das ferramentas em torno dele é exatamente isso: uma pessoa copiando linhas.

A requisição

Uma chamada ao /v1/scholar recebe a mesma busca que você digitaria na caixa de pesquisa, mais alguns parâmetros opcionais: language, page, results, e as_ylo / as_yhi se você quiser que o próprio Scholar restrinja a resposta a um intervalo de anos antes de qualquer coisa voltar para você.

Ela leva quase oito segundos, então não fique esperando

Em medições que fizemos em 27 de agosto de 2026, o /v1/scholar teve um tempo de resposta mediano em torno de 7,9 segundos, o endpoint mais lento que vendemos. Isso não é um bug para contornar, é o que custa consultar um índice de literatura desse tamanho. Projete pensando nisso: busque um lote de tópicos num job em segundo plano ou numa fila, grave o JSON no seu próprio armazenamento, e faça seu dashboard ou exportação ler dali. Não coloque esse endpoint num caminho de requisição onde uma pessoa fica olhando um spinner.

Artigos, anos e citações do mesmo JSON

Aqui está uma requisição para “web scraping api” que filtra artigos de 2020 em diante e ordena o que sobra por número de citações:

import re
import requests

API_KEY = "faf_live_..."  # sua chave FastAF

resp = requests.get(
    "https://api.fastaf.co/v1/scholar",
    params={"query": "web scraping api"},
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=60,
)
resp.raise_for_status()
papers = resp.json()["scholar_results"]

def citation_count(paper):
    total = paper.get("inline_links", {}).get("cited_by", {}).get("total", "")
    match = re.search(r"\d+", total)
    return int(match.group()) if match else 0

def pub_year(paper):
    match = re.search(r"\b(19|20)\d{2}\b", paper.get("displayed_link", ""))
    return int(match.group()) if match else None

recent = [p for p in papers if (pub_year(p) or 0) >= 2020]
recent.sort(key=citation_count, reverse=True)

for p in recent:
    authors = ", ".join(a["name"] for a in p.get("authors", []))
    print(f'{citation_count(p):>5}  {pub_year(p)}  {p["title"]}')

Rodamos exatamente isso enquanto escrevíamos o post (agosto de 2026). Dez artigos voltaram por 5 créditos; seis eram de 2020 em diante, e ao ordenar por citações um survey de 2021 sobre técnicas de web scraping ficou em primeiro com 651 citações, à frente de um artigo de 2024 com 14 e um artigo de 2025 que não tinha nenhuma contagem de citação, que o código acima corretamente pontua como zero em vez de quebrar.

O que é sólido, e o que você precisa calcular sozinho

Cada item em scholar_results te dá title, title_link, um snippet, e uma lista authors de {name, link, author_id} quando o Scholar chega a atribuir autores nomeados ao resultado (livros e alguns dos resultados na nossa amostra vieram com uma lista vazia). Duas coisas que as pessoas esperam não estão lá como campos limpos. Não existe um year isolado: ele vem embutido na string de texto livre displayed_link, junto com o veículo de publicação, e é por isso que o código acima extrai com uma regex em vez de ler uma chave. E também não existe uma contagem de citação como número inteiro puro, só inline_links.cited_by.total, uma string que na maioria dos resultados vem como “Cited by 418” mas veio como “Related articles” em um item da nossa amostra, o que significa que esse resultado não tem nenhum número de citação para mostrar. Trate os dois como texto de melhor esforço extraído de uma string de citação, não como campos estruturados garantidos, e escreva o fallback para “ausente” antes de escrever o caminho feliz. Quando um resultado tem PDF disponível, ele aparece em resources.

A mesma busca não retorna o mesmo número de artigos

Essa aqui vai te morder se você simplesmente assumir que não acontece. Em 27 de agosto de 2026 mandamos a mesma busca, graph neural networks, seis vezes seguidas pelo /v1/scholar. As contagens de resultado voltaram 5, 10, 10, 8, 0 e 10. Uma chamada em seis não retornou nada, e ela foi cobrada como qualquer outra chamada, porque a requisição em si teve sucesso: o Scholar simplesmente não tinha nada para dar naquele momento.

Então não trate uma lista vazia como uma resposta, e não trate a contagem como estável. Tente de novo uma vez, depois de uma pausa curta, antes de registrar “nenhum artigo encontrado”, e se você está comparando os resultados de hoje com os da semana passada, compare por artigo em vez de por contagem, ou você vai gerar alertas de artigos aparecendo e desaparecendo que nunca sumiram de verdade.

import time


def search_with_retry(query, attempts=2):
    for attempt in range(attempts):
        papers = search(query)          # a função da seção anterior
        if papers:
            return papers
        time.sleep(2)
    return []

Reserve orçamento para as novas tentativas: a 5 créditos por chamada, uma taxa de uma em seis chamadas vazias adiciona cerca de 17% ao custo de uma execução que tenta de novo toda vez que vem em branco.

Quanto custa

O /v1/scholar custa 5 créditos por chamada, o mesmo independente de quantos artigos voltam. Os 500 créditos únicos do plano gratuito cobrem 100 chamadas, suficiente para checar a cobertura de campos com seus próprios tópicos. No pacote Scale ($99 por 300.000 créditos) isso dá 60.000 chamadas, $1,65 por 1.000.

Resumindo

Trate o /v1/scholar como um job em lote, não uma consulta ao vivo: enfileire os tópicos, guarde o JSON em cache do seu lado, e deixe sua própria UI fazer a filtragem e ordenação instantânea que o endpoint em si é lento demais para fazer sob demanda. Pegue 500 créditos grátis, sem cartão e rode com um tópico que você realmente acompanha.