حالات الاستخدام

أتمتة مقارنة أسعار التأمين وحلّ اختبارات CAPTCHA

يعتمد جمع عروض أسعار التأمين آلياً على أمرين معاً: حلّ اختبار CAPTCHA الذي يحرس نموذج التسعير، والحفاظ على جلسة ثابتة من أول خطوة حتى ظهور السعر النهائي. تتكفّل CaptchaAI بالجزء الأول عبر حلّ reCAPTCHA v2 واختبارات الصور، بينما تتكفّل أنت ببقاء عنوان IP والجلسة ثابتين على امتداد المسار. هذا الدليل موجَّه لفرق ضمان الجودة ومنصات المقارنة التي تختبر تدفقات التأمين داخل بيئات مملوكة أو مصرَّح بها، لا لجمع بيانات خلف جدران تسجيل الدخول.


أين تظهر اختبارات CAPTCHA في مواقع التأمين

تضع شركات التأمين ومنصات المقارنة اختبار CAPTCHA عند اللحظة التي يُحسب فيها السعر فعلياً، لأن محرّك التسعير هو الأصل الأغلى والأكثر عرضة للجمع الآلي. لذلك يختلف نوع الاختبار ونقطة ظهوره بحسب فرع التأمين، ومن المفيد رسم هذه الخريطة قبل كتابة أي سطر برمجي:

نوع التأمين نوع اختبار CAPTCHA نقطة الظهور تعقيد النموذج
التأمين على السيارات reCAPTCHA v2 تقديم نموذج التسعير متعدد الخطوات (مركبة + سائق)
التأمين الصحي reCAPTCHA v2 مقارنة الخطط متعدد الخطوات (بيانات ديموغرافية)
التأمين على المنزل اختبار صورة (Image CAPTCHA) البحث عن العنوان متوسط
التأمين على الحياة reCAPTCHA v2 حاسبة السعر نموذج بسيط
تأمين السفر reCAPTCHA v2 Invisible طلب عرض السعر نموذج بسيط
التأمين التجاري reCAPTCHA v3 نموذج تواصل/تسعير نموذج طويل

الملاحظة العملية هنا أنّ معظم الفروع تعتمد reCAPTCHA v2، بينما ينفرد التأمين على المنزل باختبارات الصور في صفحات البحث عن العنوان، وينفرد التأمين التجاري بـ reCAPTCHA v3 الذي يعمل في الخلفية دون تفاعل مرئي. تدعم CaptchaAI هذه الأنواع الثلاثة، فيكفي أن يعرف السكربت أيّها يواجه قبل الإرسال.


بناء أداة لجمع عروض الأسعار من عدة شركات

المسار النموذجي لتأمين السيارات يمرّ بخمس خطوات: تحميل صفحة التسعير، إرسال بيانات المركبة، إرسال بيانات السائق، حلّ اختبار CAPTCHA، ثم استلام السعر. الفئة أدناه تُغلّف هذا المسار داخل جلسة requests واحدة تحمل عنوان IP ثابتاً عبر البروكسي، ثم تكرّره على قائمة من الشركات وتُرتّب النتائج حسب القسط الشهري. لاحظ أنّ الرمز الناتج من حلّ reCAPTCHA يُمرَّر في الحقل g-recaptcha-response:

import requests
import time
import re
import base64

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_recaptcha(sitekey, pageurl):
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY, "method": "userrecaptcha",
        "googlekey": sitekey, "pageurl": pageurl, "json": 1,
    })
    task_id = resp.json()["request"]
    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]
    raise TimeoutError("Timeout")


def solve_image_captcha(image_bytes):
    img_b64 = base64.b64encode(image_bytes).decode()
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY, "method": "base64",
        "body": img_b64, "json": 1,
    })
    task_id = resp.json()["request"]
    for _ in range(20):
        time.sleep(3)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]
    raise TimeoutError("Timeout")


class InsuranceQuoteCollector:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
        })

    def get_auto_quote(self, carrier_url, vehicle_data, driver_data, sitekey):
        """Get auto insurance quote from a single carrier."""
        # Step 1: Load quote page
        self.session.get(carrier_url)

        # Step 2: Submit vehicle info
        resp = self.session.post(f"{carrier_url}/vehicle", data=vehicle_data)

        # Step 3: Submit driver info
        resp = self.session.post(f"{carrier_url}/driver", data=driver_data)

        # Step 4: Solve CAPTCHA on quote page
        token = solve_recaptcha(sitekey, f"{carrier_url}/quote")

        # Step 5: Get quote
        resp = self.session.post(f"{carrier_url}/quote", data={
            "g-recaptcha-response": token,
        })

        if resp.status_code == 200:
            return self._parse_quote(resp.text)
        return None

    def compare_carriers(self, carriers, vehicle_data, driver_data):
        """Compare quotes across multiple carriers."""
        quotes = []

        for carrier in carriers:
            try:
                quote = self.get_auto_quote(
                    carrier_url=carrier["url"],
                    vehicle_data=vehicle_data,
                    driver_data=driver_data,
                    sitekey=carrier["sitekey"],
                )
                quotes.append({
                    "carrier": carrier["name"],
                    "status": "success",
                    "quote": quote,
                })
            except Exception as e:
                quotes.append({
                    "carrier": carrier["name"],
                    "status": "failed",
                    "error": str(e),
                })
            time.sleep(5)  # Delay between carriers

        # Sort by price
        successful = [q for q in quotes if q["status"] == "success" and q["quote"]]
        successful.sort(key=lambda x: x["quote"].get("monthly_premium", float("inf")))

        return {
            "quotes": quotes,
            "best_rate": successful[0] if successful else None,
            "total_compared": len(carriers),
            "successful": len(successful),
        }

    def _parse_quote(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        premium_el = soup.select_one(".premium, .monthly-rate, .quote-amount")
        coverage_el = soup.select_one(".coverage-summary, .plan-details")

        return {
            "monthly_premium": premium_el.get_text(strip=True) if premium_el else "",
            "coverage": coverage_el.get_text(strip=True) if coverage_el else "",
        }


# Usage
collector = InsuranceQuoteCollector(
    proxy="http://user-session-abc:pass@residential.proxy.com:5000"
)

vehicle = {
    "year": "2022",
    "make": "Toyota",
    "model": "Camry",
    "vin": "",
    "mileage": "15000",
}

driver = {
    "age": "35",
    "gender": "M",
    "zip": "90210",
    "driving_record": "clean",
}

carriers = [
    {"name": "Carrier A", "url": "https://carrier-a.example.com/auto", "sitekey": "6Lc_xxx1"},
    {"name": "Carrier B", "url": "https://carrier-b.example.com/auto", "sitekey": "6Lc_xxx2"},
    {"name": "Carrier C", "url": "https://carrier-c.example.com/auto", "sitekey": "6Lc_xxx3"},
]

comparison = collector.compare_carriers(carriers, vehicle, driver)
print(f"Best rate: {comparison['best_rate']}")

مصفوفة carriers هنا هي نقطة التوسيع الوحيدة: أضف شركة جديدة بعنوانها وsitekey الخاص بها، ولا يتغيّر باقي المنطق. التأخير بين الشركات (time.sleep(5)) ليس تفصيلاً تجميلياً، بل هو ما يبقي معدل الطلبات ضمن الحدود المقبولة ويقلّل ظهور اختبارات إضافية.


مقارنة خطط التأمين الصحي برمجياً

مواقع التأمين الصحي تختلف قليلاً: بدل بيانات المركبة والسائق، تُرسِل بيانات ديموغرافية (العمر، الموقع، عدد أفراد الأسرة)، ثم يُحسب قسط كل خطة عند صفحة النتائج المحميّة بـ reCAPTCHA. الدالة أدناه تعيد استخدام الفئة نفسها، وتحلّ الاختبار مرة واحدة عند الانتقال إلى صفحة الخطط، ثم تستخرج اسم كل خطة وقسطها والمبلغ المقتطع:

def compare_health_plans(marketplace_url, demographics, sitekey):
    """Compare health insurance plans on marketplace sites."""
    collector = InsuranceQuoteCollector(
        proxy="http://user-session-xyz:pass@residential.proxy.com:5000"
    )

    # Load marketplace
    collector.session.get(marketplace_url)

    # Submit demographics
    collector.session.post(f"{marketplace_url}/demographics", data=demographics)

    # Solve CAPTCHA for plan results
    token = solve_recaptcha(sitekey, f"{marketplace_url}/plans")

    resp = collector.session.post(f"{marketplace_url}/plans", data={
        "g-recaptcha-response": token,
    })

    if resp.status_code == 200:
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(resp.text, "html.parser")
        plans = []
        for card in soup.select(".plan-card, .insurance-plan"):
            plans.append({
                "name": card.select_one(".plan-name").get_text(strip=True) if card.select_one(".plan-name") else "",
                "premium": card.select_one(".premium").get_text(strip=True) if card.select_one(".premium") else "",
                "deductible": card.select_one(".deductible").get_text(strip=True) if card.select_one(".deductible") else "",
            })
        return plans

    return []

الفكرة الجوهرية أنّ اختلاف الفرع لا يعني منطقاً جديداً بالكامل: بنية «حمّل ← أرسِل البيانات ← حُلّ الاختبار ← استخرج النتائج» ثابتة، وما يتغيّر فعلياً هو حقول النموذج ومحدِّدات CSS التي تقرأ منها النتائج.


إدارة الجلسات الثابتة على امتداد خطوات التسعير

عرض أسعار التأمين متعدد الخطوات دائماً، لذا فإنّ الجلسة الثابتة (Sticky Session) شرط لا اختيار. يربط خادم شركة التأمين كل خطوة بجلستك وبعنوان IP الذي بدأت به، وأيّ تغيير في العنوان بين الخطوات يُبطل الجلسة ويعيدك إلى البداية:

خطوة البيانات المُرسَلة متطلب عنوان IP
1. المعلومات الشخصية الاسم، تاريخ الميلاد، الرمز البريدي نفس عنوان IP
2. المركبة/العقار السنة، الصنع، الموديل نفس عنوان IP
3. اختيار التغطية الحدود، مبلغ الخصم نفس عنوان IP
4. حلّ اختبار CAPTCHA الرمز المميز نفس عنوان IP
5. عرض السعر لا يوجد نفس عنوان IP

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


معالجة الأخطاء الشائعة

معظم الإخفاقات في هذا النوع من الأتمتة لا علاقة لها بحلّ الاختبار نفسه، بل بإدارة الجلسة والتوقيت. الجدول التالي يربط كل عرض خطأ بسببه المرجّح وبالإجراء المباشر:

المشكلة السبب الإجراء
يُعيد نموذج التسعير ضبط التدفق في منتصفه تغيّر عنوان IP بين الخطوات استخدم جلسة بروكسي ثابتة
«تعذّر تقديم عرض السعر» فشل التحقق من صحة البيانات راجع الحقول المطلوبة
يظهر اختبار CAPTCHA مرتين انتهت صلاحية الرمز الأول أعِد الحلّ فوراً
أسعار مختلفة عن النتيجة اليدوية اختلافات في Cookie/الجلسة ابدأ بجلسة نظيفة
محدِّد المعدل يحجب الطلبات عدد كبير من العروض من عنوان واحد خفّف الوتيرة ودوّر عنوان IP بين الشركات

عالج مشكلة الجلسة أولاً، فإصلاحها يزيل غالبية الأعراض الأخرى تلقائياً.


سيناريو محلي: مجمّعات التأمين في الخليج ومصر

تأمين السيارات إلزامي في معظم دول الخليج ومصر، ما جعل منصات مقارنة أسعار التأمين على الإنترنت تنمو بسرعة، خصوصاً في مواسم تجديد الوثائق السنوية عندما ترتفع حركة الطلبات دفعةً واحدة. تخيّل فريق ضمان جودة في منصة مقارنة إقليمية يريد التأكد أنّ محرّك التسعير لديه يستقبل عروضاً محدّثة من عدة شركات دون انقطاع قبل موسم الذروة. بدل الملء اليدوي المتكرر للنماذج، يشغّل الفريق السكربت أعلاه على بيئة اختبار مملوكة أو على شركاء مصرّح لهم، فتتولى CaptchaAI حلّ reCAPTCHA عند كل نموذج بينما تبقى الجلسة ثابتة عبر الخطوات.

ولأنّ نموذج تسعير CaptchaAI قائم على عدد المسارات المتزامنة (Threads) لا على عدد عمليات الحل، يمكن تشغيل عدة شركات في آنٍ واحد ضمن الخطة نفسها دون رسوم لكل عملية حلّ. الأسعار تُذكر دائماً بالدولار الأمريكي (USD)، ويُفضَّل مراجعة صفحة الأسعار الرسمية قبل اختيار الخطة المناسبة لحجم الاختبار المتوقع.


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

هل أتمتة جمع عروض الأسعار مسموح بها؟

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

كيف أميّز بين reCAPTCHA v2 وv3 وأتعامل مع كل منهما؟

يعرض v2 تحدياً مرئياً عند تقديم النموذج، بينما يعمل v3 في الخلفية ويمنح درجة ثقة دون تفاعل. كلاهما يُرسَل إلى CaptchaAI بالطريقة userrecaptcha، والفرق العملي أنّ v3 غالباً ما يظهر في نماذج التأمين التجاري الطويلة، لذا احرص على تمرير pageurl الصحيح.

كيف أتعامل مع اختبارات الصور في صفحات البحث عن العنوان؟

مواقع التأمين على المنزل تستخدم غالباً Image CAPTCHA عند البحث عن العنوان. استخدم الدالة solve_image_captcha التي تُرسِل الصورة بترميز base64 وتستطلع النتيجة، ثم مرّر الناتج إلى الحقل المطلوب في النموذج تماماً كما تفعل مع الرمز النصي.

لماذا تختلف الأسعار المجمّعة آلياً عن الأسعار اليدوية أحياناً؟

غالباً بسبب اختلاف ملفات تعريف الارتباط (Cookies) أو حالة الجلسة أو عنوان IP، إذ تعتمد بعض المحرّكات على الموقع الجغرافي وسجلّ التصفح. ابدأ بجلسة نظيفة وثابتة العنوان للحصول على نتيجة مطابقة للتجربة اليدوية.

هل يعمل نفس الأسلوب مع فروع تأمين مختلفة؟

نعم. بنية «حمّل ← أرسِل البيانات ← حُلّ الاختبار ← استخرج النتائج» واحدة عبر تأمين السيارات والصحة والمنزل والحياة؛ ما يتغيّر هو حقول النموذج ومحدِّدات قراءة النتائج فقط.


أدلة ذات صلة


قارن أسعار التأمين على نطاق واسع — احصل على مفتاح CaptchaAI الخاص بك وحوّل جمع العروض إلى مسار آلي قابل للتكرار.

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