تحديد المعدل من جانب العميل هو صمام الأمان الذي يمنع سكربتاً واحداً به خطأ من استنزاف رصيدك كاملاً في دقائق. صحيحٌ أن CaptchaAI مبني على نموذج الخيوط (Threads) ويتحمّل تزامناً عالياً دون أن يفرض عليك حدوداً صارمة، لكن هذه المرونة نفسها تعني أن أي حلقة لا نهائية أو ارتفاع مفاجئ في الطلبات سيمرّ دون اعتراض. الحلّ أن تضع أنت السقف: يعرض هذا الدليل ثلاثة أنماط عملية لتحديد معدل طلباتك قبل أن تغادر تطبيقك وتصل إلى CaptchaAI — دلو الرموز، والنافذة المنزلقة، والمُحدِّد المعتمد على الميزانية.
لماذا تُحدِّد معدل طلباتك بنفسك؟
الدافع الأول هو التحكم في التكلفة، لكنه ليس الوحيد. تخيّل وكالة تطوير في القاهرة أو دبي تدير اختبارات جودة (QA) آلية لعدة متاجر إلكترونية عبر مفتاح API واحد مشترك؛ يكفي أن يدخل مشروع واحد في حلقة إعادة محاولة معطوبة حتى يلتهم رصيد بقية الفرق قبل نهاية اليوم. تحديد المعدل من جانبك يحوّل هذا الخطر إلى سقف قابل للضبط.
تختصر هذه المقارنة الفارق بين التشغيل بلا حدود والتشغيل مع سقف مضبوط:
| السيناريو | بلا حدّ | مع حدّ |
|---|---|---|
| خطأ برمجي يُدخل الكود في حلقة حلّ لا نهائية | يستنزف الرصيد بالكامل | يتوقّف عند السقف المُعرّف مسبقاً |
| عدة فرق تتشارك مفتاح API واحداً | إنفاق غير منسّق | توزيع عادل لكل فريق |
| الموقع المستهدف يحظر عند تجاوز 100 طلب/دقيقة | حظر الحسابات | البقاء تحت العتبة الآمنة |
| الميزانية المخصّصة 50 دولاراً شهرياً | قد تُستهلك في فترة ما بعد الظهر | سقف صارم مفروض |
الأنماط الثلاثة التالية تعالج هذه الحالات، ويمكنك دمج أكثر من نمط معاً في أنظمة الإنتاج.
حدود الخيوط في CaptchaAI مقابل تحديدك أنت للمعدل
من المهم التمييز بين مستويين مختلفين من التحكم. خطط CaptchaAI مبنية على الخيوط (Threads) لا على عدد عمليات الحل: تبدأ خطة BASIC من 15 دولاراً شهرياً مع 5 خيوط متزامنة وعدد غير محدود من عمليات الحل داخل الشهر، وترتفع الخيوط مع كل خطة أعلى. هذا يعني أن سرعتك القصوى محكومة بعدد الخيوط، وليس برسم على كل عملية حلّ.
أمّا تحديد المعدل الذي نناقشه هنا فيعمل داخل تطبيقك أنت، قبل أن يصل الطلب إلى الخدمة أصلاً. الهدف منه ليس الالتفاف على الخطة، بل ضبط إيقاع طلباتك لأسباب تخصّك: مطابقة عتبة الموقع المستهدف، أو توزيع الرصيد بين الفرق، أو وضع سقف مالي يومي. باختصار: الخيوط تحدّد ما تستطيع الخدمة تنفيذه بالتوازي، وتحديد المعدل يحدّد ما تسمح أنت لتطبيقك بإرساله.
النمط الأول: دلو الرموز (Token Bucket)
يمنح دلو الرموز أفضل توازن بين المرونة والانضباط: يسمح بدفعات قصيرة عند الحاجة مع فرض معدل متوسط ثابت على المدى الطويل. تُضاف الرموز إلى الدلو بمعدل ثابت، ويستهلك كل طلب رمزاً واحداً؛ فإذا فرغ الدلو انتظر الطلب حتى يتجدّد رمز جديد. هذا يجعله الخيار الأنسب حين تتفاوت طلباتك بين هدوء وذروة.
في المثال التالي نضبط الحدّ على 10 عمليات حلّ في الدقيقة مع سماحية دفعة قدرها 5:
# token_bucket_solver.py
import os
import time
import threading
import requests
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
class TokenBucket:
"""Token bucket rate limiter."""
def __init__(self, rate, capacity):
"""
rate: tokens added per second
capacity: max tokens (burst size)
"""
self.rate = rate
self.capacity = capacity
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def acquire(self, timeout=30):
"""Wait for a token. Returns True if acquired, False on timeout."""
deadline = time.monotonic() + timeout
while True:
with self.lock:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True
if time.monotonic() >= deadline:
return False
time.sleep(0.1)
def _refill(self):
now = time.monotonic()
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_refill = now
# Allow 10 solves/minute with burst of 5
limiter = TokenBucket(rate=10/60, capacity=5)
def solve_rate_limited(sitekey, pageurl):
"""Solve with rate limiting."""
if not limiter.acquire(timeout=60):
raise Exception("Rate limit: could not acquire token within 60s")
session = requests.Session()
resp = session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
raise Exception(f"Submit failed: {result.get('request')}")
task_id = result["request"]
time.sleep(15)
for _ in range(25):
poll = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
})
poll_result = poll.json()
if poll_result.get("status") == 1:
return poll_result["request"]
if poll_result.get("request") != "CAPCHA_NOT_READY":
raise Exception(f"Error: {poll_result.get('request')}")
time.sleep(5)
raise Exception("Timeout")
لاحظ أن القفل (threading.Lock) يجعل المُحدِّد آمناً عند استخدامه من عدة خيوط داخل نفس العملية، وأن الحدّ مطبَّق على طلب الإرسال فقط لا على الاستطلاع.
النمط الثاني: النافذة المنزلقة (Sliding Window)
النافذة المنزلقة أبسط مفهوماً: تتتبّع عدد الطلبات خلال فترة زمنية محدّدة وترفض ما يتجاوز الحدّ حتى تخرج أقدم الطلبات من النافذة. تفتقر إلى التحكم الدقيق في الدفعات الذي يوفّره دلو الرموز، لكنها كافية تماماً حين تريد قاعدة واضحة من نوع «لا تتجاوز س طلباً كل ص دقيقة».
هنا نسمح بـ 20 عملية حلّ في كل 5 دقائق:
// sliding_window_solver.js
const axios = require('axios');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
class SlidingWindowLimiter {
constructor(maxRequests, windowMs) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.timestamps = [];
}
async acquire(timeoutMs = 60000) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
// Remove expired timestamps
const cutoff = Date.now() - this.windowMs;
this.timestamps = this.timestamps.filter(t => t > cutoff);
if (this.timestamps.length < this.maxRequests) {
this.timestamps.push(Date.now());
return true;
}
// Wait until the oldest request exits the window
const waitMs = Math.min(
this.timestamps[0] + this.windowMs - Date.now() + 10,
deadline - Date.now()
);
if (waitMs > 0) await new Promise(r => setTimeout(r, waitMs));
}
return false;
}
}
// Allow 20 solves per 5 minutes
const limiter = new SlidingWindowLimiter(20, 5 * 60 * 1000);
async function solveRateLimited(sitekey, pageurl) {
const acquired = await limiter.acquire(60000);
if (!acquired) throw new Error('Rate limit exceeded');
const submit = await axios.get('https://ocr.captchaai.com/in.php', {
params: {
key: API_KEY, method: 'userrecaptcha',
googlekey: sitekey, pageurl, json: '1',
},
});
if (submit.data.status !== 1) throw new Error(submit.data.request);
await new Promise(r => setTimeout(r, 15000));
for (let i = 0; i < 25; i++) {
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
});
if (poll.data.status === 1) return poll.data.request;
if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
await new Promise(r => setTimeout(r, 5000));
}
throw new Error('Timeout');
}
هذا النمط مناسب لمطابقة عتبة موقع مستهدف يُصرّح بعدد محدّد من الطلبات لكل فترة، لأنه يعكس الحدّ نفسه الذي يفرضه الطرف الآخر.
النمط الثالث: المُحدِّد المعتمد على الميزانية
بدلاً من التحكم في الوتيرة الزمنية، يتحكّم هذا النمط في المال: تُحدِّد ميزانية يومية وتتوقّف عند بلوغها. ولأن خطط CaptchaAI مبنية على الخيوط لا على رسم لكل عملية، فإن قيمة cost_per_solve هنا مجرد تقدير داخلي تستخدمه لتحويل استهلاكك إلى تكلفة تقريبية لأغراض المحاسبة والتنبيه — اضبطه بما يناسب خطتك وحجم تشغيلك.
# budget_limiter.py
import os
import time
from datetime import date
class BudgetLimiter:
"""Limit daily CAPTCHA spending."""
def __init__(self, daily_budget, cost_per_solve=0.003):
self.daily_budget = daily_budget
self.cost_per_solve = cost_per_solve
self.daily_spend = 0.0
self.current_date = date.today()
def can_solve(self):
"""Check if budget allows another solve."""
if date.today() != self.current_date:
self.daily_spend = 0.0
self.current_date = date.today()
return self.daily_spend + self.cost_per_solve <= self.daily_budget
def record_solve(self):
"""Record a successful solve against the budget."""
self.daily_spend += self.cost_per_solve
@property
def remaining_budget(self):
return max(0, self.daily_budget - self.daily_spend)
@property
def remaining_solves(self):
return int(self.remaining_budget / self.cost_per_solve)
# $5/day budget
budget = BudgetLimiter(daily_budget=5.00, cost_per_solve=0.003)
def solve_with_budget(sitekey, pageurl):
if not budget.can_solve():
raise Exception(
f"Daily budget exhausted. Remaining: ${budget.remaining_budget:.2f}"
)
# ... solve logic ...
token = "..." # actual solve
budget.record_solve()
return token
في أنظمة الإنتاج، ادمج هذا المُحدِّد مع دلو الرموز: يضبط الأول السقف المالي اليومي، ويضبط الثاني الوتيرة اللحظية، فتحصل على حماية مزدوجة ضد الاستنزاف المفاجئ وتجاوز الميزانية معاً.
كيف تختار نمط تحديد المعدل المناسب
لا يوجد نمط واحد يصلح لكل الحالات؛ اختر بناءً على ما تريد ضبطه أولاً — الوتيرة، أم العدد، أم التكلفة:
| النمط | الأنسب لـ | التعقيد |
|---|---|---|
| دلو الرموز | تحكّم سلس في المعدل مع السماح بالدفعات | متوسط |
| النافذة المنزلقة | عدّ طلبات بسيط لكل نافذة زمنية | منخفض |
| مُحدِّد الميزانية | ضبط التكلفة يومياً أو أسبوعياً أو شهرياً | منخفض |
| مدمج (المعدل + الميزانية) | أنظمة الإنتاج | متوسط |
القاعدة العملية: ابدأ بالنافذة المنزلقة إن كان لديك عتبة موقع واضحة، وانتقل إلى دلو الرموز حين تحتاج مرونة الدفعات، وأضِف مُحدِّد الميزانية دائماً كخط دفاع أخير على المستوى المالي.
حلّ مشكلات تحديد المعدل الشائعة
| المشكلة | السبب | الإجراء |
|---|---|---|
| كل الطلبات في قائمة الانتظار ولا يُنفَّذ أيٌّ منها | المعدل المضبوط أقل من الطلب الفعلي | ارفع قيمة rate أو حجم النافذة الزمنية |
| مُحدِّد الميزانية يُصفّر عدّاده في منتصف اليوم | تغيّر ساعة النظام أو إعادة تشغيل التطبيق | احفظ الإنفاق اليومي في ملف أو قاعدة بيانات بدل الذاكرة |
| دلو الرموز يُستهلك دفعةً واحدة | قيمة capacity أصغر من حاجة سير العمل |
زِد معامل السعة ليستوعب الذروة المتوقّعة |
| المُحدِّد يعترض طلبات الاستطلاع أيضاً | تطبيق الحدّ على res.php بالخطأ |
قيّد طلبات الإرسال فقط (in.php) واترك الاستطلاع حرّاً |
أسئلة شائعة
هل يُبطئ تحديد المعدل عملية الحل أو يقلّل من معدّل النجاح؟
لا. تحديد المعدل يضبط متى يُرسَل الطلب، لا كيف يُحلّ. بمجرد وصول الطلب إلى الخدمة، يمرّ بالمسار نفسه بلا فرق في وقت الحل أو معدّل النجاح؛ كل ما يفعله المُحدِّد هو تأخير الإرسال عند تجاوز العتبة التي حدّدتها أنت.
ما الفرق بين حدود الخيوط في CaptchaAI وتحديدي للمعدل بنفسي؟
الخيوط (Threads) هي سعة التزامن التي تشتريها ضمن الخطة، وتحكم كم طلباً يمكن أن يكون قيد التنفيذ في وقت واحد. أمّا تحديد المعدل فيعمل داخل تطبيقك ويحكم وتيرة إرسالك للطلبات بصرف النظر عن سعة الخطة. الأول سقف تفرضه الخدمة، والثاني سقف تفرضه أنت لأسبابك الخاصة.
كيف أحافظ على تتبّع الميزانية بعد إعادة تشغيل الخدمة؟
المثال في هذا الدليل يحفظ الإنفاق في الذاكرة، فيضيع عند إعادة التشغيل. للإنتاج، خزّن قيمة daily_spend وتاريخها في Redis أو قاعدة بيانات، واقرأها عند الإقلاع؛ بهذا يبقى السقف اليومي سارياً حتى لو أُعيد تشغيل التطبيق عدة مرات في اليوم.
هل أطبّق الحدّ على طلبات الإرسال فقط أم على الاستطلاع أيضاً؟
على طلبات الإرسال (in.php) فقط، لأنها هي التي تُنشئ مهام حلّ جديدة وتستهلك من رصيدك. أمّا الاستطلاع (res.php) فمجرد استعلام عن نتيجة مهمة قائمة، ولا ينبغي تقييده كي لا تتأخّر عودة الرموز الجاهزة.
الخطوات التالية
- ابدأ سريعاً: حلّ أول اختبار CAPTCHA خلال 5 دقائق
- حلّ reCAPTCHA v2 عبر الـ API خطوة بخطوة
- حلّ Cloudflare Turnstile عبر الـ API
- حلّ GeeTest v3 عبر الـ API