حين تستعلم أداةٌ آلية عن حالة رحلة كل بضع دقائق، تردّ بوابة شركة الطيران في مرحلة ما بصفحة Cloudflare Turnstile أو reCAPTCHA بدلاً من البيانات. الحل بسيط في جوهره: ترسل تفاصيل الاختبار إلى CaptchaAI، تستقبل الرمز الناتج، ثم تحقنه في الطلب لتواصل قراءة حالة الرحلة كأنّ شيئاً لم يكن. هذا الدليل يبيّن أين تظهر هذه الاختبارات في مسارات المراقبة، وكيف تبني أداة تتبّع تتعامل معها تلقائياً دون أن تتعطّل الدورة.
أين تظهر اختبارات CAPTCHA في مسارات مراقبة الرحلات
البوابات لا تعرض الاختبار عشوائياً، بل عند إشارات محدّدة مثل تكرار الطلبات من العنوان نفسه أو إرسال نماذج البحث. معرفة موضع كل نوع يساعدك على توقّعه بدل مفاجأتك به:
| نوع البوابة | اختبار CAPTCHA | المحفّز |
|---|---|---|
| صفحة حالة رحلة طيران | Cloudflare Turnstile | طلبات متكررة من نفس IP |
| لوحات الوصول/المغادرة في المطار | Cloudflare Challenge | كشف البوت |
| محركات بحث الطيران | reCAPTCHA v2/v3 | تقديم نموذج البحث |
| التحقق من حالة الحجز | reCAPTCHA v2 | قبل عرض خط سير الرحلة |
| صفحات تجاوز حدّ معدل API | اختبار CAPTCHA مخصّص | بعد تجاوز حدود الطلب |
الأنواع الأربعة الأولى مدعومة بالكامل في CaptchaAI — Turnstile وCloudflare Challenge وreCAPTCHA v2/v3 — أي أنّ الأداة التي تبنيها تستطيع المرور عبر أيٍّ منها بالمنطق نفسه: اكتشف نوع الاختبار، أرسله، احصل على الرمز، أعد الطلب.
بنية أداة مراقبة الرحلات
نبدأ بصنف Python يغلّف الجلسة ومنطق الكشف والحل معاً. يفحص كل استجابة بحثاً عن علامات الاختبار، ويحوّل تلقائياً إلى مسار Turnstile أو reCAPTCHA بحسب ما يجده في الصفحة:
import requests
import time
from datetime import datetime
class FlightMonitor:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def check_flight(self, airline_url, flight_number):
"""Check flight status, handling CAPTCHAs if encountered."""
response = self.session.get(
f"{airline_url}/flight-status/{flight_number}"
)
if self._is_captcha_page(response):
response = self._solve_and_retry(response, airline_url)
return self._parse_flight_data(response.text)
def _is_captcha_page(self, response):
return (
response.status_code == 403 or
"cf-turnstile" in response.text or
"g-recaptcha" in response.text
)
def _solve_and_retry(self, response, url):
import re
# Detect CAPTCHA type
if "cf-turnstile" in response.text:
match = re.search(r'data-sitekey="(0x[^"]+)"', response.text)
token = self._solve_turnstile(match.group(1), url)
field = "cf-turnstile-response"
else:
match = re.search(r'data-sitekey="([^"]+)"', response.text)
token = self._solve_recaptcha(match.group(1), url)
field = "g-recaptcha-response"
return self.session.post(url, data={field: token})
def _solve_turnstile(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "turnstile",
"sitekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
return self._poll_result(task_id)
def _solve_recaptcha(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
return self._poll_result(task_id)
def _poll_result(self, task_id):
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("CAPTCHA solve timed out")
def _parse_flight_data(self, html):
# Parse flight status from HTML
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
return {
"status": text_or_none(soup.select_one(".flight-status")),
"departure": text_or_none(soup.select_one(".departure-time")),
"arrival": text_or_none(soup.select_one(".arrival-time")),
"gate": text_or_none(soup.select_one(".gate-info")),
"checked_at": datetime.now().isoformat()
}
الفكرة المحورية هنا هي _solve_and_retry: فهي تفصل اكتشاف النوع عن الحل، فيبقى بقية الكود ثابتاً حتى لو غيّرت البوابة نوع الاختبار المعروض.
تشغيل الفحص الدوري مع التعامل التلقائي مع CAPTCHA
بعد جاهزية أداة الفحص المفردة، نلفّها بحلقة دورية تفحص الرحلة على فترات منتظمة وتنبّه عند تغيّر الحالة، مع التقاط أي فشل حتى لا تتوقّف المراقبة بأكملها بسبب استعلام واحد متعثّر:
def monitor_flight(monitor, airline_url, flight_number,
interval_seconds=300, max_checks=48):
"""Monitor a flight every N seconds, handling CAPTCHAs as needed."""
history = []
for check_num in range(max_checks):
try:
status = monitor.check_flight(airline_url, flight_number)
history.append(status)
# Alert on changes
if len(history) > 1 and status["status"] != history[-2]["status"]:
print(f"Status changed: {history[-2]['status']} → {status['status']}")
print(f"Check {check_num + 1}: {status['status']} "
f"(Gate: {status.get('gate', 'Coming soon')})")
except Exception as e:
print(f"Check {check_num + 1} failed: {e}")
time.sleep(interval_seconds)
return history
# Usage
monitor = FlightMonitor("YOUR_API_KEY")
monitor_flight(monitor, "https://airline.example.com", "AA1234")
قيمة interval_seconds هي أهم رافعة لديك: كلما طالت الفترة قلّ عدد الاختبارات التي ستواجهها، كما سيتّضح في جدول التردّد أدناه.
تتبّع عدة شركات طيران في آنٍ واحد بلغة JavaScript
إن كنت تراقب مسارات متعددة في وقت واحد، يفيدك تنظيم كل رحلة في خريطة مستقلّة مع سجلّها الخاص. يعالج هذا الصنف كل رحلة بجلسةٍ ومنطق حلٍّ منفصلين، فلا يؤثّر تعثّر رحلة على البقية:
class FlightTracker {
constructor(apiKey) {
this.apiKey = apiKey;
this.flights = new Map();
}
async addFlight(airline, flightNumber, checkUrl) {
this.flights.set(flightNumber, {
airline,
url: checkUrl,
history: [],
lastCheck: null
});
}
async checkAll() {
const results = [];
for (const [flightNum, flight] of this.flights) {
try {
const status = await this.checkFlight(flight.url, flightNum);
flight.history.push(status);
flight.lastCheck = new Date();
results.push({ flight: flightNum, ...status });
} catch (error) {
results.push({ flight: flightNum, error: error.message });
}
}
return results;
}
async checkFlight(url, flightNumber) {
const response = await fetch(`${url}/status/${flightNumber}`);
const html = await response.text();
// Check for CAPTCHA
if (html.includes('cf-turnstile') || response.status === 403) {
return this.solveAndRetry(url, flightNumber, html);
}
return this.parseStatus(html);
}
async solveAndRetry(url, flightNumber, html) {
const siteKeyMatch = html.match(/data-sitekey="(0x[^"]+)"/);
if (!siteKeyMatch) throw new Error('No sitekey found');
const token = await this.solveTurnstile(siteKeyMatch[1], url);
const response = await fetch(`${url}/status/${flightNumber}`, {
method: 'POST',
body: new URLSearchParams({ 'cf-turnstile-response': token })
});
return this.parseStatus(await response.text());
}
}
سيناريو عملي: مراقبة رحلات الخليج في مواسم الذروة
تخيّل فريق عمليات يتابع رحلات ناقلات مثل الخطوط السعودية وطيران الإمارات والخطوط القطرية عبر مطارات مثل جدة والرياض ودبي والدوحة، خلال موسمَي الحج والعمرة حيث ترتفع الحركة بشكل حاد. في هذه الفترات تشتدّ حساسية البوابات للطلبات المتكررة، فيقفز معدّل ظهور Turnstile وCloudflare Challenge.
هنا يظهر معنى النموذج القائم على الـ Threads في CaptchaAI: كل رحلة تُراقَب بالتوازي تشغل خيط معالجة واحداً أثناء لحظة الحل فقط، ثم يتحرّر الخيط للرحلة التالية. فريق يتابع بضع رحلات حرجة يكفيه اشتراك BASIC ($15 شهرياً، 5 threads)، بينما مركز عمليات يراقب عشرات المسارات في الموسم نفسه قد ينتقل إلى ADVANCE ($90 شهرياً، 50 threads). لا توجد رسوم لكل عملية حل ولا سقف يومي — التكلفة مرتبطة بعدد الخيوط المتزامنة لا بعدد الاختبارات المحلولة، وهو ما يجعل تقدير الميزانية لموسم كامل أمراً واضحاً منذ البداية.
ضبط تردّد الفحص لتقليل اختبارات CAPTCHA
أسرع طريقة لخفض عدد الاختبارات ليست حلّها بكفاءة أكبر، بل استدعاؤها أقل من الأساس. الجدول التالي يقرّب العلاقة بين تردّد الفحص واحتمال ظهور الاختبار:
| تردّد الفحص | معدل CAPTCHA النموذجي | التوصية |
|---|---|---|
| كل دقيقة | مرتفع (50–80%) | عدواني للغاية — أطِل الفاصل الزمني |
| كل 5 دقائق | معتدل (10–30%) | مقبول للرحلات الحرجة |
| كل 15 دقيقة | منخفض (5–10%) | توازن جيد للمراقبة الروتينية |
| كل 30 دقيقة | منخفض جداً (<5%) | الأفضل للتتبع طويل المدى |
| كل ساعة | ضئيل (<1%) | نادراً ما تُفعَّل الاختبارات |
اختر الفترة على أساس أهمية الرحلة: خمس دقائق لرحلة على وشك الإقلاع، وثلاثون دقيقة لمتابعةٍ ممتدة عبر اليوم.
تحسين الجلسة لتقليل مواجهات CAPTCHA
إلى جانب التردّد، يخفض الحفاظ على حالة جلسة متّسقة عدد الاختبارات التي تصادفك، لأنّ البوابة تتعامل معك كزائر واحد مستقرّ لا كسلسلة طلبات مشبوهة:
| التقنية | الأثر |
|---|---|
| الحفاظ على ملفات تعريف الارتباط بين الفحوصات | تبقى cf_clearance من Cloudflare صالحة لمدة 15–30 دقيقة |
| استخدام User-Agent ثابت | تغييره يستدعي تحديات جديدة |
| ثبات الخادم الوسيط | العنوان نفسه يقلّل الاشتباه |
| توزيع الطلبات بالتساوي | أنماط الدفعات المفاجئة تُفعّل حدود المعدل |
استكشاف الأخطاء وإصلاحها
عندما لا يسير المسار كما هو متوقّع، عادةً ما يعود السبب إلى واحد من ثلاثة: إيقاع جمعٍ متسارع، أو رمز حُقِن في جلسة خاطئة، أو إعادة محاولات تضخّم التكلفة:
| المشكلة | السبب | الإجراء |
|---|---|---|
| تظهر اختبارات CAPTCHA كثيرة خلال فترة قصيرة | إيقاع الجمع أو مستوى التوازي أعلى من اللازم بالنسبة للمصدر | خفّض التكرار وحافظ على استقرار الجلسات وراجع جودة الخادم الوسيط |
| البيانات ناقصة حتى بعد حل CAPTCHA | أداة الاستخراج تقرأ عرضاً قديماً أو غير مكتمل | استخرج البيانات بعد تطبيق الرمز داخل الجلسة نفسها |
| ترتفع التكلفة أسرع من المتوقع | كثرة إعادة المحاولات أو الصفحات غير الضرورية تولّد تحديات إضافية | اقصر الحل على الخطوات الحرجة وتتبّع إعادة المحاولات لكل مصدر |
الأسئلة الشائعة
أي خطة من CaptchaAI تناسب مراقبة الرحلات؟
يعتمد الاختيار على عدد الرحلات التي تراقبها في اللحظة نفسها لا على عدد مرات الفحص. لأنّ الفوترة بحسب الـ Threads المتزامنة مع حلول غير محدودة لكل خيط، يكفي BASIC ($15 شهرياً، 5 threads) لبضع رحلات، وتناسب ADVANCE ($90 شهرياً، 50 threads) مراكز العمليات الأكبر.
هل يتعامل CaptchaAI مع Cloudflare Challenge وليس Turnstile فقط؟
نعم. يدعم CaptchaAI كلاً من Cloudflare Turnstile وCloudflare Challenge كنوعين متاحين بشكل كامل، إضافةً إلى reCAPTCHA v2 وv3. أما لوحات المطار التي تعرض Challenge كاملة فتُعامَل بالمنطق نفسه: اكتشف، أرسل، احقن الرمز، أعد الطلب.
لماذا تعود بيانات الرحلة قديمة بعد حل الاختبار؟
غالباً لأنّ الرمز انتهت صلاحيته أو حُقِن في جلسة غير التي أظهرت الاختبار. طبّق الرمز فور استلامه وداخل الجلسة نفسها، ثم اقرأ البيانات مباشرةً بعد إعادة الطلب لا من عرضٍ سابق مخزّن.
هل مراقبة حالة الرحلات عبر الأتمتة أمر متوافق؟
اقتصر على البوابات التي تملك حق الوصول إليها أو التي تسمح شروط استخدامها بذلك، واحترم حدود المعدل بدل محاولة تجاوزها بالضغط. الهدف هنا مراقبة موثوقة لبياناتٍ مصرّح لك بجمعها، لا التحايل على أي حماية.
الخطوات التالية
- ابدأ سريعاً مع CaptchaAI وحلّ أول اختبار خلال دقائق
- حلّ reCAPTCHA v2 عبر الـ API خطوة بخطوة
- حلّ Cloudflare Turnstile عبر واجهة الـ API
- حلّ GeeTest v3 باستخدام الـ API