Kasus Penggunaan

Penanganan CAPTCHA untuk Monitoring Status Penerbangan

Monitoring status penerbangan memerlukan pengecekan rutin di beberapa portal maskapai dan bandara. Portal ini melindungi data real-time mereka dengan Cloudflare Turnstile, reCAPTCHA, dan CAPTCHA kustom — terutama saat mendeteksi query otomatis yang berulang. Berikut cara menangani CAPTCHA sekaligus membangun alat pelacak penerbangan yang andal.

Di Mana CAPTCHA Muncul

Tipe Portal CAPTCHA Pemicu
Halaman status penerbangan maskapai Cloudflare Turnstile Request sering dari IP yang sama
Board kedatangan/keberangkatan bandara Cloudflare Challenge Deteksi bot
Search engine penerbangan reCAPTCHA v2/v3 Submit form pencarian
Cek status pemesanan reCAPTCHA v2 Sebelum menampilkan itinerary
Halaman rate limit API CAPTCHA kustom Setelah melampaui batas request

Arsitektur Monitor Penerbangan

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()
        }

Monitoring Berkala dengan Penanganan 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")

Monitor Multi-Maskapai (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());
  }
}

Frekuensi Monitoring dan Tingkat CAPTCHA

Frekuensi cek Tingkat CAPTCHA tipikal Rekomendasi
Setiap 1 menit Tinggi (50–80%) Terlalu agresif — tambah interval
Setiap 5 menit Sedang (10–30%) Dapat diterima untuk penerbangan penting
Setiap 15 menit Rendah (5–10%) Keseimbangan baik untuk monitoring rutin
Setiap 30 menit Sangat rendah (<5%) Terbaik untuk pelacakan jangka panjang
Setiap jam Minimal (<1%) CAPTCHA jarang terpicu

Optimasi Sesi

Kurangi frekuensi CAPTCHA dengan mempertahankan state sesi:

Teknik Efek
Pertahankan cookie antar cek Cloudflare qa_validation_cookie berlaku 15–30 menit
Gunakan User-Agent yang konsisten Mengubah UA memicu challenge baru
Pertahankan konsistensi proxy IP yang sama mengurangi kecurigaan
Distribusikan request secara merata Pola burst memicu rate limit

Pemecahan Masalah

Masalah Penyebab Perbaikan
CAPTCHA di setiap cek Sesi tidak dilanjutkan Reuse requests.Session() di semua pengecekan
Cloudflare block (Error 1020) Terlalu banyak request Tingkatkan interval cek
Data penerbangan basi setelah CAPTCHA Token kedaluwarsa saat solving Gunakan just-in-time solving
Data berbeda dari yang ditampilkan browser JavaScript rendering tidak ada Gunakan browser automation untuk situs JS-heavy

Pertanyaan Umum

Seberapa sering saya harus mengecek status penerbangan?

Biasanya setiap 5–15 menit. Pengecekan lebih sering memicu lebih banyak CAPTCHA dan dapat menyebabkan pemblokiran IP. CaptchaAI menangani Turnstile dengan success rate tinggi, jadi faktor pembatasnya adalah rate limit portal, bukan solve CAPTCHA.

Bisakah saya memonitor penerbangan dari beberapa maskapai sekaligus?

Ya. Gunakan sesi terpisah per maskapai dan solve CAPTCHA secara independen untuk masing-masing. CaptchaAI menangani concurrent request di berbagai situs.

Apakah mobile API maskapai memiliki CAPTCHA?

Mobile API biasanya menggunakan autentikasi yang berbeda (API key, OAuth) dan bukan CAPTCHA. Namun, endpoint web yang mereka sajikan mungkin masih memiliki proteksi Cloudflare.

Artikel Terkait

  • Cloudflare Turnstile 403 Setelah Token Fix
  • Mode Widget Cloudflare Turnstile Explained

Langkah Selanjutnya

Bangun monitoring penerbangan yang andal — dapatkan API key CaptchaAI Anda dan tangani CAPTCHA maskapai secara otomatis.

Komentar dinonaktifkan untuk artikel ini.