حساب DR بالجملة لقائمة عملاء، كبرنامج Python لا يتجاوز 40 سطراً
لديك جدول بيانات يحتوي على نطاقات، ولوحة تحكم تحتاج إلى رقم واحد لكل صف. أداة فحص نطاقات الروابط الخلفية المخصصة لهذا الغرض تعتمد بشكل أساسي على البنية التحتية، وهذا السكربت يوفر تلك البنية.

السكربت
يقرأ الملف 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 الخام
تحلله لاحقًا، مرة واحدة، مقابل عينة مخزنة. تغيير المخطط من جانبنا يكلفك استعلامًا، وليس إعادة تشغيل.

تشغيل افتراضي، مع الحساب
هذا مثال، وليس قصة عميل. تخيل وكالة تتتبع 1800 نطاق للعملاء والمحتملين في ورقة بيانات Google وتريد تحديث DR كل يوم اثنين.
عند 200 نطاق في الدفعة، يكون ذلك 9 طلبات. تسعة طلبات بسعر 1 رصيد لكل طلب تساوي 9 أرصدة، أو $0.0009 لكل تشغيل، وعلى مدار 52 يوم اثنين، يكون المجموع 468 رصيد، وهو ما يقل عن خمسة سنتات.
العائق ليس التكلفة، بل التجدد. يُعاد بناء الفهرس شهرياً تقريباً، لذا ستظهر الجريانات الأسبوعية نفس القيم في ثلاثة أسابيع من أصل أربعة، بينما تلتقط الجريانات الشهرية نفس التغيرات مقابل ربع عدد المكالمات فقط.
لذا، قم بتشغيله شهرياً واحتفظ بنسخة الجداول الأسبوعية للتعديلات البشرية. تفاصيل سبب ثبات الدرجة موجودة في مرجع DR.
الأشياء التي قد تفشل في هذا السكربت
| العرض | السبب | الحل |
|---|---|---|
SystemExit: HTTP 402 | الرصيد صفر | قم بإضافة رصيد من صفحة الحساب، ثم أعد التشغيل؛ الدُفعات المكتملة موجودة بالفعل في SQLite |
SystemExit: HTTP 401 | متغير البيئة فارغ أو يوجد خطأ مطبعي في المفتاح | اطبع len(KEY) وتحقق من بادئة sk_ |
ok=false في الدفعة 3 | قيمة غير صالحة في القائمة، مثل عنوان URL كامل | احذف المخطط والمسار قبل الإرسال، انظر جدول الأخطاء |
| نفس DR للأسبوع الماضي | لم يتم إعادة بناء الفهرس بعد | متوقع؛ قارن شهرياً |
| تم رفض عنوان URL الطويل | حجم الدفعة كبير جداً لـ GET | قلل من BATCH أو انتقل إلى جسم الطلب POST |
حجم الدفعة، GET وPOST للقوائم الطويلة
الحد الأقصى هو 2,000 نطاق لكل استدعاء، ويرسل السكربت عُشر هذا العدد. كل استدعاء يكلف نفس المبلغ الثابت من الرصيد، لذا فإن حجم الدفعة يتعلق بالفشل وحجم الحمولة، وليس بالسعر.
لماذا 200 وليس 2,000
الدفعة الأصغر تفشل بتكلفة أقل. إذا توقف التشغيل في المنتصف، فإن الجزء المتب لإعادة التنفيذ يكون بطول دفعة واحدة، وليس بطول القائمة بأكملها.
القوائم الأقصر أيضاً تجعل كل جسم محفوظ صغيراً بما يكفي للفحص بالعين عندما يبدو رقم ما خاطئاً، وهذا الأمر أهم من توفير بضع استدعاءات محفوظة.
إذا قمت بزيادة حجم الدفعة لاحقاً، افعل ذلك مرة واحدة، في ثابت واحد، وأعد تشغيل اختبار النطاقين. الرقم الأكبر ليس سبباً لتخطي الاختبار.
خطط لحجم الدفعة بناءً على طول التشغيل الذي يمكنك تحمله، وليس بناءً على الحد الأقصى. مهمة ليلية يجب أن تنتهي قبل تقرير صباحي يمكنها تحمل دفعات أصغر وأكثر أماناً من عملية تعبئة خلفية لمرة واحدة.
استخدم GET للقوائم القصيرة، وPOST للقوائم الطويلة
يضع نموذج GET كل النطاقات في عنوان URL. يعمل ذلك حتى تقوم وسيط أو سجل السجلات بتقصير الاستعلام، ولهذا السبب يُنصح بخفض حجم الدفعة أولاً. بمجرد نمو القوائم، أرسل قيمة domains نفسها في جسم طلب POST.
اختبر الفاصل والترميز باستخدام نطاقين قبل التشغيل الكامل. قد يظهر فاصلة زائدة في الدفعة الأولى، ومن الأرخص ملاحظتها هناك.
يحافظ جسم طلب POST أيضاً على القائمة خارج سجلات الوصول التي تحتفظ بها العديد من الخوادم افتراضياً. هذه ميزة خصوصية صغيرة لقوائم العملاء، وتكلف سطر رأس إضافياً في السكربت.
عند التحول إلى POST، احتفظ بالإصدار GET من السكربت كحل بديل للقوائم القصيرة. قد يبدو مساران للشفرة ثقيلاً، لكن دالة صغيرة واحدة تختار المسار بناءً على الطولة سهلة القراءة والاختبار.
قم بإزالة المسافات الزائدة وإزالة التكرارات قبل الطلب الأول
احذف المسافات، وحول الأحرف إلى صغيرة، وأزل التكرارات قبل إرسال أي شيء من جهازك. تستهلك التكرارات مساحة تحت سقف الـ 2000 نطاق، وتجعل العدد في ورقتك غير متطابق مع العدد في الاستجابة.
اختر قاعدة واحدة لـ www. وطبقها في دالة واحدة، بحيث لا يتم البحث عن نفس الموقع تحت تهجئتين مختلفتين. أي أداة لفحص الروابط الخلفية تتجاوز بضع مئات من الأسماء تحتاج إلى هذا الانضباط.
ما يجب أن يظهره سجل التشغيل
اطبع قيمة _credits بعد كل دفعة، واكتب رقم الدفعة وعدد النطاقات والوقت بجانبها. هذه الأرقام الثلاثة تجيب تقريباً على كل سؤال يطرحه قارئ مالي حول التشغيل.
التشغيل الذي لا يطبع شيئاً حتى النهاية لا يعطيك أي فكرة عندما يتوقف. سطر لكل دفعة رخيص، ويحول التوقف الصامت إلى رقم دفعة يمكنك تحديده.
احتفظ بالسجل في نفس المجلد الموجود فيه ملف SQLite، بحيث ينسخ النسخ الاحتياطي كليهما. السجل الذي يعيش في مكان آخر هو الذي يضيع أثناء الحادث.

ملحقان يستحقان بعد الظهر
احفظ رقم الدفعة بجانب الطابع الزمني. هو موجود بالفعل في الجدول أعلاه، ويسمح لك بإعادة تشغيل الدفعة التي فشلت فقط بعد حدوث خطأ 402 أثناء التشغيل.
اكتب عرضاً صغيراً فوق الجدول الخام بمجرد فحص استجابة حقيقية: النطاق، النتيجة، وقت الاسترداد. تقرأ لوحات التحكم العرض، ويبقى العمود الخام كدليل لك إذا تم التشكيك في أي رقم.
شغله من GitHub Actions أو cron عادي على خادم VPS صغير. أي منهما جيد. ما يهم هو أن المهمة تخرج بقيمة غير صفرية عند أي فشل، لأن الفشل الصامت يبدو وكأنه رسم بياني قديم بعد ثلاثة أسابيع.
ترتيب المخرجات مسألة واجهة أمامية. رتب المحفظة حسب النتيجة، أو صنّفها، أو قارنها مع dr_distribution (1 رصيد) لمعرفة مكان كل عميل بين جميع 181,132,097 نطاقاً.
انحراف الحقول والجدول الخام
تفشل المهمة طويلة الأمد بصمت عندما يتغير شكل حقل. العمود الخام موجود لضمان بقاء الفشل واضحاً ورخيصاً في الإصلاح.
احفظ الجسم الخام أولاً
العمود الخام هو الشبكة الأمان. إذا تم تغيير اسم حقل أو ظهور حقل جديد، يمكنك إعادة تشغيل استعلام فوق الاستجابات المخزنة بدلاً من دفع ثمن القائمة بالكامل مرة أخرى. التحليل لاحقاً رخيص، بينما إعادة الشراء ليست كذلك.
احتفظ بنموذج محفوظ واحد في الأسبوع في ملف خاص به أيضاً. عندما ينكسر التحليل، قارن عينة هذا الأسبوع مع آخر عينة عملت.
اكتب الجسم بالضبط كما استلمته، بدون تنسيق جميل أو قص. أي تغيير تقوم به على النص قبل تخزينه هو تغيير لا يمكنك التراجع عنه لاحقاً.
كيف يبدو الانحراف في الممارسة العملية
لنفترض أن لوحة التحكم تظهر فجأة نتائج فارغة لدفعة واحدة. الصف الخام لا يزال موجوداً. تفتحه، تقارن مفاتيحه مع عينة الأسبوع الماضي، وتجد الفرق في بضع دقائق.
الإصلاح هو سطر واحد فقط في العرض، وليس إعادة تشغيل للقائمة بالكامل. هذه هي الفكرة الكاملة من تخزين الجسم.
احتفظ بملاحظة قصيرة بجانب العرض تقول أي حقل يقرأه ومتى قمت بفحصه آخر مرة مقابل استجابة حقيقية. تأخذ الملاحظة دقيقة للكتابة وتوفر بحثاً طويلاً لاحقاً.
رتب عمليات الترحيل مع البيانات
أنشئ الجدول الخام قبل العرض، والعرض قبل أي استعلام لوحة تحكم. احتفظ ببيانات الإنشاء في ملف واحد، وشغلها بالترتيب وسجل رقم الدفعة مع كل صف.
قواعد البيانات المنفصلة لكل عميل تنحرف بنفس الطريقة التي تنحرف بها قاعدة بيانات مشتركة. القاعدة تبقى نفسها: جسم مخزن واحد لكل استدعاء، ومكان واحد لتحليله.
احتفظ بالمحلل بجانب الاختبارات
ضع دالة التحليل في نفس المستودع مع اختبار يقرأ جسماً محفوظاً واحداً. الاختبار هو المكان الذي يظهر فيه الحقل المنحرف أولاً، قبل وقت طويل من ظهور الرسم البياني.
سمِّ الحقول التي يتوقعها المحلل في ثابت واحد بالقرب من بداية الملف. يمكن للمراجع بعد ذلك رؤية كل الاعتماديات على شكل الاستجابة في شاشة واحدة.
التشغيل الأول، بالترتيب
- 1
اختبر نطاقاً واحداً
استدعِ
bulk_drبنطاق واحد تعرفه، واقرأ الاستجابة بالكامل. اكتب المفاتيح قبل كتابة أي كود تحليل. - 2
شغل دفعتين
أعط السكربت قائمة من 400 نطاق، بحيث تعمل بالضبط دفعتين. تحقق من أن انخفاض
_creditsيتطابق مع استدعائين بسعر رصيد واحد لكل منهما. - 3
عد الصفوف المخزنة
افتح ملف SQLite وقارن عدد الصفوف مع عدد الدفات. عدم التطابق يعني أن دفعة تم تخطيها أو تخزينها مرتين، وستكتشف ذلك قبل أن تكتشفه لوحة التحكم.
- 4
جدول القائمة الكاملة
فقط بعد أن تكون اختبارات التشغيل نظيفة، أضف القائمة الكاملة والمجدول. احتفظ بخروج غير صفري، حتى يكون الفشل واضحاً في الليلة الأولى.
قبل جدولته
- أزل التكرارات وحول الأحرف إلى صغيرة في القائمة أولاً
- قرر مرة واحدة، في دالة واحدة، ما إذا كان
www.يبقى أم يذهب - احتفظ بالمفتاح في متجر الأسرار، وليس في جدول cron
- تنبيه على رمز خروج غير صفري
- شغل أرقام ميزانية الرصيد لحجمك الخاص