Use Cases

Pemantauan Tarif Maskapai dengan Penanganan CAPTCHA

Yang membuat monitor tarif maskapai mati diam-diam bukan selector HTML yang berubah, melainkan CAPTCHA di tengah pemeriksaan harga. Begitu skrip mengecek banyak rute dari satu IP, situs perjalanan menyodorkan reCAPTCHA v2, Cloudflare Turnstile, atau halaman Cloudflare Challenge, dan pipeline berhenti. CaptchaAI mengambil alih langkah penyelesaian CAPTCHA lewat satu panggilan API, sehingga monitor bisa lanjut mengumpulkan data tarif tanpa intervensi manual.

Bagi tim data dan freelancer scraping di Indonesia yang memantau harga tiket lintas rute, langkah inilah yang paling sering menghentikan pipeline tanpa peringatan. Panduan ini membangun monitor tarif Python yang mendeteksi jenis CAPTCHA, menyelesaikannya via CaptchaAI, lalu menyuntikkan token kembali ke request.


Cara kerja alur pemantauan tarif

Schedule check → Request fare page → CAPTCHA detected?
                                         ↓ Yes
                                    Solve via CaptchaAI → Inject token → Retry request
                                         ↓ No
                                    Parse fare data → Store → Alert on price change

Alurnya lurus: penjadwal memicu pemeriksaan, monitor mengambil halaman tarif, lalu memeriksa apakah ada CAPTCHA. Bila ada, token dari CaptchaAI disuntikkan dan request diulang; bila tidak, harga langsung diurai, disimpan, lalu dibandingkan dengan snapshot sebelumnya untuk memicu peringatan saat harga bergerak.


Yang perlu disiapkan

Sebelum menulis kode, siapkan tiga hal berikut. Ketiganya cukup untuk menjalankan monitor pertama Anda dari laptop atau server kecil.

Kebutuhan Keterangan
API key CaptchaAI Daftar di captchaai.com
Python 3.8+ Beserta pustaka requests
Proxy egress jaringan yang diotorisasi untuk situs perjalanan
pip install requests

Fungsi pembantu solver CaptchaAI

Dua fungsi ini menangani CAPTCHA tersering di situs maskapai — reCAPTCHA v2 dan Cloudflare Turnstile — dengan pola empat langkah yang sama:

  • Kirim task ke in.php beserta sitekey dan URL halaman.
  • Simpan task ID dari respons.
  • Polling res.php sampai status bernilai 1.
  • Pakai token yang dikembalikan pada request berikutnya.
import requests
import time

API_KEY = "YOUR_API_KEY"


def solve_recaptcha_v2(sitekey, pageurl):
    """Solve reCAPTCHA v2 and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "userrecaptcha",
        "googlekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(20)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")


def solve_turnstile(sitekey, pageurl):
    """Solve Cloudflare Turnstile and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "turnstile",
        "sitekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(10)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")

Kelas pemantau tarif dengan penanganan CAPTCHA

Kelas FareMonitor menyatukan semuanya: mengambil halaman tarif, mendeteksi reCAPTCHA atau Turnstile, menyelesaikannya bila perlu, lalu mengurai harga. Sesuaikan _parse_fare per situs.

import json
from datetime import datetime


class FareMonitor:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {
                "http": f"http://{proxy}",
                "https": f"http://{proxy}"
            }
        self.fare_history = {}

    def check_fare(self, route):
        """Check fare for a route, solving CAPTCHAs if needed."""
        url = route["url"]

        response = self.session.get(url)

        # Detect CAPTCHA in response
        if self._has_recaptcha(response.text):
            sitekey = self._extract_sitekey(response.text)
            token = solve_recaptcha_v2(sitekey, url)
            response = self.session.post(url, data={
                "g-recaptcha-response": token,
                **route.get("params", {})
            })

        elif self._has_turnstile(response.text):
            sitekey = self._extract_turnstile_key(response.text)
            token = solve_turnstile(sitekey, url)
            response = self.session.post(url, data={
                "cf-turnstile-response": token,
                **route.get("params", {})
            })

        return self._parse_fare(response.text, route)

    def _has_recaptcha(self, html):
        return "g-recaptcha" in html or "recaptcha/api" in html

    def _has_turnstile(self, html):
        return "cf-turnstile" in html or "turnstile" in html

    def _extract_sitekey(self, html):
        # Extract data-sitekey from reCAPTCHA div
        if 'data-sitekey="' in html:
            start = html.index('data-sitekey="') + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _extract_turnstile_key(self, html):
        if 'data-sitekey="' in html:
            idx = html.index("cf-turnstile")
            start = html.index('data-sitekey="', idx) + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _parse_fare(self, html, route):
        """Parse fare data from the response. Customize per target site."""
        # Placeholder — implement per site
        return {
            "route": route["name"],
            "timestamp": datetime.now().isoformat(),
            "raw_length": len(html)
        }

    def monitor_routes(self, routes):
        """Check all routes and report price changes."""
        results = []
        for route in routes:
            try:
                fare = self.check_fare(route)
                results.append(fare)
                print(f"[OK] {route['name']}: checked")
            except Exception as e:
                print(f"[ERROR] {route['name']}: {e}")
        return results


# Usage
routes = [
    {
        "name": "NYC-LAX",
        "url": "https://example-airline.com/search?from=JFK&to=LAX&date=2025-08-15",
        "params": {"adults": 1}
    },
    {
        "name": "SFO-ORD",
        "url": "https://example-airline.com/search?from=SFO&to=ORD&date=2025-08-20",
        "params": {"adults": 1}
    }
]

monitor = FareMonitor(proxy="user:pass@proxy.example.com:8080")
results = monitor.monitor_routes(routes)

for r in results:
    print(json.dumps(r, indent=2))

Menyimpan riwayat harga dan memicu peringatan

FareMonitor menyimpan fare_history di memori, tetapi monitor produksi butuh data yang bertahan antar-run. Tulis tiap hasil ke database ringan atau file JSON, lalu bandingkan tarif terbaru dengan nilai tersimpan sebelum mengirim notifikasi.

  • Simpan tiap snapshot dengan rute, tarif, mata uang, dan timestamp agar setiap perubahan mudah ditelusuri.
  • Kirim peringatan hanya saat selisih harga melewati ambang tertentu — misalnya turun lebih dari 10% — supaya kanal notifikasi tidak kebanjiran.
  • Pisahkan logika penyimpanan dari logika penyelesaian CAPTCHA agar keduanya bisa diuji terpisah.

Menjadwalkan pemeriksaan berkala

Jalankan monitor terjadwal via cron atau penjadwal tugas lain:

# Check fares every 6 hours
0 */6 * * * cd /path/to/project && python fare_monitor.py >> /var/log/fares.log 2>&1

Interval 4–8 jam biasanya memadai: cukup sering untuk menangkap perubahan tarif, tetapi tidak seagresif itu sehingga memancing CAPTCHA di tiap request.


Daftar periksa pengambilan data tarif

Sebelum menandai monitor siap produksi, pastikan tiap pengambilan data memenuhi hal berikut agar perbandingan harga tetap dapat diandalkan.

  • Simpan tarif dasar, pajak, mata uang, dan kelas tarif dalam satu payload agar perbandingan tetap konsisten.
  • Sediakan minimal dua selector cadangan, dan simpan HTML mentah setiap kali penguraian gagal.
  • Catat rute, tanggal keberangkatan, waktu pengambilan, dan latensi CAPTCHA di tiap penyelesaian.

Mengatasi masalah umum

Sebagian besar kegagalan monitor tarif berulang mengerucut ke empat penyebab. Tabel berikut memetakan gejala ke perbaikannya.

Masalah Penyebab Solusi
CAPTCHA muncul terlalu sering Terlalu banyak request dari satu IP Pakai egress jaringan yang diotorisasi dan diputar berkala
Harga tidak update Halaman ter-cache Tambahkan header cache-busting atau acak parameter request
IP diblokir Pembatasan laju (rate limiting) Perbesar jeda antar pemeriksaan, putar proxy
Penyelesaian CAPTCHA gagal Ekstraksi sitekey keliru Pastikan sitekey cocok dengan CAPTCHA yang ada di halaman

Biaya dan skala untuk pemantauan rutin

CaptchaAI menagih per thread bersamaan, bukan per solve, dan tiap thread menyelesaikan CAPTCHA tanpa batas sepanjang bulan. Monitor tarif yang mengecek beberapa rute tiap 4–8 jam punya volume harian rendah, jadi paket BASIC ($15/bulan, 5 thread) biasanya cukup. Untuk puluhan rute paralel, naik ke STANDARD ($30/bulan, 15 thread) atau ADVANCE ($90/bulan, 50 thread) menambah konkurensi tanpa biaya per-CAPTCHA.

Tanpa tarif per solve, ongkos bulanan tetap dapat diprediksi berapa pun lonjakan CAPTCHA — poin penting bagi agensi price-monitoring yang sensitif terhadap biaya.


Pertanyaan umum

Apakah CaptchaAI bisa menangani hCaptcha di situs maskapai?

Belum. hCaptcha tidak didukung saat ini. CaptchaAI menyelesaikan reCAPTCHA v2/v3, Cloudflare Turnstile dan Challenge, GeeTest v3, serta image/OCR — yang mencakup mayoritas situs perjalanan. Untuk hCaptcha, Anda perlu layanan lain.

Region cloud mana yang cocok untuk deploy monitor dari Indonesia?

Untuk latensi rendah ke API dan situs target, deploy ke AWS ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta), atau GCP asia-southeast2 (Jakarta). Region terdekat memangkas waktu bolak-balik saat polling.

Apakah scraping harga tiket melanggar hukum di Indonesia?

Kumpulkan hanya data yang boleh Anda proses dan hindari data pribadi — UU Pelindungan Data Pribadi (UU 27/2022) dan UU ITE berlaku. Ini bukan nasihat hukum; tinjau ketentuan tiap situs.

Bagaimana cara menangani halaman Cloudflare Challenge?

Pakai method=cloudflare_challenge bersama proxy. Cookie qa_validation_cookie yang dikembalikan memberi akses ke situs. Lihat panduan parameter dan alur token Cloudflare Challenge.


Mulai tangani CAPTCHA di monitor tarif Anda

Daftar dan ambil API key di captchaai.com, lalu sambungkan langkah penyelesaian CAPTCHA ke pipeline pelacakan harga Anda — satu panggilan API, dan monitor berjalan tanpa berhenti di CAPTCHA.


Panduan terkait

Komentar dinonaktifkan untuk artikel ini.