कैसे एक आत्म-उपचारक वेब स्क्रैपर बनाएं
Advanced Data Extraction Specialist
TL;DR:
- एक CSS चयनकर्ता जो आज काम कर रहा है, एक साइट के एक वर्ग का नाम बदलने पर सुबह टूट जाता है, और एक सामान्य स्क्रेपर चुपचाप कुछ भी वापस लौटाकर प्रतिक्रिया करता है।
- एक आत्म-स्वास्थ्य स्क्रेपर रनटाइम पर चयनकर्ता को मरम्मत करता है: यह पृष्ठ लाता है, सहेजे गए चयनकर्ता को आजमाता है, और चूक पर एक भाषा मॉडल से लाइव DOM से पढ़ने के लिए एक नया चयनकर्ता मांगता है।
- यह पाइपलाइन स्क्रैपलेस यूनिवर्सल स्क्रैपिंग API के माध्यम से प्रस्तुत पृष्ठ लाती है, ताकि मॉडल हमेशा वास्तविक, स्क्रिप्ट-निष्पादित मार्कअप के खिलाफ हील कर सके, न कि एक खाली खोल के खिलाफ।
- एक लाइव रन दिखाता है कि सहेजा गया चयनकर्ता
.author-nameशून्य तत्वों से मेल खा रहा है, फिरdeepseek-v4-flashsmall.authorका प्रस्ताव करता है, जो सभी दस लेखकों को निकालता है। - ठीक किया गया चयनकर्ता DOM के खिलाफ मान्य किया जाता है इससे पहले कि इसे विश्वास किया जाए — एक गलत अनुमान कुछ भी मेल नहीं खाता और उसे खारिज कर दिया जाता है, इसलिए एक बुरा सुझाव आपके डेटा को चुपचाप भ्रष्ट नहीं करता।
- एक मुफ्त योजना पर स्क्रैपलेस API कुंजी प्राप्त करें और पूरे लूप को अंत से अंत तक चलाएं।
पाइपलाइन पर एक नज़र
एक स्क्रेपर इसलिए नहीं टूटता क्योंकि कोड गलत है बल्कि इसलिए क्योंकि पृष्ठ स्थानांतरित हो गया। समाधान यह है कि चयनकर्ता को डेटा के रूप में देखा जाए जिसे पुनः उत्पन्न किया जा सकता है, न कि स्रोत में पका हुआ एक स्थिरांक। यह पाइपलाइन पाँच चरणों में ऐसा करती है:
प्रस्तुत पृष्ठ लाएं (Scrapeless) → सहेजे गए चयनकर्ता का प्रयास करें → चूक का पता लगाएं → एक भाषा मॉडल के साथ ठीक करें → मान्य करें और निकालें → कार्यशील चयनकर्ता को सहेजें
नीचे प्रत्येक चरण लाइव पृष्ठ के खिलाफ चलता है। लाना और ठीक करना वे दो कॉल हैं जो आपके मशीन से बाहर जाती हैं; बाकी सब स्थानीय पार्सिंग है जिसे आप निरीक्षण कर सकते हैं।
चरण 1: स्क्रैपलेस के साथ प्रस्तुत पृष्ठ लाएं
ठीक करना उतना ही अच्छा है जितना कि HTML जिसे आप मॉडल को दिखाते हैं, इसलिए लाना उस पृष्ठ को वापस करना चाहिए जो एक ब्राउज़र बनाएगा, न कि खाली कंटेनर जो एक क्लाइंट-रेन्डर्ड साइट पहले भेजती है। वह प्रस्तुत लाना Scrapeless Universal Scraping API का काम है। यूनिवर्सल स्क्रैपिंग API पृष्ठ को सर्वर-साइड js_render के साथ प्रस्तुत करती है और समाप्त HTML को अपने data फ़ील्ड में लौटाती है। पाइपलाइन द्वारा उपयोग की जाने वाली दो पुस्तकों को स्थापित करें:
bash
pip install requests beautifulsoup4
दोनों कुंजियों को पर्यावरण में रखें - लाने के लिए स्क्रैपलेस कुंजी, ठीक करने के लिए मॉडल कुंजी:
bash
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
export OPENROUTER_API_KEY="your_openrouter_api_key"
चरण 2: सहेजे गए चयनकर्ता का प्रयास करें और टूटने का पता लगाएं
पिछले महीने का स्क्रेपर आज के पृष्ठ के खिलाफ चलाएँ और विफलता शांत है: चयनकर्ता कुछ भी मेल नहीं खा रहा है और निकासी एक खाली सूची लौटा रही है, इसलिए एक naive नौकरी शून्य पंक्तियाँ लिखती है और सफलता की रिपोर्ट करती है। नीचे का डेमो पृष्ठ अपने उद्धरणों को JavaScript के साथ बनाता है; सहेजा गया चयनकर्ता .author-name एक वर्ग है जिसका मार्कअप अब उपयोग नहीं करता है, जो किसी भी चयनकर्ता के लिए खड़ा है जिसका नाम पुन: डिजाइन ने बदल दिया है:
python
import os, requests
from bs4 import BeautifulSoup
def fetch(url):
r = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
json={"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": True}},
timeout=120,
)
r.raise_for_status()
return r.json()["data"]
soup = BeautifulSoup(fetch("https://quotes.toscrape.com/js/"), "html.parser")
SAVED_SELECTOR = ".author-name" # पिछले महीने काम कर रहा था; साइट ने वर्ग का नाम बदल दिया
authors = [e.get_text(strip=True) for e in soup.select(SAVED_SELECTOR)]
print(f"saved selector {SAVED_SELECTOR!r} matched: {len(authors)}",
"-> BROKEN, extracted nothing" if not authors else "-> ok")
यह रन चुपचाप विफलता को जोर से बनाता है:
text
saved selector '.author-name' matched: 0 -> BROKEN, extracted nothing
चरण 3: एक भाषा मॉडल के साथ ठीक करें
जब सहेजा गया चयनकर्ता कुछ भी मेल नहीं खाता है, तो मॉडल को लाइव DOM सौंपें और प्रतिस्थापन के लिए पूछें। मॉडल वास्तविक संरचना को पढ़ता है - टैग, वर्ग और घोंसले जो CSS चयनकर्ताओं विशेषण द्वारा परिभाषित होते हैं - और उन चयनकर्ताओं को लौटाता है जो उसके सामने के पृष्ठ पर आकार में होते हैं, न कि किसी पृष्ठ पर जिसे उसने प्रशिक्षण में देखा हो। उत्तर को एक JSON वस्तु में सीमित करना उत्तर को एक एकल चयनकर्ता स्ट्रिंग के रूप में बनाए रखता है न कि किसी स्पष्टीकरण के रूप में:
python
import os, json, requests
def propose_selector(html_fragment, target):
prompt = (f'एक वेब-स्क्रैपिंग CSS चयनकर्ता टूट गया है। इस HTML फ्रैगमेंट से, JSON लौटाएँ '
f'{{"selector": "<css>"}} उस तत्व के लिए जो {target} रखता है। केवल JSON लौटाएँ।\n\n'
f"HTML:\n{html_fragment}")
r = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}", "Content-Type": "application/json"},
json={"model": "deepseek/deepseek-v4-flash",
"messages": [{"role": "user", "content": prompt}],
"response_format": {"type": "json_object"}, "temperature": 0},
timeout=120,
)
r.raise_for_status()
return json.loads(r.json()["choices"][0]["message"]["content"])["selector"]
एक रेंडर की गई .quote ब्लॉक भेजना पर्याप्त संदर्भ है और टोकन की गणना — और लागत — को छोटा रखता है।
निःशुल्क योजना पर अपना API कुंजी प्राप्त करें: app.scrapeless.com
चरण 4: मान्य करें और निकालें
एक प्रस्तावित चयनकर्ता तब तक एक सुझाव है जब तक DOM इसे पुष्टि नहीं करता। ठीक किया गया चयनकर्ता उसी सूप के खिलाफ चलाएं और मिलानों की गिनती करें: एक असली सुधार पंक्तियाँ लौटाता है, और एक खराब अनुमान शून्य को लौटाता है और इसे खाली डेटा लिखने से पहले हटा दिया जाता है। पूरी लूप तीन कॉल को एक साथ बांधती है:
python
import os, json, requests
from bs4 import BeautifulSoup
def fetch(url):
r = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
json={"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": True}},
timeout=120,
)
r.raise_for_status()
return r.json()["data"]
def propose_selector(html_fragment, target):
prompt = (f'एक वेब-सक्रैपिंग CSS चयनकर्ता टूट गया है। इस HTML अंश से, JSON लौटाएं '
f'{{"selector": "<css>"}} उस तत्व के लिए जो {target} रखता है। केवल JSON लौटाएं।\n\n'
f"HTML:\n{html_fragment}")
r = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}", "Content-Type": "application/json"},
json={"model": "deepseek/deepseek-v4-flash",
"messages": [{"role": "user", "content": prompt}],
"response_format": {"type": "json_object"}, "temperature": 0},
timeout=120,
)
r.raise_for_status()
return json.loads(r.json()["choices"][0]["message"]["content"])["selector"]
def extract(soup, selector):
return [e.get_text(strip=True) for e in soup.select(selector)]
soup = BeautifulSoup(fetch("https://quotes.toscrape.com/js/"), "html.parser")
authors = extract(soup, ".author-name")
if not authors:
print("सहेजा गया चयनकर्ता '.author-name' से मेल खाता है: 0 -> ठीक करना")
sample = str(soup.select_one(".quote") or soup.body)[:1500]
healed = propose_selector(sample, "quote author name")
print("मॉडल द्वारा प्रस्तावित चयनकर्ता:", repr(healed))
authors = extract(soup, healed)
print("ठीक किया हुआ चयनकर्ता मेल खाता है:", len(authors))
print("लेखक:", ", ".join(authors[:3]), "...")
टूटे हुए चयनकर्ता का इलाज होता है और निष्कर्षण फिर से भरता है:
text
सहेजा गया चयनकर्ता '.author-name' से मेल खाता है: 0 -> ठीक करना
मॉडल द्वारा प्रस्तावित चयनकर्ता: 'small.author'
ठीक किया हुआ चयनकर्ता मेल खाता है: 10
लेखक: अल्बर्ट आइंस्टीन, जे.के. राउलिंग, जेन ऑस्टेन ...
क्योंकि ठीक किया हुआ चयनकर्ता आपके पार्सर द्वारा पहले से उपयोग किए जाने वाले CSS चयनकर्ता इंजन के खिलाफ जांचा जाता है, मान्यता सटीक है: या तो यह तत्वों से मेल खाता है या यह नहीं, और केवल एक मेल को स्वीकार किया जाता है।
चरण 5: कार्यशील चयनकर्ता को स्थायी बनाना
एक बार ठीक करना मरम्मत है; हर बार ठीक करना बर्बादी है। जब एक चयनकर्ता मान्यता प्राप्त करता है, तो इसे क्षेत्र द्वारा कुंजी वाले एक छोटे कैश में वापस लिखें, ताकि अगली बार कार्यशील चयनकर्ता सीधे डिस्क से पढ़ा जा सके और केवल उस समय मॉडल को कॉल किया जाए जब वह भी टूट जाए:
python
import json
from pathlib import Path
CACHE = Path("selectors.json")
def remember(field, selector):
cache = json.loads(CACHE.read_text()) if CACHE.exists() else {}
cache[field] = selector
CACHE.write_text(json.dumps(cache, indent=2))
remember("quote_author", "small.author") # अगली बार ठीक किए गए चयनकर्ता से शुरू होता है
अब स्क्रैपर शालीनता से गिरता है: यह काम करने के दौरान कैश किए गए चयनकर्ताओं पर निर्भर रहता है और एक पृष्ठ बदलते ही उन्हें ठीक करता है, बजाय इसके कि कोई इसे नोटिस करे और कोड को संपादित करे।
निष्कर्ष
एक स्व-चुनौती समाधान स्क्रैपर एक आउटेज से टूटे हुए चयनकर्ता को एक रनटाइम इवेंट में बदल देता है। स्क्रैपलेस यूनिवर्सल स्क्रैपिंग एपीआई वह प्रदर्शित HTML प्रदान करता है जिसकी मॉडल को तर्क करने की आवश्यकता होती है, मॉडल उस लाइव DOM को पढ़ता है और एक चयनकर्ता प्रस्तावित करता है, और विश्लेषक के खिलाफ एक मिलान गणना यह तय करती है कि सुधार वास्तविक है या नहीं इससे पहले कि कोई डेटा लिखा जाए। ऊपर का रन - .author-name शून्य से मिलाना, small.author दस से मिलाना - छोटे रूप में लूप है: लाना, पता लगाना, ठीक करना, मान्य करना, स्थायी करना। आपकी अपेक्षित अनुरोध मात्रा को मूल्य निर्धारण पृष्ठ के खिलाफ आकार दें, और पढ़ें कैसे एक ब्राउज़र HTML को DOM में पार्स करता है अगर आप समझना चाहते हैं कि प्रदर्शित फ़ेच क्यों महत्वपूर्ण है। निष्कर्षण में भाषा मॉडलों के व्यापक परिप्रेक्ष्य के लिए, LLM स्क्रैपर क्या है पर व्याख्याकार संदर्भ प्रदान करता है।
हमारी समुदाय में शामिल हों एक मुफ्त योजना का दावा करने के लिए और अन्य डेवलपर्स के साथ नोट्स की तुलना करें जो लचीले स्क्रैपर्स बना रहे हैं: डिस्कॉर्ड · टेलीग्राम।
सामान्य प्रश्न
प्र: जब एक स्क्रैपर "टूटता" है, तो वास्तव में क्या टूटता है?
आमतौर पर चयनकर्ता, न कि लॉजिक। एक साइट एक नया डिज़ाइन प्रस्तुत करती है जो एक वर्ग का नाम बदलती है या अपने मार्कअप को पुनर्गठित करती है, तो सहेजा गया CSS चयनकर्ता मिलान करना बंद कर देता है, और निष्कर्ष एक खाली सूची लौटाता है। अनुरोध अभी भी सफल होता है, यही कारण है कि विफलता को पहचानना आसान होता है।
प्र: साधारण अनुरोध के बजाय स्क्रैपलेस यूनिवर्सल स्क्रैपिंग एपीआई के माध्यम से क्यूं fetch करें?
क्योंकि मॉडल केवल उसी HTML के खिलाफ ठीक कर सकता है जिसे उसे दिखाया गया है। एक ग्राहक-निर्मित पृष्ठ एक नग्न HTTP क्लाइंट को खाली कंटेनर लौटाता है, इसलिए मॉडल मार्कअप के बारे में तर्क करेगा जिसमें कोई डेटा नहीं है। यूनिवर्सल स्क्रैपिंग API पृष्ठ को सर्वर-साइड प्रदर्शित करता है और तैयार DOM लौटाता है, जिससे मॉडल के लिए काम करने की वास्तविक संरचना मिलती है।
प्र: आप मॉडल को कैसे रोकते हैं कि वह ऐसा चयनकर्ता बनाएं जो कुछ नहीं मिलाता?
आप भरोसा करने से पहले मान्य करते हैं। ठीक किया गया चयनकर्ता पार्स किए गए DOM के खिलाफ चलाया जाता है और मिलान की संख्या गिनी जाती है; एक चयनकर्ता जो शून्य तत्व लौटाता है उसे इस्तेमाल करने के बजाय त्याग दिया जाता है। मॉडल प्रस्तावित करता है, लेकिन मिलान की संख्या निर्णय लेती है।
प्र: क्या यह हर रन में मॉडल को कॉल करता है?
नहीं। एक बार एक चयनकर्ता मान्य हो जाने के बाद, इसे फ़ील्ड द्वारा कुंजी के साथ डिस्क में कैश कर दिया जाता है, और बाद के रन सीधे कार्यशील चयनकर्ता को पढ़ते हैं। मॉडल केवल तब कॉल किया जाता है जब एक कैश किया हुआ चयनकर्ता मिलान करना बंद कर देता है, जिससे लेटेंसी और लागत वास्तविक टूटने से जुड़ी रहती है न कि प्रत्येक स्क्रैप से।
प्र: क्या इस तरीके से स्क्रैप करना कानूनी है?
ठीक होने वाला लूप यह नहीं बदलता कि आप क्या इकट्ठा करने की अनुमति रखते हैं। सार्वजनिक पृष्ठों को स्क्रैप करें, साइट की शर्तों और उसके रोबोट निर्देशों का सम्मान करें, व्यक्तिगत डेटा से बचें जिसका आपके पास संसोधन करने का आधार नहीं है, और अनुरोध दरों को मध्यम रखें।
स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।



