Use Cases

Scraping Data Keuangan dengan Penanganan CAPTCHA

Begitu volume request Anda naik, scraping stock screener, filing SEC EDGAR, atau feed harga saham hampir pasti akan menabrak reCAPTCHA v2 atau Cloudflare Turnstile. Jalan keluarnya langsung: serahkan setiap challenge ke API CaptchaAI, ambil token hasilnya, lalu kirim ulang request dengan token tersebut — seluruhnya dari dalam skrip Python Anda, tanpa satu pun klik manual.

Pola integrasinya sama di semua contoh di bawah ini, dan selalu empat langkah:

  1. Kirim challenge ke in.php beserta sitekey dan URL halaman.
  2. Simpan task ID yang dikembalikan.
  3. Lakukan polling ke res.php sampai token siap.
  4. Pakai token pada request asli.

Untuk operator data yang menjalankan riset pasar harian — termasuk banyak tim scraping freelance dan agensi price-monitoring di Indonesia — pola inilah yang membuat pipeline tetap jalan tanpa diawasi.


Di mana CAPTCHA muncul di data keuangan

Tiap sumber memicu challenge dengan cara berbeda, dan tahu tipe apa yang Anda hadapi menentukan method mana yang dikirim ke API:

Sumber Tipe CAPTCHA Pemicu Nilai data
SEC EDGAR reCAPTCHA v2 Request volume tinggi Filing perusahaan
Yahoo Finance reCAPTCHA v2 Deteksi scraping Harga saham, riwayat
Bloomberg Cloudflare Turnstile Semua akses otomatis Data pasar
Finviz reCAPTCHA v2 Akses stock screener Hasil screener
TradingView Cloudflare Challenge Rate limiting Chart, indikator
Morningstar reCAPTCHA v3 Halaman ekspor data Analisis reksa dana

Semua tipe di kolom itu termasuk yang didukung CaptchaAI, jadi satu integrasi menutup hampir seluruh sumber:

  • reCAPTCHA v2 — SEC EDGAR, Yahoo Finance, Finviz
  • reCAPTCHA v3 — halaman ekspor bergaya Morningstar
  • Cloudflare Turnstile dan Cloudflare Challenge — Bloomberg, TradingView

Scraping stock screener

Fungsi solve_captcha di bawah adalah inti dari semua contoh: ia mengirim challenge, melakukan polling sampai token siap, lalu mengembalikannya. Kelas FinancialScraper mendeteksi data-sitekey di halaman, menyelesaikan reCAPTCHA v2, dan mengirim ulang request dengan g-recaptcha-response:

import requests
import time
from bs4 import BeautifulSoup
import re

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY,
        "method": method,
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }
    data.update(kwargs)

    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]

    raise TimeoutError("Solve timeout")


class FinancialScraper:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def scrape_screener(self, url):
        """Scrape stock screener, handling CAPTCHA if triggered."""
        resp = self.session.get(url, timeout=30)

        # Check for CAPTCHA
        sitekey_match = re.search(r'data-sitekey="([^"]+)"', resp.text)
        if sitekey_match:
            sitekey = sitekey_match.group(1)
            token = solve_captcha("userrecaptcha", sitekey, url)

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

        return self._parse_stocks(resp.text)

    def _parse_stocks(self, html):
        soup = BeautifulSoup(html, "html.parser")
        stocks = []
        for row in soup.select("table.screener-table tr")[1:]:
            cols = row.select("td")
            if len(cols) >= 8:
                stocks.append({
                    "ticker": cols[1].get_text(strip=True),
                    "company": cols[2].get_text(strip=True),
                    "sector": cols[3].get_text(strip=True),
                    "price": cols[6].get_text(strip=True),
                    "change": cols[7].get_text(strip=True),
                })
        return stocks


# Usage
scraper = FinancialScraper(
    proxy="http://user:pass@residential.proxy.com:5000"
)
stocks = scraper.scrape_screener("https://screener.example.com/screener.ashx?v=111")
for stock in stocks[:5]:
    print(f"{stock['ticker']}: {stock['price']} ({stock['change']})")

Perhatikan bahwa deteksi CAPTCHA bersifat bersyarat: selama request belum ditandai, skrip mem-parsing tabel secara langsung dan tidak memanggil API sama sekali — jadi Anda hanya memakai thread saat benar-benar dibutuhkan.


Ekstraksi filing SEC EDGAR

SEC EDGAR menerapkan rate limiting dan CAPTCHA untuk akses volume tinggi, dan mewajibkan User-Agent yang mengidentifikasi Anda lengkap dengan email kontak. Tanpa header itu, Anda akan menerima 403 sebelum CAPTCHA pun muncul:

import json


class SECFilingScraper:
    BASE_URL = "https://efts.sec.gov/LATEST"

    def __init__(self, user_agent_email, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        # SEC requires identifying User-Agent
        self.session.headers.update({
            "User-Agent": f"CompanyName admin@{user_agent_email}",
            "Accept": "application/json",
        })

    def search_filings(self, company, filing_type="10-K"):
        """Search EDGAR for specific filing types."""
        url = f"{self.BASE_URL}/search-index"
        params = {
            "q": company,
            "dateRange": "custom",
            "forms": filing_type,
        }

        resp = self.session.get(url, params=params, timeout=30)

        # Handle CAPTCHA if triggered
        if "captcha" in resp.text.lower() or resp.status_code == 403:
            sitekey = self._extract_sitekey(resp.text)
            if sitekey:
                token = solve_captcha("userrecaptcha", sitekey, url)
                resp = self.session.post(url, data={
                    **params,
                    "g-recaptcha-response": token,
                })

        return resp.json() if resp.status_code == 200 else {}

    def download_filing(self, filing_url):
        """Download individual filing document."""
        resp = self.session.get(filing_url, timeout=60)
        if resp.status_code == 200:
            return resp.text
        return None

    def _extract_sitekey(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        return match.group(1) if match else None


# Usage
sec = SECFilingScraper(
    user_agent_email="example.com",
    proxy="http://user:pass@proxy.example.com:5000",
)
filings = sec.search_filings("Apple Inc", "10-K")

Karena skrip hanya memanggil solve_captcha ketika status 403 atau kata "captcha" muncul di respons, filing yang lolos tanpa challenge tetap gratis dari sisi solve.


Data pasar di balik Cloudflare Turnstile

Situs seperti Bloomberg memasang Cloudflare Turnstile pada seluruh akses otomatis. Alurnya sama, hanya method-nya berganti menjadi turnstile dan field token yang dikirim ulang menjadi cf-turnstile-response:

def scrape_turnstile_market_data(url, sitekey):
    """Handle Cloudflare Turnstile on financial data sites."""
    token = solve_captcha("turnstile", sitekey, url)

    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
    })

    resp = session.post(url, data={
        "cf-turnstile-response": token,
    }, timeout=30)

    return resp.json() if resp.status_code == 200 else None

Snapshot pasar harian terjadwal

Riset pasar jarang berhenti di satu request. Fungsi berikut membungkus scraper ke dalam loop harian: menjelajahi daftar ticker, memberi jeda antar-request, dan menulis hasilnya ke CSV bertanggal — cocok dijalankan sebagai cron job:

import csv
from datetime import datetime


def daily_market_snapshot(tickers, output_dir="data"):
    """Collect daily stock data, handling CAPTCHAs automatically."""
    scraper = FinancialScraper(
        proxy="http://user:pass@residential.proxy.com:5000"
    )

    date_str = datetime.now().strftime("%Y-%m-%d")
    results = []

    for ticker in tickers:
        url = f"https://screener.example.com/quote.ashx?t={ticker}"
        try:
            data = scraper.scrape_screener(url)
            if data:
                results.extend(data)
            time.sleep(2)  # Rate limit
        except Exception as e:
            print(f"Error on {ticker}: {e}")

    # Save to CSV
    filepath = f"{output_dir}/market_{date_str}.csv"
    with open(filepath, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["ticker", "company", "sector", "price", "change"])
        writer.writeheader()
        writer.writerows(results)

    print(f"Saved {len(results)} records to {filepath}")
    return results


# Run daily
tickers = ["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"]
daily_market_snapshot(tickers)

Dua hal yang perlu dijaga saat menjadikannya cron job:

  • Pertahankan time.sleep antar-request meski Anda men-deploy dekat pasar, misalnya AWS ap-southeast-3 (Jakarta) — kedekatan region bukan alasan untuk membanjiri sumber data.
  • Bungkus tiap ticker dalam try/except seperti di atas agar satu kegagalan tidak menghentikan seluruh batch harian.

Aturan rate limiting untuk situs keuangan

Situs keuangan jauh lebih ketat terhadap akses otomatis dibanding situs biasa. Beberapa pedoman yang menjaga pipeline tetap sehat:

Praktik Rekomendasi
Delay antar request 2–5 detik per halaman
Concurrent connection Maks 3–5 per domain
Tipe egress IP residensial atau ISP
Durasi sesi Sesi sticky 5–10 menit
User-Agent Realistis, konsisten per sesi
SEC EDGAR Sertakan email kontak di UA (wajib)
Jam pasar Scrape di luar jam sibuk bila memungkinkan

Satu catatan kepatuhan yang relevan di Indonesia: UU Pelindungan Data Pribadi (UU 27/2022) mengatur pemrosesan data pribadi, jadi batasi scraping pada data pasar publik dan hindari data pribadi yang bukan hak Anda.


Pemecahan masalah

Sebagian besar kegagalan di pipeline keuangan bukan soal solving, melainkan header atau ritme request. Tabel ini memetakan gejala yang paling sering muncul ke perbaikannya:

Masalah Penyebab Perbaikan
403 di SEC EDGAR User-Agent tanpa email tidak ada Tambahkan header CompanyName email@domain
CAPTCHA di setiap request Rate limit terlampaui Tambahkan delay 3–5 detik antar request
Data harga basi Respons ter-cache Tambahkan query parameter cache-bust
JSON parse error Halaman CAPTCHA yang dikembalikan Cek CAPTCHA sebelum parsing
IP diblokir Terlalu banyak request dari IP yang sama Beralih ke rotating egress jaringan yang diotorisasi

Pertanyaan umum

Berapa thread yang saya butuhkan untuk scraping data keuangan harian?

Tergantung berapa banyak challenge yang jalan bersamaan. CaptchaAI menagih per thread, bukan per solve, dengan solve tak terbatas per thread. Untuk snapshot beberapa ratus ticker sehari, paket BASIC ($15/bulan, 5 thread) biasanya cukup; naik ke STANDARD ($30/bulan, 15 thread) hanya jika Anda memparalelkan banyak sumber sekaligus.

Apakah CaptchaAI mendukung reCAPTCHA v3 di halaman ekspor seperti Morningstar?

Ya. reCAPTCHA v3 termasuk tipe yang didukung, jadi halaman ekspor data yang memakai v3 dapat ditangani dengan alur yang sama — hanya method dan parameter score-nya yang berbeda dari v2.

Bagaimana cara menghindari error 403 di SEC EDGAR?

403 di EDGAR hampir selalu berarti User-Agent Anda tidak menyertakan email kontak yang diwajibkan SEC. Set header User-Agent ke format CompanyName email@domain sebelum request pertama; ini menyelesaikan mayoritas blokir sebelum CAPTCHA sekalipun ikut bermain.

Apakah boleh melakukan scraping data keuangan?

Data keuangan publik seperti filing SEC dan harga saham umumnya boleh diakses, dan SEC EDGAR secara eksplisit menyediakan akses untuk tujuan penelitian. Hormati terms of service serta rate limit tiap situs, dan sesuai UU 27/2022 hindari mengumpulkan data pribadi yang tidak Anda miliki haknya.


Panduan terkait

  • Rotating egress jaringan yang diotorisasi untuk penyelesaian CAPTCHA

Kumpulkan data pasar tanpa terhenti oleh CAPTCHA — ambil API key CaptchaAI Anda dan jalankan riset pasar secara otomatis.

Komentar dinonaktifkan untuk artikel ini.