Estrarre paper, autori e conteggi delle citazioni da Google Scholar con Python
Interroga Google Scholar per un argomento e ottieni paper, anni e conteggi delle citazioni in JSON, costruito attorno a un endpoint così lento da dover girare come job in batch. Testato ad agosto 2026.

Stai costruendo una literature review, o una dashboard che traccia un argomento di ricerca nel tempo, e ti servono i paper: titolo, autori, anno, quante volte ciascuno è stato citato. Il modo di default per ottenerli è una scheda del browser su Google Scholar e un foglio di calcolo che riempi a mano, una pagina di risultati alla volta. Google Scholar non ha un’API pubblica ufficiale, quindi la maggior parte degli strumenti attorno ad esso è esattamente questo: una persona che copia righe.
La richiesta
Una chiamata a /v1/scholar prende la stessa query che digiteresti nella casella di ricerca, più alcuni parametri opzionali: language, page, results, e as_ylo / as_yhi se vuoi che sia Scholar stesso a limitare la risposta a un intervallo di anni prima che ti torni indietro qualcosa.
Ci mette quasi otto secondi, quindi non aspettarlo
Nelle misurazioni che abbiamo fatto il 27 agosto 2026, /v1/scholar aveva un tempo di risposta mediano intorno ai 7,9 secondi, l’endpoint più lento che vendiamo. Non è un bug da aggirare, è quello che costa interrogare un indice di letteratura così grande. Progetta di conseguenza: recupera un batch di argomenti in un job in background o in una coda, scrivi il JSON nel tuo storage, e fai leggere da lì la tua dashboard o il tuo export. Non mettere questo endpoint in un percorso di richiesta dove una persona sta a guardare uno spinner.
Paper, anni e citazioni dallo stesso JSON
Ecco una richiesta per “web scraping api” che filtra i paper dal 2020 in poi e ordina quello che resta per numero di citazioni:
import re
import requests
API_KEY = "faf_live_..." # la tua chiave 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"]}')
Abbiamo eseguito esattamente questo scrivendo il post (agosto 2026). Sono tornati dieci paper per 5 crediti; sei erano dal 2020 in poi, e ordinando per citazioni una survey del 2021 sulle tecniche di web scraping è finita in cima con 651 citazioni, davanti a un paper del 2024 a 14 e un paper del 2025 che non aveva alcun conteggio di citazioni, che il codice sopra valuta correttamente come zero invece di andare in crash.
Cosa è solido, e cosa devi calcolarti da solo
Ogni voce in scholar_results ti dà title, title_link, uno snippet, e una lista authors di {name, link, author_id}, quando Scholar associa proprio autori nominati al risultato (libri e alcuni risultati nel nostro campione sono tornati con una lista vuota). Due cose che ci si aspetterebbe non sono lì come campi puliti. Non c’è uno year a sé stante: è incorporato nella stringa in testo libero displayed_link insieme alla sede di pubblicazione, motivo per cui il codice sopra lo estrae con una regex invece di leggere una chiave. E non c’è nemmeno un conteggio citazioni come intero semplice, solo inline_links.cited_by.total, una stringa che per la maggior parte dei risultati si legge “Cited by 418” ma per una voce nel nostro campione si leggeva “Related articles”, il che significa che quel risultato non ha alcun numero di citazioni da mostrare. Tratta entrambi come testo estratto alla meglio da una stringa di citazione, non campi strutturati garantiti, e scrivi il fallback per il caso “mancante” prima di scrivere il percorso felice. Quando un risultato ha davvero un PDF disponibile, compare sotto resources.
La stessa query non restituisce lo stesso numero di paper
Questa ti frega se dai per scontato il contrario. Il 27 agosto 2026 abbiamo mandato la stessa identica query, graph neural networks, sei volte di fila a /v1/scholar. I risultati sono tornati in numero di 5, 10, 10, 8, 0 e 10. Una chiamata su sei non ha restituito niente, ed è stata addebitata come tutte le altre, perché la richiesta era andata a buon fine: semplicemente in quel momento Scholar non aveva nulla da dare.
Quindi non trattare una lista vuota come una risposta, e non trattare il conteggio come stabile. Riprova una volta dopo una breve pausa prima di registrare “nessun paper trovato”, e se stai confrontando i risultati di oggi con quelli della settimana scorsa, confronta per paper e non per numero, altrimenti genererai avvisi su paper che compaiono e scompaiono senza essere mai davvero spariti.
import time
def search_with_retry(query, attempts=2):
for attempt in range(attempts):
papers = search(query) # la funzione della sezione precedente
if papers:
return papers
time.sleep(2)
return []
Mettilo a bilancio: a 5 crediti a chiamata, un tasso di vuoti di uno su sei aggiunge circa il 17 per cento al costo di un giro che riprova ogni risposta vuota.
Quanto costa
/v1/scholar costa 5 crediti a chiamata, lo stesso indipendentemente da quanti paper tornano. I 500 crediti una tantum del piano gratuito coprono 100 chiamate, abbastanza per verificare la copertura dei campi sui tuoi argomenti. Sul pacchetto Scale ($99 per 300.000 crediti) sono 60.000 chiamate, $1,65 per 1.000.
La versione breve
Tratta /v1/scholar come un job in batch, non un lookup live: metti in coda gli argomenti, metti in cache il JSON dal tuo lato, e lascia che sia la tua UI a fare il filtraggio e l’ordinamento istantanei che l’endpoint stesso è troppo lento per fare su richiesta. Prendi 500 crediti gratis, senza carta e provalo su un argomento che segui davvero.