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

مراقبة أسعار السفر باستخدام تقنية CAPTCHA

تقوم مواقع حجز السفر بحظر عمليات فحص الأسعار الآلية باستخدام اختبارات CAPTCHA واكتشاف الروبوتات. يتيح لك CaptchaAI مراقبة أسعار رحلات الطيران والفنادق بشكل موثوق من خلال هذه الدفاعات.

منظر CAPTCHA على مواقع السفر

فئة الموقع نوع التحقق صعوبة
الخطوط الجوية (مباشرة) reCAPTCHA v3، كلاودفلير متوسط
وكالات السفر عبر الإنترنت (إكسبيديا، الحجز) reCAPTCHA v2، Turnstile متوسطة عالية
البحث التعريفي (Google Flights، Kayak) reCAPTCHA v3 متوسط
شركات الطيران الميزانية صورة CAPTCHA، reCAPTCHA منخفض-متوسط
مجمعات الفنادق Cloudflare Challenge عالية

تنفيذ مراقبة الأجرة

import requests
import time
import re
import json
import os
from datetime import datetime, timedelta

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


def solve_captcha(params):
    params["key"] = API_KEY
    resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit: {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": 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: {result.text}")
    raise TimeoutError()


class FareMonitor:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers["User-Agent"] = (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/120.0.0.0"
        )
        self.history = []

    def fetch_with_captcha(self, url):
        """Fetch a travel page, solving CAPTCHAs if encountered."""
        resp = self.session.get(url)

        # reCAPTCHA v2/v3
        match = re.search(
            r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', resp.text
        )
        if match:
            site_key = match.group(1)

            # Detect v3 vs v2
            if "recaptcha/api.js?render=" in resp.text:
                token = solve_captcha({
                    "method": "userrecaptcha",
                    "googlekey": site_key,
                    "pageurl": url,
                    "version": "v3",
                    "action": "search",
                })
            else:
                token = solve_captcha({
                    "method": "userrecaptcha",
                    "googlekey": site_key,
                    "pageurl": url,
                })

            resp = self.session.post(url, data={
                "g-recaptcha-response": token,
            })

        # Cloudflare Turnstile
        if "cf-turnstile" in resp.text:
            match = re.search(
                r'data-sitekey=["\']([^"\']+)', resp.text
            )
            if match:
                token = solve_captcha({
                    "method": "turnstile",
                    "sitekey": match.group(1),
                    "pageurl": url,
                })
                resp = self.session.post(url, data={
                    "cf-turnstile-response": token,
                })

        return resp.text

    def check_fares(self, routes):
        """Check fares for a list of routes."""
        results = []
        for route in routes:
            try:
                html = self.fetch_with_captcha(route["url"])
                prices = self._extract_prices(html)

                result = {
                    "route": f"{route['origin']}-{route['destination']}",
                    "date": route["date"],
                    "prices": prices,
                    "min_price": min(prices) if prices else None,
                    "timestamp": datetime.utcnow().isoformat(),
                }
                results.append(result)
                self.history.append(result)

                if prices:
                    print(f"  {result['route']} ({route['date']}): "
                          f"${min(prices)}-${max(prices)}")
                else:
                    print(f"  {result['route']}: No prices found")

                time.sleep(3)  # Respectful delay

            except Exception as e:
                print(f"  {route.get('origin', '?')}-"
                      f"{route.get('destination', '?')}: ERROR - {e}")

        return results

    def _extract_prices(self, html):
        """Extract prices from travel page HTML."""
        prices = []
        # Common price patterns
        for match in re.finditer(
            r'\$\s*([\d,]+(?:\.\d{2})?)', html
        ):
            price = float(match.group(1).replace(",", ""))
            if 20 < price < 10000:  # Filter noise
                prices.append(price)
        return sorted(set(prices))

    def detect_price_drops(self, threshold_pct=5):
        """Detect significant price drops in history."""
        route_prices = {}
        for entry in self.history:
            key = f"{entry['route']}_{entry['date']}"
            if key not in route_prices:
                route_prices[key] = []
            if entry["min_price"]:
                route_prices[key].append(entry["min_price"])

        alerts = []
        for key, prices in route_prices.items():
            if len(prices) >= 2:
                prev = prices[-2]
                current = prices[-1]
                change_pct = ((current - prev) / prev) * 100
                if change_pct < -threshold_pct:
                    alerts.append({
                        "route": key,
                        "previous": prev,
                        "current": current,
                        "change": f"{change_pct:.1f}%",
                    })

        return alerts

    def export_report(self, filename="fare_report.json"):
        """Export fare history to JSON."""
        with open(filename, "w") as f:
            json.dump(self.history, f, indent=2)
        print(f"Exported {len(self.history)} fare checks to {filename}")


# Define routes to monitor
routes = [
    {
        "origin": "JFK",
        "destination": "LAX",
        "date": "2025-03-15",
        "url": "https://example-airline.com/flights?from=JFK&to=LAX&date=2025-03-15",
    },
    {
        "origin": "SFO",
        "destination": "ORD",
        "date": "2025-03-20",
        "url": "https://example-airline.com/flights?from=SFO&to=ORD&date=2025-03-20",
    },
]

monitor = FareMonitor()
results = monitor.check_fares(routes)
monitor.export_report()

الجدولة

# Check fares every 4 hours
0 */4 * * * cd /opt/fare-monitor && python fare_monitor.py

تحليل التكلفة

مستوى المراقبة الطرق الشيكات/Day CAPTCHAs/Day مؤسسة. التكلفة
شخصي 5 6/route ~30 0.50 دولار
وكالة صغيرة 50 4/route ~200 2-5 دولار
المؤسسة 500 6/route ~ 3000 20-40 دولارًا

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

كم مرة يجب أن أتحقق من الأسعار؟

كل 4-6 ساعات للاستخدام الشخصي. كل 1-2 ساعات للاستخدام التجاري. تقوم شركات الطيران بتحديث الأسعار على دفعات، لذا فإن عمليات الفحص المتكررة تؤدي إلى عوائد متناقصة.

هل يمكنني مراقبة أسعار الفنادق أيضًا؟

نعم. يعمل نفس الأسلوب مع مواقع Booking.com وExpedia والفنادق المباشرة. ضبط أنماط استخراج الأسعار لتنسيقات صفحات الفندق.

كيف أتعامل مع صفحات التسعير الديناميكية؟

تتطلب بعض مواقع السفر عرض JavaScript. استخدم السيلينيوم أو Playwright لجلب الصفحة، ثم CaptchaAI لحل اختبار CAPTCHA.

أدلة ذات صلة

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