الدروس التطبيقية

حل CAPTCHA داخل Selenium وPython باستخدام CaptchaAI

عندما يصل سكربت الأتمتة إلى صفحة تسجيل دخول، غالباً ما يتوقّف عند اختبار CAPTCHA. الحل مباشر وفي أربع خطوات: يدير Selenium المتصفح ويستخرج مفتاح الموقع، ويتولّى CaptchaAI حل الاختبار وإعادة التوكن، ثم تحقن التوكن في الصفحة وترسل النموذج. الدورة نفسها — اكتشاف، حل، حقن، إرسال — تعمل مع reCAPTCHA v2 وv3 وCloudflare Turnstile وCAPTCHA الصور. في هذا الشرح نبني الدالة المساعدة مرة واحدة، ثم ننتهي بفئة Python تكتشف النوع وتحلّه وترسل النموذج تلقائياً.

مثال من المنطقة العربية: فريق يختبر آلياً تدفّق التسجيل في منتجه المحمي بـ Cloudflare Turnstile ضمن اختبارات الجودة (QA). يكفيه worker واحد على خطة BASIC ($15 شهرياً، 5 threads)، والفوترة على الـ threads المتزامنة مع حلول غير محدودة لكل thread، لا لكل عملية حل.


التثبيت والمتطلبات

ثبّت المكتبات الثلاث التي يعتمد عليها المثال: Selenium لقيادة المتصفح، وrequests للاتصال بـ API، وwebdriver-manager لتنزيل درايفر Chrome المطابق تلقائياً.

pip install selenium requests webdriver-manager

تهيئة متصفح Selenium لتشغيل مستقر

بعض المواقع تفحص علامة navigator.webdriver وتغيّر سلوكها بناءً عليها. الإعدادات التالية تُهيّئ Chrome بحيث لا يُعلن عن نفسه كمتصفح مُؤتمت، وتزيل هذه العلامة عبر أمر CDP قبل تحميل أي صفحة، ما يجعل جلسات الاختبار أكثر استقراراً.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

def create_driver():
    """Create a Selenium driver with stealth-configuredion settings."""
    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(
        service=Service(ChromeDriverManager().install()),
        options=options,
    )

    # Remove webdriver flag
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
    })

    return driver

دالة الحل عبر CaptchaAI

هذه الدالة هي القلب المشترك لكل الأنواع: ترسل المهمة إلى in.php، تحتفظ بمعرّفها، ثم تستطلع res.php كل 5 ثوانٍ حتى يجهز التوكن أو تنتهي المهلة. يتغيّر method والمعاملات فقط بين نوع وآخر.

import time
import requests

API_KEY = "YOUR_API_KEY"

def solve_captcha(method, **params):
    """Generic CaptchaAI solver — works for all supported CAPTCHA types."""
    submit_data = {
        "key": API_KEY,
        "method": method,
        "json": 1,
        **params,
    }

    submit = requests.post("https://ocr.captchaai.com/in.php", data=submit_data, timeout=30)
    data = submit.json()

    if data.get("status") != 1:
        raise Exception(f"Submit error: {data.get('request')}")

    task_id = data["request"]

    for _ in range(30):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=30).json()

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") == "ERROR_CAPTCHA_UNSOLVABLE":
            raise Exception("CAPTCHA unsolvable")

    raise TimeoutError("Solve timed out")

حل reCAPTCHA v2 داخل Selenium

الدورة الكاملة: اكتشاف، حل، حقن التوكن، إرسال

الخطوات أربع: افتح الصفحة، استخرج مفتاح الموقع عبر تعبير نمطي، مرّره إلى CaptchaAI بالطريقة userrecaptcha، ثم احقن التوكن في الحقل g-recaptcha-response. وإن اعتمد النموذج على دالة callback، استدعها صراحةً قبل الإرسال.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re

def solve_recaptcha_v2_selenium(driver, url):
    """Complete reCAPTCHA v2 solve in Selenium."""
    driver.get(url)

    # Wait for page to load
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.TAG_NAME, "body"))
    )

    # Extract sitekey
    page_source = driver.page_source
    match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]{40})["\']', page_source)
    if not match:
        raise ValueError("reCAPTCHA sitekey not found")
    sitekey = match.group(1)
    print(f"Sitekey: {sitekey}")

    # Solve via CaptchaAI
    token = solve_captcha(
        "userrecaptcha",
        googlekey=sitekey,
        pageurl=url,
    )
    print(f"Token received: {token[:50]}...")

    # Inject token into the page
    driver.execute_script(f"""
        document.getElementById('g-recaptcha-response').value = '{token}';
        document.getElementById('g-recaptcha-response').style.display = 'block';
    """)

    # If there's a callback function, call it
    driver.execute_script(f"""
        if (typeof ___grecaptcha_cfg !== 'undefined') {{
            var clients = ___grecaptcha_cfg.clients;
            for (var key in clients) {{
                var client = clients[key];
                if (client.rr && client.rr.l) {{
                    client.rr.l.callback('{token}');
                }}
            }}
        }}
    """)

    # Submit the form
    submit_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], input[type='submit']")
    submit_button.click()

    return token

# Usage
driver = create_driver()
try:
    solve_recaptcha_v2_selenium(driver, "https://example.com/login")
    print(f"Current URL: {driver.current_url}")
finally:
    driver.quit()

حل Cloudflare Turnstile داخل Selenium

يتبع Turnstile النمط نفسه مع اختلافين: مفتاح الموقع يبدأ عادةً بالمقطع 0x، والتوكن يُكتب في الحقل المخفي cf-turnstile-response. وإن غاب عن السمة data-sitekey، استخرجه من استدعاء JavaScript.

def solve_turnstile_selenium(driver, url):
    """Complete Turnstile solve in Selenium."""
    driver.get(url)

    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.TAG_NAME, "body"))
    )

    # Extract sitekey
    page_source = driver.page_source
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', page_source)
    if not match:
        # Try JavaScript API pattern
        match = re.search(r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", page_source)
    if not match:
        raise ValueError("Turnstile sitekey not found")

    sitekey = match.group(1)
    print(f"Turnstile sitekey: {sitekey}")

    # Solve via CaptchaAI
    token = solve_captcha(
        "turnstile",
        sitekey=sitekey,
        pageurl=url,
    )
    print(f"Turnstile token: {token[:50]}...")

    # Inject token
    driver.execute_script(f"""
        // Set the hidden input value
        var inputs = document.querySelectorAll('[name="cf-turnstile-response"]');
        inputs.forEach(function(input) {{ input.value = '{token}'; }});

        // Also try the callback approach
        if (typeof turnstile !== 'undefined' && turnstile.getResponse) {{
            // Widget already rendered
        }}
    """)

    # Submit form
    submit_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], input[type='submit']")
    submit_button.click()

    return token

حل CAPTCHA الصور داخل Selenium

لاختبارات الصور التقليدية (اكتب الحروف الظاهرة)، التقط لقطة للعنصر وحوّلها إلى base64، أرسلها بالطريقة base64، ثم اكتب الإجابة في حقل الإدخال.

import base64

def solve_image_captcha_selenium(driver, captcha_selector):
    """Solve image CAPTCHA visible in the browser."""
    # Find the CAPTCHA image element
    captcha_img = driver.find_element(By.CSS_SELECTOR, captcha_selector)

    # Get image as base64
    img_base64 = captcha_img.screenshot_as_base64

    # Solve via CaptchaAI
    answer = solve_captcha("base64", body=img_base64)
    print(f"CAPTCHA answer: {answer}")

    # Type the answer into the input field
    captcha_input = driver.find_element(
        By.CSS_SELECTOR, "input[name='captcha'], input[name='code'], input.captcha-input"
    )
    captcha_input.clear()
    captcha_input.send_keys(answer)

    return answer

حل reCAPTCHA v3 داخل Selenium

لا يعرض reCAPTCHA v3 مربع اختيار؛ يعمل في الخلفية ويعيد درجة ثقة مرتبطة بـ action محدد. مرّر مفتاح الموقع وقيمة الـ action إلى CaptchaAI، ثم احقن التوكن في الحقل g-recaptcha-response أو في أي حقل مخفي يحمل الاسم ذاته.

def solve_recaptcha_v3_selenium(driver, url, sitekey, action="verify"):
    """Solve reCAPTCHA v3 in Selenium."""
    driver.get(url)

    # Solve via CaptchaAI
    token = solve_captcha(
        "userrecaptcha",
        googlekey=sitekey,
        pageurl=url,
        version="v3",
        action=action,
    )

    # Inject token
    driver.execute_script(f"""
        // Set reCAPTCHA response
        var textarea = document.getElementById('g-recaptcha-response');
        if (textarea) {{
            textarea.value = '{token}';
            textarea.style.display = 'block';
        }}

        // Set any hidden input fields
        var inputs = document.querySelectorAll('input[name="g-recaptcha-response"]');
        inputs.forEach(function(input) {{ input.value = '{token}'; }});
    """)

    return token

فئة أتمتة متكاملة تكتشف النوع وتحلّه

تجمع فئة SeleniumCaptchaSolver كل ما سبق في مسار واحد: تفتح الصفحة، تكتشف نوع CAPTCHA، تستدعي دالة الحل المناسبة، تملأ الحقول، ثم ترسل النموذج — وهي ما ستستخدمه فعلياً في الإنتاج.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re
import time
import requests

API_KEY = "YOUR_API_KEY"

class SeleniumCaptchaSolver:
    """Complete Selenium + CaptchaAI automation class."""

    def __init__(self, api_key):
        self.api_key = api_key
        self.driver = None

    def start(self):
        """Initialize the browser."""
        options = webdriver.ChromeOptions()
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        self.driver = webdriver.Chrome(options=options)
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
        })

    def stop(self):
        """Close the browser."""
        if self.driver:
            self.driver.quit()

    def navigate(self, url):
        """Navigate to URL and wait for load."""
        self.driver.get(url)
        WebDriverWait(self.driver, 15).until(
            lambda d: d.execute_script("return document.readyState") == "complete"
        )

    def detect_captcha(self):
        """Detect which CAPTCHA type is on the page."""
        source = self.driver.page_source

        if re.search(r'data-sitekey=["\'][A-Za-z0-9_-]{40}["\']', source):
            if "recaptcha/api.js" in source or "grecaptcha" in source:
                return "recaptcha_v2"

        if "cf-turnstile" in source or "challenges.cloudflare.com/turnstile" in source:
            return "turnstile"

        if re.search(r'recaptcha.*v3|render=[A-Za-z0-9_-]{40}', source):
            return "recaptcha_v3"

        captcha_imgs = self.driver.find_elements(
            By.CSS_SELECTOR, "img.captcha, img[alt*='captcha'], img[src*='captcha']"
        )
        if captcha_imgs:
            return "image"

        return None

    def solve_and_submit(self, url, form_data=None):
        """Navigate, solve CAPTCHA, fill form, and submit."""
        self.navigate(url)
        captcha_type = self.detect_captcha()

        if not captcha_type:
            print("No CAPTCHA detected")
            return self._fill_and_submit(form_data)

        print(f"Detected: {captcha_type}")

        if captcha_type == "recaptcha_v2":
            self._solve_recaptcha_v2()
        elif captcha_type == "turnstile":
            self._solve_turnstile()
        elif captcha_type == "image":
            self._solve_image()

        return self._fill_and_submit(form_data)

    def _solve_recaptcha_v2(self):
        source = self.driver.page_source
        match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]{40})["\']', source)
        sitekey = match.group(1)

        token = self._api_solve("userrecaptcha", googlekey=sitekey, pageurl=self.driver.current_url)

        self.driver.execute_script(f"""
            document.getElementById('g-recaptcha-response').value = '{token}';
        """)

    def _solve_turnstile(self):
        source = self.driver.page_source
        match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', source)
        sitekey = match.group(1)

        token = self._api_solve("turnstile", sitekey=sitekey, pageurl=self.driver.current_url)

        self.driver.execute_script(f"""
            document.querySelectorAll('[name="cf-turnstile-response"]')
                .forEach(function(el) {{ el.value = '{token}'; }});
        """)

    def _solve_image(self):
        img = self.driver.find_element(
            By.CSS_SELECTOR, "img.captcha, img[alt*='captcha'], img[src*='captcha']"
        )
        answer = self._api_solve("base64", body=img.screenshot_as_base64)

        captcha_input = self.driver.find_element(
            By.CSS_SELECTOR, "input[name='captcha'], input[name='code']"
        )
        captcha_input.clear()
        captcha_input.send_keys(answer)

    def _fill_and_submit(self, form_data):
        if form_data:
            for name, value in form_data.items():
                try:
                    field = self.driver.find_element(By.NAME, name)
                    field.clear()
                    field.send_keys(value)
                except Exception:
                    pass

        submit = self.driver.find_element(
            By.CSS_SELECTOR, "button[type='submit'], input[type='submit']"
        )
        submit.click()
        time.sleep(3)
        return self.driver.current_url

    def _api_solve(self, method, **params):
        submit = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key, "method": method, "json": 1, **params,
        }, timeout=30)
        data = submit.json()
        if data.get("status") != 1:
            raise Exception(f"Submit error: {data.get('request')}")

        task_id = data["request"]
        for _ in range(30):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key, "action": "get", "id": task_id, "json": 1,
            }, timeout=30).json()
            if result.get("status") == 1:
                return result["request"]
        raise TimeoutError("Solve timed out")

# Usage
solver = SeleniumCaptchaSolver(API_KEY)
solver.start()
try:
    result_url = solver.solve_and_submit(
        "https://example.com/login",
        form_data={"username": "user@example.com", "password": "pass123"},
    )
    print(f"Result: {result_url}")
finally:
    solver.stop()

استكشاف الأخطاء الشائعة

العرض (المشكلة) السبب المحتمل الحل
تم اكتشاف الاختبار لكن حقن التوكن يفشل الحقل g-recaptcha-response غير موجود في الصفحة تحقّق من وجود iframe؛ قد يكون reCAPTCHA داخل إطار منفصل
تم حقن التوكن لكن النموذج لا يُرسَل لم تُستدعَ دالة الـ callback استدعِ دالة callback الخاصة بـ reCAPTCHA صراحةً
الموقع يُبلّغ عن "webdriver detected" إعدادات التشغيل المستقر غير مُطبّقة أضف أمر CDP الذي يزيل علامة webdriver
لقطة شاشة CAPTCHA فارغة العنصر خارج نطاق العرض مرّر العنصر إلى داخل الشاشة أولاً
رفض توكن Turnstile مفتاح الموقع غير صحيح أعد استخراجه من مصدر الصفحة المباشر

الأسئلة الشائعة

هل تكفي دالة واحدة لحل reCAPTCHA وTurnstile وCAPTCHA الصور؟

نعم. الدالة solve_captcha تستقبل اسم الطريقة والمعاملات فقط، فتعمل مع userrecaptcha وturnstile وbase64 دون تغيير في منطقها — لذلك تبقى فئة SeleniumCaptchaSolver قصيرة.

كم يكلّف تشغيل هذا الحل عبر CaptchaAI؟

الفوترة على الـ threads المتزامنة لا على كل عملية حل، وكل خطة تشمل حلولاً غير محدودة لكل thread. تبدأ BASIC من $15 شهرياً مع 5 threads؛ راجع صفحة الأسعار الرسمية لاختيار الخطة الأنسب لحجمك.

هل يمكن حل hCaptcha أو FunCaptcha عبر CaptchaAI؟

لا، لا يدعم CaptchaAI حل hCaptcha ولا FunCaptcha (Arkose Labs). لكنه يحل reCAPTCHA v2/v3 بما فيها Enterprise، وCloudflare Turnstile وChallenge، وGeeTest v3، وCAPTCHA الصور والشبكية، وBLS، مع CaptchaFox وFriendly Captcha وLemin في مرحلة beta.

كيف أتعامل مع reCAPTCHA الموجود داخل iframe؟

انتقل إلى إطار reCAPTCHA أولاً عبر driver.switch_to.frame(...)، استخرج مفتاح الموقع، ثم عُد إلى المحتوى الأصلي بـ driver.switch_to.default_content().

هل يؤثر التشغيل بدون واجهة (headless) على نجاح الحل؟

الحل نفسه يجري على خوادم CaptchaAI فلا يتأثر بوضع headless. لكن بعض المواقع تكتشف هذا الوضع وتغيّر سلوكها، فاستخدم --headless=new وتأكّد من صحة بصمات WebGL/Canvas.


الخلاصة

يمنحك دمج CaptchaAI مع Selenium وPython مساراً واحداً لأي اختبار CAPTCHA في أتمتة المتصفح: اكتشف النوع من مصدر الصفحة، احصل على التوكن عبر API، احقنه بـ JavaScript، ثم أرسل النموذج. وفئة SeleniumCaptchaSolver تختصر هذا المسار في استدعاء واحد قابل لإعادة الاستخدام.

مقالات ذات صلة

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.