إذا نجح سكربت الحل نفسه على موقع ورُفض على آخر رغم أن كليهما يشغّل reCAPTCHA v3، فالسبب غالباً معلمة واحدة: الموقع الثاني يستخدم نسخة Enterprise وطلبك لا يحمل enterprise=1. الإصداران متطابقان أمام الزائر ويعيدان درجة مخاطرة بين 0.0 و1.0، لكن Enterprise يضيف عتبات لكل إجراء، ورموز أسباب، وإدارة على مستوى المشروع.
وما يهمّ المطوّر هنا ثلاث نقاط:
- كيف تميّز الإصدارين من كود الصفحة قبل إرسال أي طلب.
- ما الذي تضيفه Enterprise فعلياً، وما الذي يبقى كما هو.
- كيف تبني مساراً واحداً يتعرّف على الإصدار ويتصرّف تلقائياً.
كيف تعرف أي إصدار يشغّله الموقع
الفارق ظاهر في وسم البرنامج النصي وحده: الإصدار القياسي يحمّل api.js?render=KEY ويستدعي grecaptcha.execute()، بينما تحمّل Enterprise ملف enterprise.js?render=KEY وتستدعي grecaptcha.enterprise.execute(). لا فرق بصري بينهما إطلاقاً. الدالة التالية تقرأ HTML مرة واحدة وتعيد الإصدار ومفتاح الموقع وقيمة action:
import requests
import re
def detect_v3_version(url):
html = requests.get(url).text
if "enterprise.js" in html:
version = "enterprise"
elif "recaptcha/api.js" in html and "render=" in html:
version = "standard"
else:
return None
# Extract sitekey
key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
sitekey = key_match.group(1) if key_match else None
# Extract action
action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
action = action_match.group(1) if action_match else None
return {"version": version, "sitekey": sitekey, "action": action}
ونفس المنطق في Node.js عبر Axios:
const axios = require("axios");
async function detectV3Version(url) {
const { data: html } = await axios.get(url);
const version = html.includes("enterprise.js")
? "enterprise"
: html.includes("recaptcha/api.js") && html.includes("render=")
? "standard"
: null;
const keyMatch = html.match(/render[=:]\s*['"]?([A-Za-z0-9_-]{40})/);
const actionMatch = html.match(/action['"]?\s*[:=]\s*['"](\w+)/);
return {
version,
sitekey: keyMatch?.[1],
action: actionMatch?.[1],
};
}
وانتبه: مفتاح موقع v3 يأتي من معلمة render لا من سمة data-sitekey كما في v2 — وهذا أشهر أسباب ERROR_WRONG_GOOGLEKEY.
ما الذي تضيفه Enterprise فوق الإصدار القياسي
معظم إضافات Enterprise موجّهة إلى مالك الموقع لا إليك، لكن العتبات المختلفة لكل إجراء تنعكس مباشرة على تكاملك:
| العنصر | v3 القياسي | v3 Enterprise |
|---|---|---|
| تشغيل غير مرئي | نعم | نعم |
| درجة المخاطرة 0.0–1.0 | نعم | نعم |
معلمة action |
مطلوبة | مطلوبة |
| رموز الأسباب | لا | نعم |
| عتبات مخصصة لكل إجراء | لا | نعم — عبر Cloud Console |
| كشف تسرّب كلمات المرور | لا | نعم |
| Account Defender | لا | نعم |
| وسوم منع الاحتيال | لا | نعم |
| تكامل المصادقة متعددة العوامل | لا | نعم |
| نقطة نهاية التحقق | siteverify — مجانية |
recaptchaenterprise.googleapis.com |
| الحصة الشهرية | مليون تقييم مجاناً | دفع لكل تقييم |
| ملف JavaScript | api.js?render=KEY |
enterprise.js?render=KEY |
| معلمات CaptchaAI | version=v3 |
version=v3 + enterprise=1 |
كيف يتغيّر احتساب الدرجة
يعتمد الإصدار القياسي على نموذج تسجيل عالمي واحد، أما Enterprise فيبني فوقه طبقات إضافية:
| جانب الاحتساب | القياسي | Enterprise |
|---|---|---|
| نموذج الدرجة الأساسي | نموذج Google العام | نموذج Google العام + إشارات مخصصة |
| العتبات | عتبة واحدة لكل الإجراءات | عتبة لكل إجراء — تسجيل الدخول 0.7، إتمام الشراء 0.9 |
| التعلّم التكيفي | أساسي | يتعلّم من أنماط حركة الموقع نفسه |
| دقة الدرجة | 0.0–1.0 برقمين عشريين | النطاق نفسه بإشارات أدق |
| معالجة الإنذارات الكاذبة | ضبط يدوي للعتبة | تحليل آلي عبر رموز الأسباب |
الخلاصة: إرسال قيمة action الصحيحة يصبح أهم مع Enterprise، لأن كل إجراء قد يُقاس بعتبة مختلفة.
معلمة واحدة تفصل بين المسارين
الفرق على جانب CaptchaAI ليس نقطة نهاية مختلفة ولا استجابة مختلفة، بل معلمة واحدة في طلب in.php.
المسار القياسي
import requests
import time
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"version": "v3",
"googlekey": sitekey,
"action": "login",
"pageurl": page_url
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
مسار Enterprise
import requests
import time
# Only difference: enterprise=1
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"version": "v3",
"enterprise": 1,
"googlekey": sitekey,
"action": "login",
"pageurl": page_url
})
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
حلّ يتعرّف على الإصدار تلقائياً
بدل تثبيت الإصدار في الكود، اجعل الكشف جزءاً من دورة الحل — الصنف التالي يستنتج الإصدار والمفتاح والإجراء ثم يستطلع النتيجة حتى يعود الرمز:
class V3AutoSolver:
def __init__(self, api_key):
self.api_key = api_key
def solve(self, page_url, action=None):
import re
html = requests.get(page_url).text
is_enterprise = "enterprise.js" in html
key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
if not key_match:
raise Exception("No v3 sitekey found")
if not action:
act_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
action = act_match.group(1) if act_match else "verify"
params = {
"key": self.api_key,
"method": "userrecaptcha",
"version": "v3",
"googlekey": key_match.group(1),
"action": action,
"pageurl": page_url
}
if is_enterprise:
params["enterprise"] = 1
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key, "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
return result.text.split("|")[1]
if result.text != "CAPCHA_NOT_READY":
raise Exception(f"Solve error: {result.text}")
raise Exception("Timed out")
رموز الأسباب: ما يراه مالك الموقع ولا تراه أنت
تعيد Enterprise رموز أسباب تشرح سبب الدرجة. لا تصلك عند الحل عبر CaptchaAI، لكن معرفتها تختصر تشخيص الرفض المتكرر:
| الرمز | الدلالة | الأثر على الأتمتة |
|---|---|---|
AUTOMATION |
رُصد سلوك آلي | استخدم بصمة متصفح واقعية |
UNEXPECTED_ENVIRONMENT |
بيئة متصفح غير معتادة | راجع كشف المتصفح بدون واجهة رسومية |
TOO_MUCH_TRAFFIC |
حجم طلبات مرتفع من المصدر نفسه | حدّد معدل الطلبات ودوّر الخادم الوسيط |
UNEXPECTED_USAGE_PATTERNS |
أنماط تفاعل غير طبيعية | أضف تأخيرات واقعية بين الإجراءات |
LOW_CONFIDENCE_SCORE |
بيانات غير كافية لدرجة موثوقة | أرسل إشارات سلوكية أكثر |
SUSPECTED_CARDING |
أنماط احتيال ببطاقات الدفع | لا تخصّ معظم حالات الأتمتة |
SUSPECTED_CHARGEBACK |
أنماط احتيال بردّ المبالغ | لا تخصّ معظم حالات الأتمتة |
ملاحظة: تُعاد هذه الرموز إلى الواجهة الخلفية للموقع أثناء التحقق، لا إلى أدوات الحل. اقرأها كخريطة لأسباب الرفض المحتملة.
سيناريو من السوق العربي: متجر إقليمي ينتقل إلى Enterprise
تخيّل متجراً إلكترونياً يخدم السعودية والإمارات ومصر بواجهة عربية واحدة. قبل ذروة رمضان يرفع فريق الأمن حماية صفحتي تسجيل الدخول وإتمام الشراء إلى Enterprise، ويضبط عتبة أعلى للدفع منها للدخول. النتيجة على فريق اختبارات الجودة: السكربت المستقر بدأ يفشل عند الدفع وحده، بينما بقي تسجيل الدخول يعمل.
والتشخيص هنا يبدأ من الصفحة لا من الخدمة:
- تأكد من وجود
enterprise.jsفي وسوم الصفحة الجديدة. - اقرأ قيمة
actionلكل مسار على حدة —loginوcheckoutعتبتان مختلفتان. - أعد الإرسال مع
enterprise=1وقيمةactionالمطابقة للمسار.
ولا يغيّر هذا الانتقال فاتورة CaptchaAI: التسعير قائم على عدد الـ Threads المتزامنة لا على نوع الـ CAPTCHA.
أخطاء شائعة أثناء التكامل
| العَرَض | السبب الأرجح | المعالجة |
|---|---|---|
| رفض الرمز على موقع Enterprise | enterprise=1 غير مُرسلة |
ابحث عن enterprise.js وأضف المعلمة |
| درجة منخفضة على Enterprise | قيمة action خاطئة |
استخرجها من استدعاء grecaptcha.enterprise.execute() |
| ينجح في الاختبار ويفشل في الإنتاج | التعلّم التكيفي رصد نمط طلباتك | دوّر عناوين IP، وأضف تأخيرات، ونوّع الأنماط |
ERROR_WRONG_GOOGLEKEY |
المفتاح مأخوذ من data-sitekey بدل render |
ابحث عن render=KEY في رابط البرنامج النصي |
أسئلة شائعة
متى أرسل enterprise=1 ومتى أتركها؟
تعامل معها كوصف لحالة الموقع لا كخيار جودة: أرسلها فقط عندما يؤكد الكشف وجود enterprise.js أو استدعاء grecaptcha.enterprise.execute(). إرسالها عشوائياً يجعل تشخيص أي رفض لاحق أصعب لأنك غيّرت متغيرين معاً؛ اترك القرار للكشف التلقائي.
هل تختلف تكلفة الحل بين v3 القياسي وEnterprise على CaptchaAI؟
لا. التسعير قائم على عدد الـ Threads المتزامنة، لا على عدد عمليات الحل أو نوعها. تبدأ الخطط من BASIC بسعر 15 دولاراً شهرياً مع 5 Threads وتصل إلى VIP-3 بسعر 7,500 دولار مع 5,000 Thread، وراجع صفحة الأسعار الرسمية قبل الاعتماد.
كيف أستخرج قيمة action إذا كان JavaScript مُصغّراً؟
ابحث عن الاستدعاء نفسه لا عن اسم المتغير: execute( متبوعاً بكائن يحتوي المفتاح action. إن لم تجده في الملف المُصغّر، راقب طلبات الشبكة أثناء تنفيذ الإجراء يدوياً واقرأ القيمة من حمولة الطلب. واستخدام قيمة عامة مثل verify احتياط أخير.
هل تعني الدرجة المرتفعة قبول الطلب دائماً؟
لا. الدرجة إشارة واحدة يقرأها خادم الموقع عبر siteverify أو واجهة Enterprise، ثم يقرر وفق العتبة المضبوطة لهذا الإجراء تحديداً، وقد يضيف فحوصاً أخرى مثل سمعة عنوان IP. اختبر على المسار الحقيقي بدل الاكتفاء برقم الدرجة.