Hai un DOI, la pagina dell'editore chiede il login e a te serve solo leggere la sezione dei metodi. Molto spesso esiste da qualche parte una copia legale e gratuita: il repository dell'università dell'autore, un preprint, PubMed Central. A mano bisogna provare quattro o cinque siti. In codice bastano poche API pubbliche chiamate in un ordine sensato.
L'ordine qui sotto è quello del pulsante View nello spazio di lavoro Paper dell'app desktop osFoundry, il cui backend prova ogni fonte a turno finché una restituisce un vero PDF (la pagina dello spazio di lavoro per scrivere e ricercare articoli mostra la schermata intorno). Gli esempi usano due DOI reali sulla stessa domanda: un telefono appoggiato sulla scrivania danneggia la concentrazione?
-
10.1037/tmb0000123, una meta-analisi del 2024 (Hartanto et al.) -
10.1086/691462, il molto citato studio «Brain Drain» del 2017 (Ward et al.)
Chiedi a OpenAlex tutte le posizioni, non solo il flag open access
OpenAlex elenca ogni posto in cui un articolo è ospitato e, per ciascuno, se c'è un PDF diretto:
curl -s "https://api.openalex.org/works/doi:10.1037/tmb0000123?select=open_access,best_oa_location,locations"
Per la meta-analisi restituisce tre posizioni: il link al PDF dell'editore, un PDF nel repository della James Cook University e una landing page nella biblioteca della Singapore Management University.
Per l'articolo Brain Drain la stessa richiesta mostra "is_oa": true e "oa_status": "green", ma ogni pdf_url è null. L'unica copia aperta è la landing page di un repository. Quindi is_oa dice che una versione gratuita esiste da qualche parte, non che puoi scaricarla. Leggi pdf_url su ogni posizione.
Senza chiave API, le richieste a OpenAlex condividono un piccolo budget giornaliero gratuito con tutti quelli che usano il tuo indirizzo IP, e meta.notice nella risposta lo segnala. Una chiave gratuita (api_key= nella query) ti dà un budget tuo, e l'app ne legge una da OPENALEX_API_KEY se la imposti.
Controlla i primi cinque byte
Anche un pdf_url è solo una promessa. Gli editori restituiscono muri di cookie, controlli anti-bot e pagine di login con stato 200 e corpo HTML. Il codice Rust dell'app accetta un download solo se il corpo inizia con il magic number del PDF:
if !fetched.bytes.starts_with(b"%PDF-") {
self.tried.push(format!("{source}: link was a web page, not a PDF"));
return None;
}
La stessa idea in Python, con la sola libreria standard:
import json
import urllib.error
import urllib.request
UA = {"User-Agent": "paper-finder/1.0"}
def get(url):
with urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=30) as r:
return r.read()
def pdf_links(doi):
try:
work = json.loads(get(f"https://api.openalex.org/works/doi:{doi}"
"?select=best_oa_location,locations"))
except urllib.error.HTTPError as e:
if e.code == 404:
return []
raise
links = []
for loc in [work.get("best_oa_location")] + work.get("locations", []):
url = (loc or {}).get("pdf_url")
if url and url not in links:
links.append(url)
return links
def first_real_pdf(doi):
tried = []
for url in pdf_links(doi)[:4]:
try:
body = get(url)
except (urllib.error.URLError, TimeoutError) as e:
tried.append(f"{url}: {e}")
continue
if body[:5] == b"%PDF-":
return url, body
tried.append(f"{url}: a web page, not a PDF")
raise LookupError("no free PDF; tried: " + "; ".join(tried or ["no pdf_url at all"]))
Sulla meta-analisi il link dell'editore ha rifiutato lo script e la copia del repository è arrivata come PDF da 1,6 MB. Su Brain Drain fallisce subito con «no pdf_url at all». Tenere l'elenco di cosa è stato provato e perché è fallito vale le righe in più; un semplice «nessun testo completo» ti rimanda a fare tutto a mano.
Anche il limite di quattro link per ricerca e la deduplicazione degli URL vengono dall'app. Alcuni articoli hanno una dozzina di posizioni che puntano tutte allo stesso file.
Il resto della catena
La ricerca su OpenAlex è solo un passo. L'ordine completo dell'app:
- Il link al PDF arrivato con il risultato di ricerca, se c'era.
- arXiv. Un DOI del tipo
10.48550/arXiv.<id>è un articolo arXiv, ehttps://arxiv.org/pdf/<id>è sempre gratuito. - Le copie aperte del DOI, da Unpaywall o da OpenAlex. Unpaywall (
https://api.unpaywall.org/v2/<doi>?email=<your email>) restituiscebest_oa_location.url_for_pdfe un elencooa_locations, e rifiuta le richieste senza un indirizzo e-mail vero. I due servizi sono della stessa organizzazione non profit, OurResearch, quindi l'app interroga Unpaywall quandoUNPAYWALL_EMAILè impostata e altrimenti legge le posizioni di OpenAlex, mai entrambi. - CORE. La sua API v3 (
/v3/search/works?q=doi:"<doi>", con una chiave bearer) aggrega copie dai repository e spesso restituisce direttamente il testo estratto infullText. L'app usa quel testo se supera i 500 caratteri, altrimenti prova ildownloadUrl. - PubMed Central. Con un PMCID, Europe PMC serve l'articolo intero come XML JATS all'indirizzo
https://www.ebi.ac.uk/europepmc/webservices/rest/<PMCID>/fullTextXML. Non è un PDF, ma ha sezioni, figure e riferimenti in una struttura che si converte bene in HTML.
Gli abstract arrivano come indice invertito
Un ultimo dettaglio di OpenAlex su cui si inciampa spesso: non esiste un campo abstract. Si riceve abstract_inverted_index, una mappa da ogni parola alle posizioni in cui compare, e il testo va ricostruito da soli:
words = {pos: w for w, positions in idx.items() for pos in positions}
abstract = " ".join(words[i] for i in sorted(words))
Basta per i risultati di ricerca e per una prima selezione. Per l'articolo Brain Drain, la cui unica copia aperta è una pagina di repository, l'abstract più il testo completo della meta-analisi è circa il massimo a cui arriva la via gratuita, ed è di solito il momento di controllare se la tua biblioteca ha accesso.

Top comments (0)