आपके पास DOI है, पब्लिशर का पेज लॉगिन माँग रहा है, और आपको बस मेथड्स वाला सेक्शन पढ़ना है। अक्सर कहीं न कहीं उसकी एक कानूनी और मुफ़्त कॉपी मौजूद होती है: लेखक की यूनिवर्सिटी की रिपॉज़िटरी, कोई प्रीप्रिंट, या PubMed Central। हाथ से ढूँढने में चार-पाँच साइटें आज़मानी पड़ती हैं। कोड में यही काम कुछ पब्लिक API को सही क्रम में कॉल करके हो जाता है।
नीचे का क्रम osFoundry डेस्कटॉप ऐप के Paper वर्कस्पेस के View बटन का है। उसका बैकएंड हर स्रोत को बारी-बारी आज़माता है, जब तक कोई असली PDF न लौटा दे (पेपर लेखन और रिसर्च वर्कस्पेस वाले पेज पर इसके आसपास की स्क्रीन दिखती है)। उदाहरणों में एक ही सवाल पर दो असली DOI लिए गए हैं: क्या मेज़ पर रखा फ़ोन एकाग्रता को नुकसान पहुँचाता है।
-
10.1037/tmb0000123, 2024 का एक मेटा-विश्लेषण (Hartanto et al.) -
10.1086/691462, 2017 का बहुचर्चित "Brain Drain" अध्ययन (Ward et al.)
OpenAlex से सिर्फ़ ओपन-एक्सेस फ़्लैग नहीं, सारी लोकेशन माँगें
OpenAlex बताता है कि पेपर कहाँ-कहाँ रखा है, और हर जगह के लिए यह भी कि सीधा PDF है या नहीं:
curl -s "https://api.openalex.org/works/doi:10.1037/tmb0000123?select=open_access,best_oa_location,locations" मेटा-विश्लेषण के लिए यह तीन लोकेशन लौटाता है: पब्लिशर का PDF लिंक, James Cook University की रिपॉज़िटरी में एक PDF, और Singapore Management University की लाइब्रेरी पर एक लैंडिंग पेज।
Brain Drain पेपर के लिए वही रिक्वेस्ट "is_oa": true और "oa_status": "green" दिखाती है, फिर भी हर pdf_url null है। एकमात्र खुली कॉपी एक रिपॉज़िटरी का लैंडिंग पेज है। यानी is_oa सिर्फ़ इतना बताता है कि कहीं मुफ़्त वर्शन है, यह नहीं कि आप उसे डाउनलोड कर सकते हैं। हर लोकेशन का pdf_url पढ़ें।
API key के बिना OpenAlex की रिक्वेस्टें आपके IP एड्रेस वाले सभी लोगों के साथ एक छोटा मुफ़्त दैनिक कोटा साझा करती हैं, और रिस्पॉन्स का meta.notice यह बताता है। मुफ़्त key (क्वेरी में api_key=) से आपका अपना कोटा मिलता है, और ऐप OPENALEX_API_KEY सेट होने पर उसी से key पढ़ लेता है।
शुरुआती पाँच बाइट जाँचें
pdf_url भी अभी सिर्फ़ एक वादा है। पब्लिशर कुकी वॉल, बॉट चेक और लॉगिन पेज को 200 स्टेटस और HTML बॉडी के साथ लौटा देते हैं। ऐप का Rust कोड डाउनलोड तभी मानता है जब बॉडी PDF के मैजिक नंबर से शुरू होती है:
if !fetched.bytes.starts_with(b"%PDF-") { self.tried.push(format!("{source}: link was a web page, not a PDF")); return None; } यही विचार Python में, सिर्फ़ स्टैंडर्ड लाइब्रेरी के साथ:
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"])) मेटा-विश्लेषण पर पब्लिशर के लिंक ने स्क्रिप्ट को मना कर दिया और रिपॉज़िटरी वाली कॉपी 1.6 MB के PDF के रूप में आ गई। Brain Drain पर यह तुरंत "no pdf_url at all" के साथ फ़ेल हो जाता है। क्या-क्या आज़माया और क्यों फ़ेल हुआ, इसकी सूची रखना कुछ अतिरिक्त लाइनों के लायक है; सिर्फ़ "फ़ुल टेक्स्ट नहीं मिला" आपको वापस हाथ से ढूँढने पर भेज देता है।
हर लुकअप में चार लिंक की सीमा और URL की डुप्लिकेट छँटाई भी ऐप से ही है। कुछ पेपरों की एक दर्जन लोकेशन होती हैं जो सब एक ही फ़ाइल की ओर इशारा करती हैं।
बाक़ी की श्रृंखला
OpenAlex लुकअप सिर्फ़ एक कदम है। ऐप का पूरा क्रम यह है:
- सर्च रिज़ल्ट के साथ आया PDF लिंक, अगर कोई था।
- arXiv।
10.48550/arXiv.<id>जैसा DOI arXiv का पेपर है, औरhttps://arxiv.org/pdf/<id>हमेशा मुफ़्त है। - DOI की खुली कॉपियाँ, Unpaywall या OpenAlex से। Unpaywall (
https://api.unpaywall.org/v2/<doi>?email=<your email>)best_oa_location.url_for_pdfऔरoa_locationsकी सूची लौटाता है, और असली ईमेल पते के बिना रिक्वेस्ट ठुकरा देता है। दोनों सेवाएँ एक ही नॉन-प्रॉफ़िट OurResearch की हैं, इसलिएUNPAYWALL_EMAILसेट होने पर ऐप Unpaywall से पूछता है, वरना OpenAlex की लोकेशन पढ़ता है, दोनों नहीं। - CORE। इसका v3 API (
/v3/search/works?q=doi:"<doi>", bearer key के साथ) रिपॉज़िटरी की कॉपियाँ जमा करता है और अक्सर निकाला हुआ टेक्स्ट सीधेfullTextमें लौटा देता है। ऐप वह टेक्स्ट तब लेता है जब वह 500 अक्षरों से लंबा हो, वरनाdownloadUrlआज़माता है। - PubMed Central। PMCID होने पर Europe PMC पूरा लेख JATS XML के रूप में
https://www.ebi.ac.uk/europepmc/webservices/rest/<PMCID>/fullTextXMLपर देता है। यह PDF नहीं है, लेकिन इसमें सेक्शन, फ़िगर और रेफ़रेंस ऐसी बनावट में होते हैं जो साफ़-सुथरे HTML में बदल जाती है।
एब्स्ट्रैक्ट इनवर्टेड इंडेक्स के रूप में आता है
OpenAlex की एक और बात, जिस पर लोग अटकते हैं: abstract नाम का कोई फ़ील्ड नहीं है। आपको abstract_inverted_index मिलता है, जो हर शब्द को उन पोज़िशनों से जोड़ने वाला मैप है जहाँ वह आता है, और टेक्स्ट आपको ख़ुद दोबारा जोड़ना पड़ता है:
words = {pos: w for w, positions in idx.items() for pos in positions} abstract = " ".join(words[i] for i in sorted(words)) सर्च रिज़ल्ट और शुरुआती छँटाई के लिए यह काफ़ी है। Brain Drain पेपर के लिए, जिसकी एकमात्र खुली कॉपी रिपॉज़िटरी का एक पेज है, एब्स्ट्रैक्ट और मेटा-विश्लेषण का पूरा टेक्स्ट ही मुफ़्त रास्ते की लगभग आख़िरी सीमा है, और आम तौर पर वही मौक़ा है जब देखना चाहिए कि आपकी लाइब्रेरी के पास एक्सेस है या नहीं।

