Use Cases

Pemantauan Harga E-Commerce dengan Penanganan CAPTCHA

Bot pemantau harga Anda tiba-tiba berhenti mengirim data — bukan karena scraper-nya rusak, tapi karena Amazon, Walmart, atau toko Shopify yang Anda pantau baru saja menampilkan CAPTCHA di tengah request otomatis. Ini bukan kegagalan yang selesai dengan retry biasa: halaman produk tetap termuat, harga tetap ada di HTML, tapi request Anda ditahan sampai tantangan itu diselesaikan.

CaptchaAI menyelesaikan reCAPTCHA v2, Cloudflare Turnstile, dan Cloudflare Challenge tepat di titik itu, sehingga pipeline monitoring Anda jalan terus tanpa campur tangan manual.

Kenapa pipeline monitoring berhenti kena CAPTCHA

Setiap platform e-commerce punya pemicu yang berbeda untuk memunculkan tantangan — semakin agresif frekuensi request Anda ke satu domain, semakin cepat CAPTCHA muncul:

  • Amazon — Image CAPTCHA dan reCAPTCHA, dipicu volume request yang tinggi.
  • Walmart — Cloudflare Turnstile, dipicu deteksi bot.
  • eBay — reCAPTCHA v2, dipicu pola request yang mencurigakan.
  • Best Buy — Cloudflare Challenge, berlaku untuk hampir semua traffic otomatis.
  • Toko Shopify — reCAPTCHA v3, bervariasi tergantung konfigurasi masing-masing toko.

Tanpa penanganan otomatis, pipeline ini gagal secara diam-diam: job scheduler tetap "sukses" secara teknis, padahal data harga yang masuk kosong atau basi, dan Anda baru sadar setelah laporan harian menunjukkan celah.

Arsitektur: dari scheduler sampai token tervalidasi

Alurnya sederhana — kirim, deteksi, selesaikan, lanjutkan:

Scheduler (every 30 min)
    → URL Queue
        → Scraper Workers (5-10 concurrent)
            → Fetch page
            → CAPTCHA detected?
                → Yes → CaptchaAI → Solve → Retry page
                → No → Parse prices
            → Store in database
        → Alert on price changes

Begitu langkah "CAPTCHA detected?" bernilai ya, urutan yang sebenarnya terjadi di dalam kotak "CaptchaAI → Solve" selalu sama, apa pun jenis CAPTCHA-nya:

  1. Kirim task ke in.php dengan sitekey halaman dan method yang sesuai (userrecaptcha untuk reCAPTCHA, turnstile untuk Cloudflare Turnstile).
  2. Simpan task_id yang dikembalikan endpoint.
  3. Polling res.php setiap beberapa detik sampai statusnya bukan lagi CAPCHA_NOT_READY.
  4. Pakai token yang dikembalikan untuk mengirim ulang request asli — barulah scraper lanjut ke "Retry page".

Konteks untuk tim monitoring harga di Indonesia

Banyak tim yang menjalankan monitoring harga semacam ini adalah agensi freelance atau tim data kecil yang mengerjakan proyek client lewat Upwork atau Fastwork, di mana sensitivitas biaya bulanan nyata — ini alasan model thread bulanan CaptchaAI (solve tanpa batas per thread) sering lebih masuk akal dibanding skema per-solve murni untuk beban kerja yang naik-turun tiap bulan.

Kalau worker Anda di-deploy ke region seperti AWS ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta), maupun GCP asia-southeast2 (Jakarta), latensi ke situs target umumnya lebih rendah dibanding region default seperti us-east-1 — relevan kalau timeout polling Anda cukup ketat. Perhatikan juga UU Pelindungan Data Pribadi (UU 27/2022): pastikan data yang di-scrape adalah data harga publik yang memang ditampilkan ke semua pengunjung, bukan data pribadi, dan Anda punya otorisasi untuk mengaksesnya. Ini bukan nasihat hukum, sekadar pengingat praktik yang aman.

Implementasi Python: memantau harga sambil menangani CAPTCHA

Fungsi solve_captcha di bawah menjalankan alur submit-and-poll klasik CaptchaAI: kirim task ke in.php, simpan task_id, lalu polling res.php setiap 5 detik sampai token siap. fetch_with_captcha memakainya untuk dua kasus paling umum di halaman produk — reCAPTCHA lewat atribut data-sitekey, dan Cloudflare Turnstile lewat class cf-turnstile.

Pemantau Harga (Python)

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

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
BASE_URL = "https://ocr.captchaai.com"


def solve_captcha(method, params):
    params["key"] = API_KEY
    params["method"] = method

    resp = requests.get(f"{BASE_URL}/in.php", params=params)
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit failed: {resp.text}")

    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{BASE_URL}/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 failed: {result.text}")

    raise TimeoutError("CAPTCHA solve timed out")


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

    # Check for reCAPTCHA
    match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', resp.text)
    if match:
        site_key = match.group(1)
        token = solve_captcha("userrecaptcha", {
            "googlekey": site_key,
            "pageurl": url,
        })
        resp = session.post(url, data={"g-recaptcha-response": token})

    # Check for Turnstile
    match = re.search(
        r'class="cf-turnstile"[^>]*data-sitekey=["\']([^"\']+)', resp.text
    )
    if match:
        site_key = match.group(1)
        token = solve_captcha("turnstile", {
            "sitekey": site_key,
            "pageurl": url,
        })
        resp = session.post(url, data={"cf-turnstile-response": token})

    return resp


def extract_price(html, selectors):
    """Extract price from HTML using regex patterns."""
    for pattern in selectors:
        match = re.search(pattern, html)
        if match:
            price_str = match.group(1).replace(",", "")
            return float(price_str)
    return None


def monitor_prices(products):
    """Monitor prices for a list of products."""
    session = requests.Session()
    session.headers["User-Agent"] = (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/120.0.0.0"
    )

    results = []
    for product in products:
        try:
            resp = fetch_with_captcha(product["url"], session)
            price = extract_price(resp.text, product["selectors"])

            results.append({
                "name": product["name"],
                "url": product["url"],
                "price": price,
                "timestamp": datetime.utcnow().isoformat(),
                "status": "ok",
            })
            print(f"  {product['name']}: ${price}")

        except Exception as e:
            results.append({
                "name": product["name"],
                "url": product["url"],
                "price": None,
                "timestamp": datetime.utcnow().isoformat(),
                "status": f"error: {e}",
            })
            print(f"  {product['name']}: ERROR - {e}")

    return results


# Define products to monitor
products = [
    {
        "name": "Wireless Headphones",
        "url": "https://example.com/product/headphones",
        "selectors": [
            r'class="price"[^>]*>\$?([\d,]+\.?\d*)',
            r'itemprop="price" content="([\d.]+)"',
        ],
    },
    {
        "name": "Bluetooth Speaker",
        "url": "https://example.com/product/speaker",
        "selectors": [
            r'class="price"[^>]*>\$?([\d,]+\.?\d*)',
        ],
    },
]

print("Starting price check...")
results = monitor_prices(products)

# Save results
with open("prices.json", "w") as f:
    json.dump(results, f, indent=2)

monitor_prices menjalankan seluruh daftar produk, mencatat status per item, dan tetap menulis hasil ke prices.json walau sebagian request gagal — satu produk yang error tidak menghentikan seluruh batch.

Versi Node.js untuk tim yang stack-nya JavaScript

Logikanya identik dengan versi Python — submit ke in.php, polling res.php, pengaturan token ke request berikutnya — hanya dibungkus axios dan cheerio untuk parsing HTML sisi server:

Implementasi Node.js

const axios = require("axios");
const cheerio = require("cheerio");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

async function solveCaptcha(method, params) {
  params.key = API_KEY;
  params.method = method;

  const submit = await axios.get("https://ocr.captchaai.com/in.php", {
    params,
  });
  const taskId = String(submit.data).split("|")[1];

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId },
    });
    const text = String(poll.data);
    if (text === "CAPCHA_NOT_READY") continue;
    if (text.startsWith("OK|")) return text.split("|").slice(1).join("|");
    throw new Error(text);
  }
  throw new Error("Timeout");
}

async function monitorPrice(url) {
  const resp = await axios.get(url);
  const $ = cheerio.load(resp.data);

  // Check for reCAPTCHA
  const siteKey = $(".g-recaptcha").attr("data-sitekey");
  if (siteKey) {
    const token = await solveCaptcha("userrecaptcha", {
      googlekey: siteKey,
      pageurl: url,
    });
    // Re-fetch with token
    const formResp = await axios.post(url, { "g-recaptcha-response": token });
    return cheerio.load(formResp.data);
  }

  const price = $('[itemprop="price"]').attr("content") || $(".price").text();
  return parseFloat(price.replace(/[^0-9.]/g, ""));
}

Jadwalkan pengecekan otomatis

Untuk cek berkala setiap 30 menit, cron di server Linux paling sederhana:

# crontab -e
*/30 * * * * cd /opt/monitor && python price_monitor.py >> /var/log/prices.log 2>&1

Kalau Anda lebih suka menjaga logika penjadwalan di dalam kode Python — misalnya supaya gampang di-deploy sebagai satu proses long-running di container — library schedule adalah alternatifnya:

import schedule

schedule.every(30).minutes.do(lambda: monitor_prices(products))

while True:
    schedule.run_pending()
    time.sleep(60)

Berapa biaya menjalankan monitoring ini?

Biaya sebenarnya bergantung pada berapa banyak request yang benar-benar memicu CAPTCHA, bukan sekadar jumlah produk yang dipantau:

Volume CAPTCHA/Hari Estimasi Biaya Harian
50 produk, setiap 30 menit ~2.400 ~$2-5
200 produk, setiap 15 menit ~19.200 ~$15-30
1000 produk, setiap jam ~24.000 ~$20-40

Tidak semua pemuatan halaman memicu CAPTCHA, jadi biaya aktual biasanya 50-70% lebih rendah dari perkiraan di atas.

Angka pada tabel di atas adalah estimasi kasar berdasarkan volume request, bukan tarif resmi — sesuaikan dengan rasio CAPTCHA aktual yang Anda amati di platform yang dipantau.

Karena CaptchaAI menagih per thread — bukan per solve — angka ini juga menentukan paket yang paling pas: skenario 1000 produk per jam di atas biasanya cukup dengan STANDARD ($30/bulan, 15 thread) atau ADVANCE ($90/bulan, 50 thread), tergantung berapa banyak worker scraper yang berjalan bersamaan.

Pertanyaan umum

Bisakah saya memantau 1000 produk dengan paket BASIC?

Tergantung frekuensi. BASIC ($15/bulan, 5 thread) cukup untuk volume rendah dengan interval cek yang longgar; begitu Anda menaikkan concurrency worker atau mempersempit interval jadi tiap 15 menit, jumlah thread biasanya jadi batas lebih dulu daripada biaya — cocokkan volume Anda dengan tabel biaya di atas sebelum memilih paket.

Apakah CaptchaAI mendukung reCAPTCHA v3 untuk toko Shopify?

Ya. reCAPTCHA v3 termasuk tipe yang didukung penuh, dan ini relevan karena banyak toko Shopify memakainya tanpa tantangan visual sama sekali — token tetap diminta lewat API yang sama seperti reCAPTCHA v2.

Bagaimana cara tahu perubahan harga itu signifikan, bukan sekadar noise?

Bandingkan harga baru dengan nilai tersimpan terakhir, lalu set ambang batas. Alert hanya pada perubahan di atas 5% biasanya cukup untuk menyaring fluktuasi kecil seperti pembulatan atau diskon sementara.

Request saya masih diblokir walau CAPTCHA sudah diselesaikan, kenapa?

CAPTCHA yang solved menghapus satu hambatan, bukan semuanya. Kalau IP atau User-Agent Anda sudah masuk daftar hitam sebelumnya, rotasi proxy dan header tetap diperlukan, dan beri jarak antar-request daripada fetch berturutan supaya pola traffic tidak langsung terlihat otomatis.

Apakah scraping harga kompetitor melanggar UU PDP?

Selama Anda hanya mengambil data harga publik yang memang ditampilkan ke semua pengunjung — bukan data pribadi pelanggan atau data di balik login — risikonya rendah, tapi ini bukan pengganti nasihat hukum untuk kasus spesifik Anda.

Bacaan lanjutan

Komentar dinonaktifkan untuk artikel ini.