التكاملات

Scrapy + دليل التكامل CaptchaAI

Scrapy هو إطار عمل الزحف الأكثر شيوعًا في Python. يوضح هذا الدليل كيفية إضافة حل CaptchaAI CAPTCHA إلى العناكب الخاصة بك باستخدام برنامج وسيط مخصص.

المتطلبات

المتطلبات التفاصيل
بايثون 3.8+
سكرابي 2.5+
طلبات لمكالمات CaptchaAI API
مفتاح CaptchaAI API احصل على واحدة هنا
pip install scrapy requests

وحدة حل CaptchaAI

قم بإنشاء captcha_solver.py في جذر مشروع Scrapy الخاص بك:

import requests
import time


class CaptchaAISolver:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://ocr.captchaai.com"

    def solve_recaptcha(self, site_key, page_url, timeout=300):
        resp = requests.get(f"{self.base_url}/in.php", params={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
        })

        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit failed: {resp.text}")

        task_id = resp.text.split("|")[1]
        deadline = time.time() + timeout

        while time.time() < deadline:
            time.sleep(5)
            result = requests.get(f"{self.base_url}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
            })

            if result.text == "CAPCHA_NOT_READY":
                continue
            if result.text.startswith("OK|"):
                return result.text.split("|", 1)[1]
            raise Exception(f"Solve failed: {result.text}")

        raise TimeoutError(f"Task {task_id} timed out")

    def solve_image(self, image_base64, timeout=120):
        resp = requests.get(f"{self.base_url}/in.php", params={
            "key": self.api_key,
            "method": "base64",
            "body": image_base64,
        })

        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit failed: {resp.text}")

        task_id = resp.text.split("|")[1]
        deadline = time.time() + timeout

        while time.time() < deadline:
            time.sleep(5)
            result = requests.get(f"{self.base_url}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
            })

            if result.text == "CAPCHA_NOT_READY":
                continue
            if result.text.startswith("OK|"):
                return result.text.split("|", 1)[1]
            raise Exception(f"Solve failed: {result.text}")

        raise TimeoutError(f"Task {task_id} timed out")

سكرابي الوسيطة

إنشاء middlewares.py:

import base64
import re
from scrapy import signals
from scrapy.http import HtmlResponse
from captcha_solver import CaptchaAISolver


class CaptchaAIMiddleware:
    """Scrapy downloader middleware that detects and solves CAPTCHAs."""

    def __init__(self, api_key):
        self.solver = CaptchaAISolver(api_key)

    @classmethod
    def from_crawler(cls, crawler):
        api_key = crawler.settings.get("CAPTCHAAI_API_KEY")
        if not api_key:
            raise ValueError("CAPTCHAAI_API_KEY setting is required")
        return cls(api_key)

    def process_response(self, request, response, spider):
        # Check for reCAPTCHA on the page
        site_key = self._find_recaptcha_key(response.text)
        if site_key:
            spider.logger.info(f"reCAPTCHA detected on {response.url}")
            token = self.solver.solve_recaptcha(site_key, response.url)
            request.meta["captcha_token"] = token
            spider.logger.info("CAPTCHA solved successfully")

        # Check for image CAPTCHA
        captcha_img = self._find_image_captcha(response)
        if captcha_img:
            spider.logger.info(f"Image CAPTCHA detected on {response.url}")
            text = self.solver.solve_image(captcha_img)
            request.meta["captcha_text"] = text
            spider.logger.info(f"Image CAPTCHA solved: {text}")

        return response

    def _find_recaptcha_key(self, html):
        match = re.search(
            r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', html
        )
        return match.group(1) if match else None

    def _find_image_captcha(self, response):
        img = response.css("img#captcha-image::attr(src)").get()
        if img and img.startswith("data:image"):
            return img.split(",", 1)[1]
        return None

تكوين الإعدادات

أضف إلى settings.py:

import os

CAPTCHAAI_API_KEY = os.environ.get("CAPTCHAAI_API_KEY")

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.CaptchaAIMiddleware": 560,
}

مثال العنكبوت

import scrapy


class ProductSpider(scrapy.Spider):
    name = "products"
    start_urls = ["https://example.com/products"]

    def parse(self, response):
        # If CAPTCHA was solved, the token is in meta
        token = response.meta.get("captcha_token")
        if token:
            # Resubmit the page with the token
            yield scrapy.FormRequest(
                url=response.url,
                formdata={"g-recaptcha-response": token},
                callback=self.parse_products,
            )
        else:
            yield from self.parse_products(response)

    def parse_products(self, response):
        for product in response.css(".product-item"):
            yield {
                "name": product.css("h2::text").get(),
                "price": product.css(".price::text").get(),
                "url": response.urljoin(
                    product.css("a::attr(href)").get()
                ),
            }

        next_page = response.css("a.next-page::attr(href)").get()
        if next_page:
            yield scrapy.Request(response.urljoin(next_page))

أعد المحاولة على صفحات CAPTCHA

أضف إعادة المحاولة التلقائية عندما تظهر اختبارات CAPTCHA:

class CaptchaRetryMiddleware:
    """Retry requests that return CAPTCHA challenge pages."""

    max_retries = 3

    def process_response(self, request, response, spider):
        if self._is_captcha_page(response):
            retries = request.meta.get("captcha_retries", 0)
            if retries < self.max_retries:
                request.meta["captcha_retries"] = retries + 1
                spider.logger.info(
                    f"CAPTCHA page detected, retry {retries + 1}"
                )
                return request.copy()

        return response

    def _is_captcha_page(self, response):
        indicators = [
            "g-recaptcha",
            "cf-turnstile",
            "captcha-image",
            "Please verify you are human",
        ]
        return any(ind in response.text for ind in indicators)

تشغيل العنكبوت

export CAPTCHAAI_API_KEY="YOUR_API_KEY"
scrapy crawl products -o products.json

استكشاف الأخطاء وإصلاحها

المشكلة السبب الإجراء
ValueError: CAPTCHAAI_API_KEY setting is required إنف فار مفقود قم بتعيين CAPTCHAAI_API_KEY
لم يتم اكتشاف اختبار CAPTCHA بنية HTML مختلفة تحديث نمط regex في البرامج الوسيطة
TimeoutError على الحل حل بطيء أو شبكة زيادة المهلة في
يتم حظر العنكبوت بعد الحل الحظر على أساس IP إضافة وسيطة لتناوب الوكيل

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

هل يمكنني استخدام هذا مع Scrapy-Splash أو Scrapy-Playwright؟

نعم. بالنسبة للصفحات التي يتم عرضها باستخدام JavaScript، تعمل البرامج الوسيطة بنفس الطريقة - فهي تفحص استجابة HTML النهائية لعناصر CAPTCHA.

هل تعمل البرامج الوسيطة على إبطاء عملية الزحف؟

يستغرق حل اختبار CAPTCHA من 5 إلى 15 ثانية لكل صفحة. استخدم CONCURRENT_REQUESTS للزحف إلى الصفحات الأخرى أثناء الانتظار. فقط الصفحات التي تحتوي على اختبار CAPTCHA هي التي تسبب التأخير.

كيف يمكنني التعامل مع أنواع CAPTCHA المختلفة لكل صفحة؟

قم بتوسيع طريقة process_response الخاصة بالبرنامج الوسيط للتحقق من Turnstile أو GeeTest أو الأنواع الأخرى واستدعاء طريقة الحل المناسبة.

أدلة ذات صلة

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