कैसे Scrapeless के साथ एक संवर्धित वेब स्क्रैपिंग पाइपलाइन बनाएं
Senior Web Scraping Engineer
TL;DR:
- एक शर्तीय अनुरोध एक अपरिवर्तित पृष्ठ को एक
304में बदलता है जिसमें 0-बाइट का शरीर होता है — पूरी प्रतिक्रिया 50,388 बाइट्स की थी, और दस शर्तीय जांचें 4.21 s में चलीं जबकि दस पूर्ण फ़ेच 5.52 s में हुईं। - सामग्री हैशिंग परिवर्तन का पता लगाती है लेकिन कोई बैंडविड्थ बचाती नहीं है: एक वैधता-रहित पृष्ठ का दूसरा फ़ेच अभी भी 11,064 बाइट्स स्थानांतरित करता है इससे पहले कि हैश की तुलना की जा सके।
- प्रति-रिकॉर्ड अंगूठियों के निशान हैं जो लिखाई को छोटा करते हैं। एक स्थानांतरित मूल्य ने 20-रिकॉर्ड पृष्ठ को नीचे 1 पंक्ति में लिखा।
- उन क्षेत्रों की अंगूठी बनाएं जो आप महत्वपूर्ण मानते हैं, न कि पूरे रिकॉर्ड और एक अप्रासंगिक क्षेत्र का परिवर्तन सब कुछ गंदा चिह्नित करता है।
- HTTP वैधता यह वर्णन करते हैं कि सर्वर ने कौन-सी दस्तावेज़ भेजा, इसलिए वे उपयोगी होना बंद कर देते हैं जब रिकॉर्ड क्लाइंट-साइड रेंडरिंग के माध्यम से आते हैं।
- एक
304केवल तभी उपलब्ध है जब पिछले रन ने वैधता को संग्रहीत किया हो, जो राज्य तालिका को पाइपलाइन का हिस्सा बनाता है न कि एक ऑप्टिमाइजेशन। - रेंडर किए गए पृष्ठों पर एक अनुक्रमिक क्रॉल डालें Scrapeless मुफ्त योजना के साथ।
अधिकांश अनुसूचित स्क्रैपर्स सब कुछ पुनः डाउनलोड करते हैं, सब कुछ पुनः पार्स करते हैं, और हर पंक्ति को फिर से लिखते हैं, फिर सफलता की रिपोर्ट करते हैं। यह तब तक काम करता है जब तक कैटलॉग बढ़ता नहीं है, जिस बिंदु पर दैनिक कार्य लगभग सारा समय यह पुष्टि करने में बिता रहा है कि कल के डेटा में अब भी कल का डेटा है।
अनुक्रमिक स्क्रैपिंग इसके विपरीत व्यवस्था है: पूछें कि क्या बदला है, और केवल वहीं काम करें जहां उत्तर हाँ है। यह प्रश्न तीन स्तरों पर पूछा जा सकता है, और प्रत्येक एक अलग चीज़ बचाता है — बैंडविड्थ, पार्सिंग, या लिखता है। प्रत्येक नीचे परत को उसी लाइव कैटलॉग पृष्ठ के खिलाफ मापा गया था, इसलिए व्यापार-बार की लागत संख्याओं के साथ आती है न कि विशेषणों के साथ।
Pipeline at a Glance
| Layer | Question | Mechanism | Measured result |
|---|---|---|---|
| HTTP | क्या दस्तावेज़ में परिवर्तन हुआ? | If-None-Match / If-Modified-Since |
304, 0 बाइट्स बनाम 50,388 |
| दस्तावेज़ | क्या बाइट्स में परिवर्तन हुआ? | शरीर का SHA-256 | पता चला, लेकिन 11,064 बाइट्स अभी भी स्थानांतरित हुए |
| रिकॉर्ड | कौन-सी पंक्तियाँ बदलीं? | प्रति-रिकॉर्ड क्षेत्र अंगूठी | 20 पंक्तियों में से 1 लिखी गई |
प्रवाह है जांच करें → केवल तब फ़ेच करें जब परिवर्तन हुआ हो → निकालें → अंगूठियों की तुलना करें → केवल विभिन्नताओं को लिखें। परतें ढेर होती जा रही हैं: HTTP जांच सबसे सस्ती और कम उपलब्ध होती है, रिकॉर्ड जांच हमेशा उपलब्ध होती है और एक पूर्ण फ़ेच की लागत आती है।
कम बार जांचना उसी समस्या का दूसरा भाग है। रोबोट्स बहिष्करण प्रोटोकॉल वह स्थान है जहां एक साइट कहती है कि वह क्या चाहती है कि क्रॉल किया जाए, और एक अनुक्रमिक डिज़ाइन वह है जो एक स्क्रैपर को धीमी ताल को सम्मानित करने की अनुमति देता है बिना पीछे पड़े — प्रत्येक जांच एक राउंड ट्रिप की कीमत चुकाती है न कि पूर्ण स्थानांतरण की।
Layer 1: Let the Server Answer
एक HTTP वैधता सर्वर की अपनी राय है कि क्या उसकी प्रस्तुति में परिवर्तन हुआ। दो मौजूद हैं: ETag, एक अपारदर्शी संस्करण टोकन, और Last-Modified, एक टाइमस्टैम्प। पहली प्रतिक्रिया उन्हें ले जाती है।
python
import requests
URL = "https://books.toscrape.com/catalogue/category/books/mystery_3/index.html"
first = requests.get(URL, timeout=30)
first.raise_for_status()
etag = first.headers["ETag"]
print(f"first GET {first.status_code} {len(first.content)} bytes ETag={etag}")
second = requests.get(URL, headers={"If-None-Match": etag}, timeout=30)
print(f"second GET {second.status_code} {len(second.content)} bytes")
print(f"bytes avoided: {len(first.content) - len(second.content)}")
text
first GET 200 50388 bytes ETag=W/"63e40de8-c4d4"
second GET 304 0 bytes
bytes avoided: 50388
304 में बिल्कुल भी कोई शरीर नहीं होता। शर्तीय-अनुरोध तंत्र को इस तरह परिभाषित किया गया है ताकि क्लाइंट उसकी पहले से मौजूद प्रति बनाए रखे, जिसका अर्थ है कि स्क्रैपर को एक ग्रामित रखना होगा।
Last-Modified एक अलग हेडर के माध्यम से उसी तरह कार्य करता है:
python
third = requests.get(URL, headers={"If-Modified-Since": first.headers["Last-Modified"]}, timeout=30)
print(first.headers["Last-Modified"], "->", third.status_code, len(third.content), "bytes")
text
Wed, 08 Feb 2023 21:02:32 GMT -> 304 0 bytes
जब दोनों उपस्थित हों तो ETag को प्राथमिकता दें। HTTP अर्थशास्त्र विनिर्देशन Last-Modified को एक सेकंड-समाधान टाइमस्टैम्प बनाता है, इसलिए एक ही सेकंड के भीतर दो परिवर्तन पहचानने योग्य नहीं होते हैं, जबकि एक एंटिटी टैग किसी भी संपादन पर परिवर्तन करने के लिए स्वतंत्र है।
बचत वास्तविक है लेकिन यह पूरा रन नहीं है:
text
10 conditional GETs 4.21s
10 full GETs 5.52s
शर्तीय जांच 1.31 गुना तेजी से चली और 492 KB अप्राप्त छोड़ी। अनुरोध अभी भी एक राउंड ट्रिप की कीमत चुकाता है — जो गायब होता है वह शरीर है, न कि कनेक्शन।
Layer 2: Hash the Document When the Server Says Nothing
कई लक्ष्य वैधता नहीं भेजते। तब यह जानने का एकमात्र तरीका है कि क्या दस्तावेज़ बदला है वह इसे फ़ेच करना और देखना है। एक क्रिप्टोग्राफिक डाइजेस्ट वह मानक उपकरण है जो उस तुलना के लिए है — SHA-256 मानक एक निश्चित चौड़ाई का मान प्रदान करता है जहां किसी भी एक-बाइट के अंतर से एक अप्रासंगिक डाइजेस्ट उत्पन्न होता है, इसलिए समानता एक विश्वसनीय "कुछ नहीं हिला" संकेत है।
python
import hashlib
import requests
q1 = requests.get("https://quotes.toscrape.com/", timeout=30)
q1.raise_for_status()
print("ETag:", q1.headers.get("ETag"), " Last-Modified:", q1.headers.get("Last-Modified"))
q2 = requests.get("https://quotes.toscrape.com/", timeout=30)
h1 = hashlib.sha256(q1.content).hexdigest()
h2 = hashlib.sha256(q2.content).hexdigest()
print(f"sha256 run 1: {h1[:16]}...")
print(f"sha256 run 2: {h2[:16]}...")
print(f"unchanged: {h1 == h2} bytes still transferred: {len(q2.content)}")
text
ETag: None Last-Modified: None
sha256 run 1: efdc2605a2062dce...
sha256 run 2: efdc2605a2062dce...
unchanged: True bytes still transferred: 11064
नोट करें कि यह परत क्या खरीदती है और क्या नहीं खरीदती है। हैश सही ढंग से परिवर्तन नहीं दर्शाता है, और 11,064 बाइट्स फिर भी नेटवर्क के पार गए। दस्तावेज़ हैशिंग पार्सिंग, डेटाबेस लेखन, डाउनस्ट्रीम चेतावनी और किसी भी पुनः-आधारित को बचाता है — कभी भी बैंडविड्थ नहीं।
यहाँ एक झूठी-पॉज़िटिव समस्या भी है कि यहाँ की संख्या नहीं दिखती। ऐसे पृष्ठ जिनमें एक सत्र टोकन, एक घुमने वाला विज्ञापन या एक रेंडर किया गया टाइमस्टैम्प है, हर बार फ़ेच करते समय एक अलग हैश उत्पन्न करते हैं, जबकि डेटा एक समान होता है। निकाले गए रिकॉर्ड को कच्चे बॉडी के बजाय हैशिंग करना उस सिग्नल को स्थिर बनाता है, जो अगली परत है।
परत 3: रिकॉर्ड की पहचान करना
ऊपर की परतें दस्तावेज़ के बारे में प्रश्नों का उत्तर देती हैं। एक पाइपलाइन को आमतौर पर यह जानने की आवश्यकता होती है कि कौन से पंक्तियों को लिखना है।
python
import json, hashlib
def fingerprint(record):
payload = json.dumps({k: record[k] for k in ("price", "rating")}, sort_keys=True)
return hashlib.sha256(payload.encode()).hexdigest()[:16]
संपूर्ण रिकॉर्ड के बजाय चुने हुए फ़ील्ड के उपसमुच्चय की पहचान करना वही निर्णय है जो इसे काम करता है। एक ऐसा फ़ील्ड शामिल करें जो अपने आप बदलता है — एक दृश्य गणना, एक "अंतिम देखे गए" टाइमस्टैम्प, एक रैंक की गई सूची में एक स्थिति — और हर रिकॉर्ड हर बार गंदा दिखता है।
प्रत्येक रिकॉर्ड के लिए एक फ़िंगरप्रिंट स्टोर करें, फिर अगले स्क्रेप की तुलना उसके खिलाफ करें:
python
known = {title: (fp, price) for title, fp, price in conn.execute("SELECT title, fp, price FROM snap")}
new, changed, same = [], [], 0
for record in second_pass:
previous = known.get(record["title"])
if previous is None:
new.append(record)
elif previous[0] != fingerprint(record):
changed.append((record["title"], previous[1], record["price"]))
else:
same += 1
जीवित 20-रिकॉर्ड पृष्ठ के खिलाफ जिसमें एक मूल्य वास्तविक मूव के लिए प्रतिस्थापित किया गया है:
text
parsed 20 records from the live page
stored baseline: 20 fingerprints
example fingerprint: 'Sharp Objects' -> e974692e9d935048
unchanged 19 | changed 1 | new 0
CHANGED A Murder in Time £16.64 -> £99.99
rows written downstream: 1 of 20
बीस के बजाय एक पंक्ति लिखी गई। एक कैटलॉग पर जहाँ दैनिक वास्तविकता यह है कि लगभग कुछ भी नहीं चल रहा है, वह अनुपात फ़िंगरप्रिंटिंग के पूरे तर्क का है: लिखने की मात्रा परिवर्तन दर को ट्रैक करती है न कि कैटलॉग के आकार को।
पिछले मान को फ़िंगरप्रिंट के साथ रखना वह है जो पहचान को एक उपयोग करने योग्य घटना में बदलता है। £16.64 -> £99.99 एक मूल्य-परिवर्तन रिकॉर्ड है; एक गंदा ध्वज केवल एक संकेत है कि कुछ हुआ है।
क्लाइंट-साइड पर रेंडर करने वाले पृष्ठों के खिलाफ एक वृद्धिशील क्रॉल चला रहे हैं? Scrapeless मुफ्त योजना पर्याप्त अनुरोधों को कवर करता है ताकि आधारभूत रेखा और पहले कुछ अंतर बनाए जा सकें।
जहाँ HTTP परत लागू होना बंद करती है
एक वेलिडेटर उस दस्तावेज़ का वर्णन करता है जो सर्वर ने भेजा। जब रिकॉर्ड क्लाइंट-साइड रेंडरिंग के माध्यम से आते हैं, तो वह दस्तावेज़ एप्लिकेशन शेल है, और इसका ETag शेल के परिनियोजन को ट्रैक करता है न कि कैटलॉग को।
यह परिणाम विशेष है: एक पृष्ठ 304 लौटा सकता है जबकि इसके पीछे के मूल्य सभी बदल गए हैं, क्योंकि शेल वास्तव में नहीं बदला। किसी भी लक्षित सामग्री जिसे ब्राउज़र में इकट्ठा किया गया है, उसे रिकॉर्ड परत पर जांचा जाना चाहिए, यूनिवर्सल स्क्रेपिंग API का उपयोग करके तुलना करने से पहले रेंडर करें।
यह एक आंतरिक JSON एंडपॉइंट पर भी लागू होता है जिसे पृष्ठ कॉल करता है। वे अक्सर वेलिडेटर भेजते हैं, और जब वे करते हैं, तो शीर्ष परत फिर से रिकॉर्ड ले जाने वाले पेलोड के खिलाफ काम करती है।
स्थिति को स्टोर करना
इनमें से कोई भी काम नहीं करता जब तक कि कहीं ऐसा न हो कि अंतिम रन ने क्या सीखा है। एक पाइपलाइन की आवश्यकता वाली स्थिति छोटी है:
| कॉलम | उद्देश्य |
|---|---|
url |
क्या परखा गया |
etag / last_modified |
शर्तपूर्ण शीर्षक के रूप में फिर से चलाया गया |
body_sha256 |
जब कोई वेलिडेटर नहीं है तो दस्तावेज़-स्तरीय तुलना |
checked_at |
जब अंतिम पुष्टि हुई थी |
प्रत्येक रिकॉर्ड के लिए, तालिका में कुंजी, फ़िंगरप्रिंट, और कोई भी पूर्व मान जो आप रिपोर्ट करना चाहते हैं। दोनों स्क्रैप किए गए डेटा के समान डेटाबेस में फिट होते हैं, और ETL पाइपलाइन का आकार अपरिवर्तित है — एक चेक स्टेप को निकालने के सामने जोड़ा गया है।
एक संचालन नोट जो चूकना आसान होता है: एक ETag केवल उस URL के लिए मान्य है जिसने इसे जारी किया। एक अलग क्वेरी स्ट्रिंग या एक पैजिनेटेड वैरिएंट के खिलाफ स्टोर किए गए टैग को फिर से चलाने से एक 200 और एक पूर्ण बॉडी उत्पन्न होगी, जो एक सही व्यवहार है न कि कोई दोष।
निष्कर्ष
वृद्धिशील स्क्रेपिंग तीन प्रश्न हैं, और यह जानना कि आप कौन सा पूछ रहे हैं यह तय करता है कि आप क्या बचाते हैं। HTTP वेलिडेटर बॉडी को बचाता है — 50,388 बाइट्स एक 0-बाइट 304 में बदल गया। दस्तावेज़ हैश पार्सिंग और लिखता है लेकिन कभी भी बैंडविड्थ नहीं, क्योंकि 11,064 बाइट्स तुलना से पहले आती हैं। रिकॉर्ड फ़िंगरप्रिंट लिखाई को बचाता है, और बीस-रिकॉर्ड पृष्ठ को एकल पंक्ति में ले गया।
जब सर्वर एक प्रदान करता है, तो वेलिडेटर से शुरू करें, कच्ची बॉडी के बजाय निकाले गए रिकॉर्ड को हैशिंग करने के लिए फिर से गिरें, और केवल उन फ़ील्ड को फ़िंगरप्रिंट करें जिनकी गतिविधि आपको वास्तव में परवाह है। मूल्य निर्धारण सूचियाँ कि जब अपरिवर्तित मूल्य खत्म हो जाते हैं तो शेष फ़ेच के लिए क्या लागत होती है।
क्या आप उन पृष्ठों को फिर से स्क्रेप करना बंद करने के लिए तैयार हैं जो नहीं चले हैं? Scrapeless मुफ्त योजना के साथ शुरू करें और वह आधारभूत रेखा बनाएं जिसके खिलाफ आपकी अगली रन की तुलना होती है।
अक्सर पूछे जाने वाले प्रश्न
प्रश्न: वृद्धिात्मक वेब स्क्रैपिंग क्या है?
सिर्फ वही स्क्रैप करना जो पिछले रन से बदल गया है, न कि पूरे लक्ष्य को फिर से संग्रहित करना। इसे एक ऐसे चेक के रूप में लागू किया जाता है जो फेच करने से पहले या लिखने से पहले चलता है: एक शर्तीय HTTP अनुरोध, एक दस्तावेज़ हैश तुलना, या प्रति-रिकॉर्ड फिंगरप्रिंट तुलना। यहाँ मापा गया प्रभाव 50,388 बाइट HTTP परत पर बचाए गए और रिकॉर्ड परत पर 20 में से 19 पंक्तियाँ छोड़ने का था।
प्रश्न: मैं स्क्रैपर में ETag का उपयोग कैसे करूं?
प्रतिक्रिया से ETag हेडर को स्टोर करें, फिर इसे उसी URL के लिए अगले अनुरोध पर If-None-Match के रूप में वापस भेजें। एक सर्वर जो सहमत है कि कुछ भी नहीं बदला, 304 के साथ एक खाली बॉडी के साथ जवाब देता है, जो शेष पाइपलाइन को छोड़ने का संकेत है। टैग सटीक URL से बंधा होता है, इसलिए एक संग्रहीत टैग जिसे एक अलग क्वेरी स्ट्रिंग के खिलाफ फिर से चलाया गया है, एक सामान्य 200 लौटाता है।
प्रश्न: क्या हो यदि कोई साइट कोई ETag या Last-Modified नहीं भेजती?
इसके बजाय हैश प्राप्त करें और तुलना करें। निकाले गए रिकॉर्ड्स का हैश करें न कि कच्चे HTML का - एक कच्चे-बॉडी हैश तब बदलता है जब एक सत्र टोकन, विज्ञापन या रेंडर्ड किए गए टाइमस्टैम्प में बदलाव होता है, जो पेज को गंदा मार्क करता है जबकि डेटा समान होता है। यह किसी भी हालत में बैंडविड्थ की लागत आती है; बचत पार्सिंग, लेखन और नीचे की किसी भी चीज में है।
प्रश्न: क्या मुझे पूरे रिकॉर्ड का हैश करना चाहिए या विशिष्ट क्षेत्रों का?
विशिष्ट क्षेत्रों का। एक पूरे रिकॉर्ड का हैश वह सब कुछ शामिल करता है जो पृष्ठ में होता है, इसलिए एक रैंक स्थिति या "अंतिम बार देखा" काउंटर हर रन पर हर रिकॉर्ड को बदलता दिखाता है। केवल उन क्षेत्रों का फिंगरप्रिंट बनाना जिनका आंदोलन महत्वपूर्ण है, वही है जिसने ऊपर स्थिर 19-अपरिवर्तित परिणाम पैदा किया।
प्रश्न: क्या एक पृष्ठ 304 लौट सकता है जबकि इसके डेटा ने वास्तव में बदलाव किया है?
हाँ, क्लाइंट-रेंडर किए गए पृष्ठों पर। वेलिडेटर उस HTML दस्तावेज़ का वर्णन करता है जो सर्वर ने भेजा, जो एक एकल-पृष्ठ एप्लिकेशन के लिए शेल है न कि रिकॉर्ड, इसलिए टैग शेल के डिप्लॉयमेंट का ट्रैक रखता है। उन लक्ष्यों को रेंडर करने के बाद रिकॉर्ड परत पर तुलना की आवश्यकता होती है, या उस आंतरिक JSON एंडपॉइंट के खिलाफ जो डेटा ले जाता है।
प्रश्न: वृद्धिात्मक स्क्रैपिंग वास्तव में कितनी बचत करता है?
यह इस बात पर निर्भर करता है कि कौन सी परत उत्तर देती है। ऊपर के माप में, शर्तीय अनुरोधों ने प्रतिक्रिया शरीर को पूरी तरह से हटा दिया और दस चेक में 1.31 गुना तेजी से चले, और रिकॉर्ड परत ने 95% लेखन को काट लिया जहाँ बीस में से एक आइटम हिल गया। राउंड ट्रिप हर मामले में बना रहता है, इसलिए बचत पृष्ठ के आकार और बदलाव दर के साथ बढ़ती है न कि अनुरोध की संख्या के साथ।
स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।



