Du hast eine DOI, die Seite des Verlags verlangt einen Login, und du willst nur den Methodenteil lesen. Sehr oft gibt es irgendwo eine legale, kostenlose Kopie: im Repositorium der Universität der Autoren, als Preprint, in PubMed Central. Von Hand heißt das, vier oder fünf Seiten durchzuprobieren. In Code braucht es eine Handvoll öffentlicher APIs, die in sinnvoller Reihenfolge abgefragt werden.
Die Reihenfolge unten ist die des Buttons „View“ im Paper-Workspace der osFoundry-Desktop-App. Deren Backend probiert nacheinander jede Quelle, bis eine ein echtes PDF liefert (die Seite zum Workspace für wissenschaftliches Schreiben und Recherchieren zeigt den Bildschirm drumherum). Die Beispiele nutzen zwei echte DOIs zur selben Frage, nämlich ob ein Smartphone auf dem Schreibtisch die Konzentration stört:
-
10.1037/tmb0000123, eine Metaanalyse von 2024 (Hartanto et al.) -
10.1086/691462, die vielzitierte „Brain Drain“-Studie von 2017 (Ward et al.)
OpenAlex nach allen Fundorten fragen, nicht nur nach dem Open-Access-Flag
OpenAlex listet jeden Ort, an dem ein Paper liegt, und für jeden, ob es dort ein direktes PDF gibt:
curl -s "https://api.openalex.org/works/doi:10.1037/tmb0000123?select=open_access,best_oa_location,locations"
Für die Metaanalyse kommen drei Fundorte zurück: der PDF-Link des Verlags, ein PDF im Repositorium der James Cook University und eine Landingpage in der Bibliothek der Singapore Management University.
Beim Brain-Drain-Paper zeigt dieselbe Anfrage "is_oa": true und "oa_status": "green", aber jedes pdf_url ist null. Die einzige offene Kopie ist die Landingpage eines Repositoriums. is_oa sagt also nur, dass irgendwo eine freie Version existiert, nicht, dass du sie herunterladen kannst. Lies deshalb pdf_url an jedem Fundort.
Ohne API-Key teilen sich OpenAlex-Anfragen ein kleines kostenloses Tagesbudget mit allen anderen hinter deiner IP-Adresse, und meta.notice in der Antwort weist darauf hin. Ein kostenloser Key (api_key= in der Abfrage) gibt dir ein eigenes Budget; die App liest einen aus OPENALEX_API_KEY, wenn du ihn setzt.
Die ersten fünf Bytes prüfen
Auch ein pdf_url ist nur ein Versprechen. Verlage liefern Cookie-Wände, Bot-Prüfungen und Login-Seiten mit Status 200 und HTML im Body. Der Rust-Code der App akzeptiert einen Download nur, wenn der Body mit der PDF-Magic-Number beginnt:
if !fetched.bytes.starts_with(b"%PDF-") {
self.tried.push(format!("{source}: link was a web page, not a PDF"));
return None;
}
Dieselbe Idee in Python, nur mit der Standardbibliothek:
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"]))
Bei der Metaanalyse lehnte der Verlagslink das Skript ab, und die Repositoriumskopie kam als 1,6 MB großes PDF zurück. Bei Brain Drain bricht es sofort mit „no pdf_url at all“ ab. Die Liste dessen, was versucht wurde und warum es scheiterte, ist die paar Zeilen wert; ein bloßes „kein Volltext“ schickt dich wieder zurück zur Handarbeit.
Auch die Begrenzung auf vier Links pro Abfrage und das Entfernen doppelter URLs stammen aus der App. Manche Paper haben ein Dutzend Fundorte, die alle auf dieselbe Datei zeigen.
Der Rest der Kette
Die OpenAlex-Abfrage ist nur ein Schritt. Die vollständige Reihenfolge der App:
- Der PDF-Link, der schon beim Suchergebnis mitkam, falls es einen gab.
- arXiv. Eine DOI der Form
10.48550/arXiv.<id>ist ein arXiv-Paper, undhttps://arxiv.org/pdf/<id>ist immer kostenlos. - Offene Kopien der DOI von Unpaywall oder OpenAlex. Unpaywall (
https://api.unpaywall.org/v2/<doi>?email=<your email>) liefertbest_oa_location.url_for_pdfund eine Listeoa_locationsund lehnt Anfragen ohne echte E-Mail-Adresse ab. Beide Dienste stammen von derselben Non-Profit-Organisation, OurResearch, deshalb fragt die App Unpaywall, wennUNPAYWALL_EMAILgesetzt ist, und liest sonst die Fundorte von OpenAlex, nicht beides. - CORE. Die v3-API (
/v3/search/works?q=doi:"<doi>", mit Bearer-Key) bündelt Kopien aus Repositorien und liefert den extrahierten Text oft direkt infullText. Die App übernimmt diesen Text, wenn er länger als 500 Zeichen ist, und probiert sonst diedownloadUrl. - PubMed Central. Mit einer PMCID liefert Europe PMC den ganzen Artikel als JATS-XML unter
https://www.ebi.ac.uk/europepmc/webservices/rest/<PMCID>/fullTextXML. Das ist kein PDF, enthält aber Abschnitte, Abbildungen und Literaturverzeichnis in einer Struktur, die sich sauber in HTML umwandeln lässt.
Abstracts kommen als invertierter Index
Noch ein OpenAlex-Detail, über das viele stolpern: Es gibt kein Feld abstract. Du bekommst abstract_inverted_index, eine Zuordnung von jedem Wort zu den Positionen, an denen es vorkommt, und baust den Text selbst wieder zusammen:
words = {pos: w for w, positions in idx.items() for pos in positions}
abstract = " ".join(words[i] for i in sorted(words))
Das reicht für Suchergebnisse und die erste Sichtung. Beim Brain-Drain-Paper, dessen einzige offene Kopie eine Repositoriumsseite ist, kommt der kostenlose Weg mit Abstract plus dem Volltext der Metaanalyse ungefähr so weit, wie er kommt – und das ist meist der Punkt, an dem man prüft, ob die eigene Bibliothek Zugang hat.

Top comments (0)