اختيارك بين WebDriver وCDP لا يغيّر معدل حل CAPTCHA، فالحل يجري على خوادم CaptchaAI بعيداً عن المتصفح. ما يتغيّر شيئان: كم مرة يظهر التحدي أمام سكربتك، وكم سطراً تكتبه لاعتراض الطلبات وحقن الرمز.
- WebDriver — معيار W3C يغطي Chrome وFirefox وSafari وEdge، لكنه يمرّ عبر ChromeDriver ويترك أثراً في الصفحة.
- CDP — اتصال WebSocket مباشر مع Chrome، يمنحك اعتراض الشبكة وتعديل الاستجابة، ولا يعمل خارج Chromium.
- CaptchaAI — مستقل عن الاثنين: نفس
in.phpوres.phpونفس وقت الحل.
بنية البروتوكولين
WebDriver (Selenium):
Script ──HTTP──▶ ChromeDriver ──DevTools──▶ Chrome
(JSON Wire) (bridge) (internal)
CDP (Puppeteer/Playwright):
Script ──WebSocket──▶ Chrome
(direct) (no middle layer)
كل أمر في WebDriver يقطع رحلة HTTP إلى ChromeDriver ثم يُترجم إلى أوامر DevTools، بينما يتحدث CDP إلى Chrome عبر اتصال WebSocket واحد يبقى مفتوحاً طوال الجلسة.
الفروق العملية بين WebDriver وCDP
| العنصر | WebDriver | CDP |
|---|---|---|
| البروتوكول | HTTP + JSON | WebSocket |
| سطح الكشف | مرتفع — navigator.webdriver = true |
أقل وقابل للتعديل |
| اعتراض الشبكة | يحتاج selenium-wire | مدمج عبر Fetch وNetwork |
| تعديل الطلب والاستجابة | غير مدعوم | مدعوم |
| دعم المتصفحات | Chrome وFirefox وSafari وEdge | Chrome/Chromium فقط |
ما تراه المواقع من كل بروتوكول
بصمة Selenium
المواقع تبحث عن متغيّرات محدّدة يتركها المتصفح المُدار:
// What sites check for Selenium
navigator.webdriver // true (set by ChromeDriver)
document.$cdc_asdjflasutopfhvcZLmcfl // ChromeDriver internal variable
window.callSelenium // Older Selenium versions
window._selenium // Selenium internals
document.__webdriver_evaluate // WebDriver namespace
وحتى بعد إخفاء العلامة، تبقى متغيّرات $cdc_ التي يحقنها ChromeDriver:
# Even with flag removal, ChromeDriver leaves traces
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
driver = webdriver.Chrome(options=options)
# Still detectable via $cdc variables in document
بصمة Puppeteer عبر CDP
هنا تُضبط الخاصية قبل تحميل الصفحة، ولا وجود لمتغيّرات ChromeDriver أصلاً:
// Puppeteer sets webdriver flag too, but it's fully patchable
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
});
// No $cdc variables to worry about
// No Selenium-specific traces
});
النتيجة سطح كشف أقل وتحديات أقل على الجلسة نفسها: بصمة أتمتة أنظف، لا وعد بتفادي الفحص.
حل reCAPTCHA v2 عبر CaptchaAI في Selenium
المسار واحد في أي مكتبة: استخرج sitekey، أرسل المهمة إلى in.php، استطلع res.php حتى تصل النتيجة، ثم احقن الرمز في g-recaptcha-response وأرسل النموذج.
# Selenium (WebDriver) + CaptchaAI
import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
API_KEY = "YOUR_API_KEY"
API_URL = "https://ocr.captchaai.com"
options = webdriver.ChromeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com/form")
time.sleep(2)
# Extract sitekey
sitekey = driver.execute_script(
"return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
)
# Solve via CaptchaAI
resp = requests.post(f"{API_URL}/in.php", data={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": sitekey, "pageurl": driver.current_url, "json": 1,
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(5)
resp = requests.get(f"{API_URL}/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1,
})
data = resp.json()
if data["request"] != "CAPCHA_NOT_READY":
break
token = data["request"]
# Inject token
driver.execute_script(f"""
document.querySelector('#g-recaptcha-response').value = '{token}';
document.querySelectorAll('[name="g-recaptcha-response"]')
.forEach(el => {{ el.value = '{token}'; }});
""")
driver.find_element(By.CSS_SELECTOR, "form").submit()
driver.quit()
كل استدعاء لـ execute_script يمرّ عبر الجسر ويضيف تأخيراً، فاجمع الحقن في استدعاء واحد.
المسار نفسه في Puppeteer
// Puppeteer (CDP) + CaptchaAI
const puppeteer = require("puppeteer-extra");
const StealthPlugin = require("puppeteer-extra-plugin-stealth");
const https = require("https");
puppeteer.use(StealthPlugin());
const API_KEY = "YOUR_API_KEY";
const API_URL = "https://ocr.captchaai.com";
function solveCaptcha(siteUrl, sitekey) {
// Submit + poll (simplified)
return new Promise(async (resolve, reject) => {
const submitResp = await httpPost(`${API_URL}/in.php`, {
key: API_KEY, method: "userrecaptcha",
googlekey: sitekey, pageurl: siteUrl, json: "1",
});
const taskId = submitResp.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await httpGet(
`${API_URL}/res.php?key=${API_KEY}&action=get&id=${taskId}&json=1`
);
if (result.request !== "CAPCHA_NOT_READY") {
return resolve(result.request);
}
}
reject(new Error("Timeout"));
});
}
(async () => {
const browser = await puppeteer.launch({
headless: "new",
args: ["--no-sandbox", "--window-size=1920,1080"],
});
const page = await browser.newPage();
// CDP: Enable network interception
const cdp = await page.createCDPSession();
await cdp.send("Network.enable");
// Monitor CAPTCHA-related requests
cdp.on("Network.requestWillBeSent", (params) => {
if (params.request.url.includes("recaptcha")) {
console.log(`CAPTCHA request: ${params.request.url}`);
}
});
await page.goto("https://example.com/form", { waitUntil: "networkidle0" });
const sitekey = await page.evaluate(() =>
document.querySelector("[data-sitekey]")?.getAttribute("data-sitekey")
);
if (sitekey) {
const token = await solveCaptcha(page.url(), sitekey);
// CDP: Inject via Runtime.evaluate (lower-level than page.evaluate)
await cdp.send("Runtime.evaluate", {
expression: `
document.querySelector('#g-recaptcha-response').value = '${token}';
document.querySelector('form').submit();
`,
});
}
await browser.close();
})();
استدعاء CaptchaAI متطابق؛ الفارق أنك تفتح جلسة CDP، وتراقب طلبات التحقق، وتحقن الرمز عبر Runtime.evaluate.
اعتراض الطلبات وتعديل الاستجابة والكوكيز
لا يملك WebDriver اعتراضاً أصلياً، بل يحتاج selenium-wire الذي يمرّر الحركة عبر خادم وسيط محلي:
# WebDriver: Cannot intercept requests natively
# Requires selenium-wire (third-party)
from seleniumwire import webdriver
driver = webdriver.Chrome()
# selenium-wire proxies all traffic through a local proxy
أما في CDP فالاعتراض جزء من البروتوكول:
// CDP: Built-in request interception
await cdp.send("Fetch.enable", {
patterns: [{ urlPattern: "*recaptcha*" }],
});
cdp.on("Fetch.requestPaused", async ({ requestId, request }) => {
console.log(`Intercepted: ${request.url}`);
await cdp.send("Fetch.continueRequest", { requestId });
});
ويمكنك إعادة استجابة معدّلة في بيئات الاختبار التي تملكها:
// CDP: Can modify responses (WebDriver cannot)
cdp.on("Fetch.requestPaused", async ({ requestId, request }) => {
if (request.url.includes("captcha-config")) {
// Return modified response
await cdp.send("Fetch.fulfillRequest", {
requestId,
responseCode: 200,
body: btoa(JSON.stringify({ enabled: false })),
});
} else {
await cdp.send("Fetch.continueRequest", { requestId });
}
});
وفي الكوكيز يقرأ CDP الكوكيز المعلّمة بـ httpOnly ويكتبها، بخلاف واجهة WebDriver القياسية التي لا تصل إليها:
# WebDriver: Standard cookie API
driver.add_cookie({"name": "session", "value": "abc123"})
cookies = driver.get_cookies()
// CDP: Full cookie control including httpOnly
await cdp.send("Network.setCookie", {
name: "session",
value: "abc123",
domain: "example.com",
httpOnly: true,
secure: true,
});
// Get all cookies including httpOnly (WebDriver can't access these)
const { cookies } = await cdp.send("Network.getAllCookies");
الجمع بين WebDriver وCDP في Selenium 4
لست مضطراً إلى الاختيار؛ يمرّر Selenium 4 أوامر CDP مباشرة:
from selenium import webdriver
driver = webdriver.Chrome()
# Use WebDriver for navigation
driver.get("https://example.com")
# Use CDP for stealth patches
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
"""
})
# Use CDP for network events
driver.execute_cdp_cmd("Network.enable", {})
أرقام الأداء
| المقياس | WebDriver — Selenium | CDP — Puppeteer | CDP — Playwright |
|---|---|---|---|
| بدء الجلسة | ~2.5 ثانية | ~1.5 ثانية | ~1.2 ثانية |
| زمن التنقل الإضافي — مللي ثانية | ~500 | ~100 | ~80 |
| تنفيذ سكربت — مللي ثانية | ~200 | ~50 | ~40 |
| الذاكرة لكل جلسة — ميجابايت | ~350 | ~280 | ~260 |
وقت الحل واحد في البروتوكولين لأنه استدعاء API مستقل عن المتصفح: أقل من 60 ثانية لـ reCAPTCHA v2 وأقل من 10 ثوانٍ لـ Cloudflare Turnstile.
متى تختار كل بروتوكول
| الحالة | الأنسب | السبب |
|---|---|---|
| اختبار عبر عدة متصفحات | WebDriver | تغطية Firefox وSafari وEdge |
| تقليل ظهور التحديات | CDP | بصمة أتمتة أنظف |
| فريق غير معتاد على CDP | WebDriver | واجهة أبسط وتوثيق أوسع |
| صفحات كثيفة التحقق | CDP + CaptchaAI | بصمة أنظف وحل مؤتمت |
| مسارات CI/CD قائمة | WebDriver | تكامل أنضج |
سيناريو عملي: منصة حجوزات في الرياض
فريق يشغّل منصة حجوزات يحتاج إلى مسارين. اختبارات القبول تعمل على Chrome وSafari معاً لأن نصف الزوار يدخلون من iPhone، وهنا يبقى Selenium الخيار الوحيد. ومراقبة مسار إتمام الشراء تعمل على Chrome وحده كل خمس دقائق مع قراءة طلبات الشبكة، وهنا يتفوق CDP.
المساران يعملان على مفتاح CaptchaAI واحد، والفوترة بعدد الـ threads المتزامنة لا بعدد عمليات الحل: خطة BASIC بسعر 15 دولاراً شهرياً تمنح 5 threads بحلول غير محدودة وتكفي جدول المراقبة، بينما تحتاج الاختبارات الليلية المتوازية إلى خطة ADVANCE بسعر 90 دولاراً شهرياً مع 50 thread.
أعطال شائعة وعلاجها
| العطل | في WebDriver | في CDP |
|---|---|---|
| الجلسة تُصنَّف كأتمتة | excludeSwitches وضبط الخيارات |
إضافة stealth مع تعديل الخصائص |
| تعذّر اعتراض الطلبات | استخدم selenium-wire | فعّل Fetch.enable |
| بطء التنفيذ | قلّل استدعاءات execute_script |
اجمع أوامر CDP في دفعة |
| فشل حقن الرمز | تحقق من سياق الـ iframe | Runtime.evaluate مع تحديد السياق |
الأسئلة الشائعة
هل يغني CDP عن الخادم الوسيط؟
لا. البروتوكول لا يغيّر عنوان IP الذي يخرج منه الطلب، والجلسات المكثفة من عنوان واحد تزيد ظهور التحديات. وزّع الخروج وقلّل التزامن أولاً.
لماذا يفشل حقن الرمز رغم وصول الحل؟
السبب الأشهر هو سياق التنفيذ: الحقل داخل iframe والسكربت يعمل في الصفحة الأم. اضبط g-recaptcha-response في السياق الصحيح، ثم أطلق حدث التغيير قبل الإرسال.
كم thread أحتاج عند تشغيل متصفحات متوازية؟
العدد يقابل عمليات الحل الجارية في اللحظة نفسها لا عدد المتصفحات. عشر جلسات تصل إلى التحقق تباعاً قد تكتفي بـ 5 threads، ودفعة تنطلق معاً تحتاج thread لكل جلسة.
هل يدعم CaptchaAI أنواعاً أخرى في المسار نفسه؟
reCAPTCHA v2 وv3 وEnterprise وCloudflare Turnstile مدعومة. أما hCaptcha وFunCaptcha وGeeTest v4 فليست مدعومة، وCaptchaFox وFriendly Captcha وLemin في مرحلة beta فقط.
أدلة ذات صلة
اختر البروتوكول الذي يناسب فريقك ودع الحل خارج المتصفح — احصل على مفتاح CaptchaAI وشغّل المسار نفسه في المكتبتين.