क्लाइंट लिस्ट के लिए बल्क DR, एक 40-लाइन Python जॉब के रूप में
आपके पास डोमेनों की एक स्प्रेडशीट है और एक डैशबोर्ड जिसमें प्रत्येक पंक्ति के लिए एक नंबर की आवश्यकता है। उस काम के लिए एक बैकलिंक डोमेन चेकर ज्यादातर प्लंबिंग है, और यह स्क्रिप्ट वह प्लंबिंग है।

स्क्रिप्ट
यह domains.txt पढ़ता है, प्रति अनुरोध 200 डोमेन भेजता है, प्रत्येक कच्चे प्रतिक्रिया को SQLite में रखता है, और प्रिंट करता है कि रन का खर्च क्या था। हम पूरा पेलोड स्टोर करते हैं क्योंकि फ़ील्ड नाम एक वास्तविक प्रतिक्रिया से कॉपी करना बेहतर होता है, न कि एक ब्लॉग पोस्ट से।
import os, sqlite3, time, json, requests
BASE = "https://seomasters.dev/api.php"
KEY = os.environ["SEO_API_KEY"]
BATCH = 200
db = sqlite3.connect("dr.db")
db.execute("create table if not exists raw (ts int, batch text, body text)")
domains = [d.strip() for d in open("domains.txt") if d.strip()]
spent = 0
for i in range(0, len(domains), BATCH):
chunk = domains[i:i + BATCH]
for attempt in range(4):
try:
r = requests.get(BASE, timeout=30,
params={"action": "bulk_dr", "domains": ",".join(chunk)},
headers={"Authorization": "Bearer " + KEY})
break
except requests.RequestException:
time.sleep(2 ** attempt)
else:
raise SystemExit(f"network gave up at batch {i}")
if r.status_code in (401, 402):
raise SystemExit(f"HTTP {r.status_code}: fix the key or top up, do not retry")
body = r.json()
if not body.get("ok"):
raise SystemExit(f"ok=false at batch {i}: {json.dumps(body)[:300]}")
db.execute("insert into raw values (?,?,?)", (int(time.time()), str(i), r.text))
db.commit()
print(body.get("_credits"))प्रत्येक भाग क्या कर रहा है
action=bulk_dr
प्रति कॉल एक क्रेडिट। दस्तावेज़ी सीमा 2,000 डोमेन प्रति कॉल है।
domains=
यहां एक GET पैरामीटर। एंडपॉइंट एक POST बॉडी भी स्वीकार करता है, जो सूचियां लंबी होने पर बेहतर विकल्प है। पूरे रन से पहले दो डोमेनों के साथ सेपरेटर का परीक्षण करें।
BATCH = 200
हमारी आदत, कोई सीमा नहीं: 200 छोटे डोमेन URL को सामान्य प्रॉक्सी सीमाओं से काफी नीचे रखते हैं।
_credits
प्रमाणीकृत प्रतिक्रियाओं पर मौजूद: कॉल की लागत और शेष राशि। प्रत्येक बैच में इसे प्रिंट करना सबसे सस्ता ऑडिट ट्रेल है।
कच्चा body कॉलम
आप बाद में पार्स करते हैं, एक बार, एक स्टोर किए गए नमूने के खिलाफ। हमारी ओर से एक स्कीमा बदलाव फिर आपको एक क्वेरी का खर्च करता है, न कि एक ररन का।

एक हाइपोथेटिकल रन, अंकगणित के साथ
यह एक उदाहरण है, ग्राहक की कहानी नहीं। एक एजेंसी की कल्पना करें जो Google Sheet में 1,800 क्लाइंट और प्रॉस्पेक्ट डोमेनों को ट्रैक करती है और हर सोमवार को DR रिफ्रेश करना चाहती है।
प्रति बैच 200 डोमेन पर, यह 9 अनुरोध हैं। 9 अनुरोध, प्रत्येक 1 क्रेडिट पर, 9 क्रेडिट हैं, या प्रति रन $0.0009, और 52 सोमवारों पर कुल 468 क्रेडिट हैं, जो पांच सेंट से कम है।
लागत बाधा नहीं है। ताजगी बाधा है। इंडेक्स लगभग मासिक रूप से रीबिल्ड होता है, इसलिए साप्ताहिक रन चार में से तीन सप्ताह समान मान दिखाएंगे, और एक मासिक रन लगभग एक चौथाई कॉल्स के लिए समान गति को कैप्चर करता है।
तो इसे मासिक रूप से चलाएं और मानव संपादन के लिए साप्ताहिक शीट रखें। स्कोर स्थिर क्यों रह सकता है, इसके विवरण DR संदर्भ में हैं।
इस स्क्रिप्ट में गलतियां
| लक्षण | कारण | फिक्स |
|---|---|---|
SystemExit: HTTP 402 | बैलेंस शून्य है | अकाउंट पेज से टॉप अप करें, ररन करें; पूरे बैच पहले से SQLite में हैं |
SystemExit: HTTP 401 | Env var खाली या की टाइपो | len(KEY) प्रिंट करें और प्रीफिक्स sk_ जांचें |
बैच 3 पर ok=false | सूची में खराब मान, जैसे कि एक पूर्ण URL | भेजने से पहले स्कीम और पथ हटाएं, त्रुटि तालिका देखें |
| पिछले सप्ताह के समान DR | इंडेक्स अभी तक रीबिल्ड नहीं हुआ है | अपेक्षित; मासिक रूप से तुलना करें |
| लंबा URL अस्वीकृत | बैच GET के लिए बहुत बड़ा | BATCH कम करें या POST बॉडी में जाएं |
लंबी सूचियों के लिए बैटच साइज, GET और POST
सीमा प्रति कॉल 2,000 डोमेन है, और स्क्रिप्ट उसका एक दसवां हिस्सा भेजता है। प्रत्येक कॉल की लागत समान फ्लैट क्रेडिट है, इसलिए बैटच साइस विफलता और पेलोड का प्रश्न है, कीमत का नहीं।
2,000 के बजाय 200 क्यों
एक छोटा बैटच सस्ता विफल होता है। यदि एक रन बीच में रुक जाता है, तो दोहराने के लिए बचा हुआ हिस्सा एक बैटच लंबा होता है, एक लिस्ट लंबा नहीं।
छोटी सूचियां प्रत्येक सहेजे गए बॉडी को इतनी छोटी रखती हैं कि आंख से स्कैन किया जा सके जब कोई नंबर गलत दिखे, और यह कुछ बचत कॉल्स से अधिक मायने रखता है।
यदि आप बाद में बैटच साइज बढ़ाते हैं, तो इसे एक बार, एक स्थिरांक में करें, और दो-डोमेन टेस्ट ररन करें। एक बड़ा नंबर टेस्ट छोड़ने का कारण नहीं है।
रन की लंबाई के खिलाफ बैटच की योजना बनाएं जिसे आप सहन कर सकते हैं, न कि सीमा के खिलाफ। एक रात की जॉब जिसे सुबह की रिपोर्ट से पहले खत्म करना है, एक-बार के बैकफिल की तुलना में छोटे, सुरक्षित बैटच सहन कर सकती है।
छोटी सूचियों के लिए GET, लंबी सूचियों के लिए POST
GET फॉर्म डोमेन को URL में डालता है। यह तब तक काम करता है जब तक प्रॉक्सी या लॉग लाइन क्वेरी को ट्रंक न कर दे, इसलिए ऊपर दी गई टेबल में बैच को कम करने की सलाह दी गई है। एक बार सूची बड़ी हो जाए, तो domains मान को POST बॉडी में भेजें।
पूरी रन शुरू करने से पहले दो डोमेन के साथ सेपरेटर और एन्कोडिंग की जांच करें। एक अकेला कॉमा पहले बैच में दिखता है, और वहां इसे देखना सस्ता पड़ता है।
POST बॉडी सूची को एक्सेस लॉग से भी बाहर रखती है, जिसे कई होस्ट डिफ़ॉल्ट रूप से रखते हैं। यह क्लाइंट सूची के लिए एक छोटा गोपनीयता लाभ है, और इसमें स्क्रिप्ट में एक अतिरिक्त हेडर लाइन की लागत आती है।
जब आप POST पर स्विच करते हैं, तो छोटी सूचियों के लिए स्क्रिप्ट का GET संस्करण फॉलबैक के रूप में रखें। दो कोड पथ भारी लग सकते हैं, लेकिन लंबाई के आधार पर एक चुनने वाला एक छोटा फंक्शन पढ़ने और टेस्ट करने में आसान है।
पहले अनुरोध से पहले स्पेस हटाएं और डुप्लिकेट हटाएं
अपनी मशीन से कुछ भी बाहर जाने से पहले व्हाइटस्पेस हटाएं, होस्ट को लोकेर और डुप्लिकेट को हटाएं। डुप्लिकेट 2,000-डोमेन की छत के नीचे जगह लेते हैं, और आपके शीट में गिनती और रिस्पॉन्स में गिनती में असंगतता पैदा करते हैं।
www. के लिए एक नियम चुनें और उसे एक ही फंक्शन में लागू करें, ताकि एक ही साइट को दो अलग-अलग वर्तनी के तहत कभी भी खोजा न जाए। हर सैकड़ों नामों से आगे बढ़ने वाले किसी भी बैकलिंक डोमेन चेकर के लिए उस अनुशासन की आवश्यकता होती है।
रन लॉग में क्या लिखा होना चाहिए
हर बैच के बाद _credits मान प्रिंट करें, और उसके बगल में बैच नंबर, डोमेन गिनती और समय लिखें। ये तीन नंबर रन के बारे में हर वित्तीय पाठक के लगभग हर सवाल का जवाब देते हैं।
एक रन जो अंत तक कुछ भी प्रिंट नहीं करता, आपको यह पता नहीं चलता कि वह कहां अटक गया है। प्रति बैच एक लाइन सस्ती है, और यह एक सन्नाटे वाली हंग को एक बैच नंबर में बदल देती है जिसे आप नाम दे सकते हैं।
लॉग को SQLite फ़ाइल के उसी फोल्डर में रखें, ताकि एक बैकअप दोनों को कॉपी कर सके। एक लॉग जो कहीं और रहता है, वह ही किसी आपदा के दौरान गायब हो जाता है।

दोपहर के समय के लिए तीन एक्सटेंशन जो किमती हैं
टाइमस्टैम्प के बगल में बैच नंबर स्टोर करें। यह ऊपर दी गई टेबल में पहले से मौजूद है, और यह आपको 402 की मध्य-रन के बाद केवल उसी बैच को फिर से चलाने देता है जो फेल हो गया था।
एक असली रिस्पॉन्स की जांच करने के बाद कच्ची टेबल पर एक छोटा व्यू लिखें: डोमेन, स्कोर, फेच-एट। डैशबोर्ड व्यू को पढ़ते हैं, और यदि किसी नंबर पर कभी सवाल उठाया जाए तो कच्ची कॉलम आपके सबूत के रूप में रहता है।
इसे GitHub Actions या एक छोटे VPS पर साधारण क्रॉन से चलाएं। दोनों ठीक हैं। जो मायने रखता है वह यह है कि किसी भी विफलता पर जॉब नॉन-जीरो एक्सिट पर निकल जाए, क्योंकि एक सन्नाटे वाली विफलता तीन सप्ताह बाद एक पुरानी चार्ट जैसी दिखती है।
आउटपुट को सॉर्ट करना एक फ्रंट-एंड मामला है। पोर्टफोलियो को स्कोर के आधार पर रैंक करें, इसे बकेट करें, या dr_distribution (1 क्रेडिट) के साथ तुलना करें ताकि यह देखा जा सके कि प्रत्येक क्लाइंट 181,132,097 डोमेन के बीच कहां बैठता है।
फ़ील्ड ड्रिफ्ट और कच्ची टेबल
एक लंबे समय तक चलने वाली जॉब तब चुपचाप फेल हो जाती है जब एक फ़ील्ड का आकार बदल जाता है। कच्ची कॉलम वहां इसलिए है ताकि विफलता शोर वाली और सस्ती हो।
कच्चे बॉडी को पहले स्टोर करें
कच्ची कॉलम सुरक्षा जाल है। यदि कोई फ़ील्ड बदल दी जाती है या कोई नई आती है, तो आप पूरी सूची के लिए फिर से पैसे देने के बजाय स्टोर किए गए रिस्पॉन्स पर एक क्वेरी फिर से चलाते हैं। बाद में पार्स करना सस्ता है, जबकि फिर से खरीदना नहीं।
एक सप्ताह में एक सहेजे गए नमूने को एक अलग फ़ाइल में भी रखें। जब पार्स टूट जाता है, तो आप इस सप्ताह के नमूने की तुलना पिछले काम करने वाले नमूने से डीफ करते हैं।
बॉडी को बिल्कुल वैसे ही लिखें जैसे प्राप्त हुआ था, बिना किसी प्रिंटिंग और बिना किसी ट्रिमिंग के। टेक्स्ट में कोई भी बदलाव जो आप स्टोर करने से पहले करते हैं, बाद में वह बदलाव आप उल्टा नहीं कर सकते।
व्यावहारिक रूप से ड्रिफ्ट कैसा दिखता है
मान लीजिए एक डैशबोर्ड अचानक एक बैच के लिए खाली स्कोर दिखाता है। कच्ची पंक्ति अभी भी मौजूद है। आप उसे खोलते हैं, इसकी कुंजियों की तुलना पिछले सप्ताह के नमूने से करते हैं, और कुछ मिनटों में अंतर पाते हैं।
फिर से ठीक करने का तरीका व्यू में एक ही लाइन है, पूरी सूची को फिर से चलाना नहीं। ही स्टोर करने का पूरा मकसद बॉडी को स्टोर करना है।
व्यू के बगल में एक छोटा नोट रखें कि यह किस फ़ील्ड को पढ़ता है और आपने कब इसे एक असली रिस्पॉन्स से जांचा था। नोट लिखने में एक मिनट लगता है और बाद में लंबी खोज बचाता है।
डेटा के साथ माइग्रेशन को क्रम में रखें
कच्ची टेबल को व्यू से पहले बनाएं, और व्यू को किसी भी डैशबोर्ड क्वेरी से पहले बनाएं। क्रिएट स्टेटमेंट्स को एक फ़ाइल में रखें, उन्हें क्रम में चलाएं और प्रत्येक पंक्ति के साथ बैच नंबर रिकॉर्ड करें।
क्लाइंट ड्रिफ्ट के लिए अलग-अलग डेटाबेस उसी तरह अलग होते हैं जैसे एक साझा डेटाबेस होता है। नियम वही रहता है: प्रत्येक कॉल के लिए एक स्टोर किया गया बॉडी, और इसे पार्स करने के लिए एक जगह।
पार्सर को टेस्ट के बगल में रखें
पार्स फंक्शन को उसी रिपॉजिटरी में रखें जहां एक टेस्ट एक सहेजे गए बॉडी को पढ़ता है। टेस्ट वह जगह है जहां एक ड्रिफ्टेड फ़ील्ड पहले दिखती है, एक चार्ट से बहुत पहले।
पार्सर द्वारा अपेक्षित फ़ील्ड्स को फ़ाइल के ऊपर एक कॉन्स्टेंट में नाम दें। एक रिव्यूअर तब प्रतिक्रिया के आकार पर हर निर्भरता को एक ही स्क्रीन पर देख सकता है।
पहली रन, क्रम में
- 1
एक डोमेन का टेस्ट करें
bulk_drको एक ऐसे डोमेन के साथ कॉल करें जिसे आप जानते हैं, और पूरी प्रतिक्रिया पढ़ें। किसी भी पार्सिंग कोड लिखने से पहले कुंजियों को नोट करें। - 2
दो बैच चलाएं
स्क्रिप्ट को 400 डोमेन की एक सूची दें, ताकि बिल्कुल दो बैच चलें। जांचें कि
_creditsमें कमी दो कॉल के बराबर है, जिनमें से प्रत्येक का एक क्रेडिट है। - 3
स्टोर की गई पंक्तियों की गिनती करें
SQLite फ़ाइल खोलें और पंक्ति गिनती की तुलना बैच गिनती से करें। एक असंगति का मतलब है कि एक बैच छोड़ दिया गया था या दो बार स्टोर किया गया था, और आप इसे डैशबोर्ड से पहले पाते हैं।
- 4
पूरी सूची को शेड्यूल करें
केवल तब ही पूरी सूची और शेड्यूलर जोड़ें जब टेस्ट रन साफ हो जाए। नॉन-जीरो एक्सिट को रखें, ताकि एक विफलता पहले रात में शोर वाली हो।
शेड्यूल करने से पहले
- सबसे पहले सूची को डुप्लिकेट हटाएं और लोकेर करें
- एक बार तय करें, एक फंक्शन में, कि
www.रहता है या जाता है - कुंजी को सीक्रेट स्टोर में रखें, क्रॉनटैब में नहीं
- नॉन-जीरो एक्सिट कोड पर अलर्ट करें
- अपने वॉल्यूम के लिए क्रेडिट बजट संख्याओं को चलाएं