Trường Hợp Sử Dụng

Quét dữ liệu tài chính bằng cách xử lý CAPTCHA

SEC EDGAR, Yahoo Finance, Bloomberg và Finviz đều bật CAPTCHA ngay khi phát hiện request tần suất cao từ cùng một IP — script chạy êm buổi sáng có thể dừng hẳn buổi chiều. Gắn bước giải reCAPTCHA v2 hoặc Cloudflare Turnstile qua CaptchaAI vào pipeline, request tiếp tục chạy mà không cần giải captcha thủ công. Nhiều đội outsourcing tại TP.HCM và Hà Nội xây dashboard chứng khoán cho khách hàng nước ngoài đang dùng cách này để giữ pipeline ổn định.


CAPTCHA xuất hiện ở đâu trong dữ liệu tài chính

  • SEC EDGAR — reCAPTCHA v2, kích hoạt khi request tần suất cao; bảo vệ hồ sơ công ty.
  • Yahoo Finance — reCAPTCHA v2, kích hoạt khi phát hiện hành vi scraping; bảo vệ báo giá và lịch sử giá.
  • Bloomberg — Cloudflare Turnstile, áp dụng cho mọi truy cập tự động; bảo vệ dữ liệu thị trường.
  • Finviz — reCAPTCHA v2, kích hoạt khi truy cập công cụ sàng lọc cổ phiếu.
  • TradingView — Cloudflare Challenge, kích hoạt theo giới hạn tần suất; bảo vệ biểu đồ và chỉ số.
  • Morningstar — reCAPTCHA v3, kích hoạt trên trang xuất dữ liệu; bảo vệ phân tích quỹ.

Thu thập dữ liệu từ công cụ sàng lọc cổ phiếu

  • Tìm data-sitekey trong HTML khi CAPTCHA xuất hiện trên trang sàng lọc.
  • Giải qua CaptchaAI rồi submit lại token trong request tiếp theo.
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:[email protected]: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']})")

Trích xuất hồ sơ SEC EDGAR

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:[email protected]:5000",
)
filings = sec.search_filings("Apple Inc", "10-K")

Dữ liệu thị trường được bảo vệ bằng Cloudflare Turnstile

  • Bloomberg chặn toàn bộ truy cập tự động bằng Cloudflare Turnstile thay vì reCAPTCHA.
  • Token cf-turnstile-response được submit lại như một field form bình thường.
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

Lên lịch thu thập dữ liệu thị trường hằng ngày

Với danh sách ticker cố định, chạy theo lịch hằng ngày ổn định hơn hẳn so với gọi liên tục trong ngày:

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:[email protected]: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)

Vận hành ổn định: lỗi thường gặp và giới hạn tần suất

Lỗi thường gặp

Vấn đề Nguyên nhân Cách xử lý
Lỗi 403 trên SEC EDGAR Thiếu User-Agent kèm email Thêm header dạng CompanyName email@domain
CAPTCHA xuất hiện ở mọi request Vượt giới hạn tần suất Thêm độ trễ 3-5 giây giữa các request
Dữ liệu giá bị cũ Response đến từ cache Thêm query parameter xoá cache
Lỗi parse JSON Server trả về trang CAPTCHA thay vì JSON Kiểm tra CAPTCHA trước khi parse response
IP bị chặn Quá nhiều request từ cùng một địa chỉ IP Đa dạng hoá nguồn gửi request, tránh dồn về một IP duy nhất

Giới hạn tần suất request

Thực hành Khuyến nghị
Độ trễ giữa các request 2-5 giây giữa các trang
Kết nối đồng thời Tối đa 3-5 mỗi domain
Chọn proxy Ưu tiên IP ổn định, tránh dải IP datacenter bị đánh dấu
Thời lượng phiên Duy trì một phiên ổn định trong 5-10 phút
User-Agent Thực tế, giữ nguyên trong cùng một phiên
SEC EDGAR Bắt buộc kèm email liên hệ trong User-Agent
Khung giờ chạy Ưu tiên giờ thấp điểm ngoài giờ giao dịch
Log & dữ liệu cá nhân Nếu log kèm email nhà đầu tư, giới hạn thu thập theo Nghị định 13/2023/NĐ-CP dù dữ liệu gốc công khai

Câu hỏi thường gặp

CaptchaAI giải được những CAPTCHA nào trên các nền tảng tài chính?

Có — reCAPTCHA v2 (SEC EDGAR, Yahoo Finance, Finviz), Cloudflare Turnstile (Bloomberg), Cloudflare Challenge (TradingView) và reCAPTCHA v3 (Morningstar) đều được hỗ trợ qua cùng một API.

Thu thập dữ liệu tài chính công khai có hợp pháp không?

Phần lớn được phép — hồ sơ SEC, báo giá cổ phiếu là dữ liệu công khai. Tôn trọng điều khoản dịch vụ và giới hạn tần suất; SEC EDGAR còn khuyến khích truy cập cho mục đích nghiên cứu.

Thu thập dữ liệu vài trăm mã cổ phiếu mỗi ngày cần gói nào?

Tuỳ số request chạy song song, không phải số mã cổ phiếu — CaptchaAI tính phí theo thread, không theo lượt giải. BASIC ($15/tháng, 5 thread) đủ cho pipeline nhỏ; scale lên STANDARD ($30/tháng, 15 thread) hoặc ADVANCE ($90/tháng, 50 thread) khi chạy nhiều ticker song song.

Vì sao các nền tảng tài chính chặn bot gắt hơn các trang khác?

Trích xuất tự động số lượng lớn có thể bị lợi dụng để thao túng thị trường, thu thập lợi thế cạnh tranh hoặc gây quá tải máy chủ — nên CAPTCHA ở đây gắt hơn một trang bán lẻ thông thường.


Tài nguyên liên quan


Giữ pipeline dữ liệu tài chính chạy liên tục, không bị CAPTCHA làm gián đoạn — lấy API key CaptchaAI và tự động hoá bước giải token ngay trong request tiếp theo.

Os comentários estão desativados para este artigo.