Use Cases

Парсинг финансовых данных с обработкой CAPTCHA

Скринер акций возвращает reCAPTCHA вместо таблицы котировок, SEC EDGAR обрывает сессию кодом 403 при частых запросах, а Bloomberg заворачивает весь автоматический трафик за Cloudflare Turnstile. Для любого, кто парсит биржевые данные скриптом, это не исключение, а норма: финансовые площадки одними из первых внедряют строгую защиту от ботов. CaptchaAI решает эти проверки через API — ниже показано, на каких сервисах они встречаются и как встроить решение CAPTCHA в парсер, не превращая его в набор костылей.


На каких финансовых сервисах вы встретите CAPTCHA

Источник Тип CAPTCHA Что запускает проверку Ценность данных
SEC EDGAR reCAPTCHA v2 Большой объём запросов Отчётность компаний
Yahoo Finance reCAPTCHA v2 Обнаружение парсинга Котировки акций, история
Bloomberg Cloudflare Turnstile Весь автоматический трафик Рыночные данные
Finviz reCAPTCHA v2 Доступ к скринеру акций Результаты скрининга
TradingView Cloudflare Challenge Ограничение частоты запросов Графики, индикаторы
Morningstar reCAPTCHA v3 Страницы экспорта данных Аналитика фондов

Если вы снимаете котировки по 500–1000 тикерам в день, пяти потоков плана BASIC ($15/мес) обычно достаточно: один поток занят одной CAPTCHA, а число решений на поток не ограничено, поэтому весь список проходит без доплат за объём. Когда нужно опрашивать несколько бирж параллельно и расписание становится плотнее, есть смысл перейти на STANDARD ($30/мес, 15 потоков).


Прежде чем писать парсер: правила частоты запросов для финансовых сайтов

Финансовые площадки строже относятся к автоматическому доступу, чем большинство сайтов, поэтому эти правила стоит заложить в код скрейпера с самого начала, а не добавлять после первой блокировки:

  • Задержка между запросами — 2–5 секунд между страницами.
  • Параллельные соединения — максимум 3–5 на домен.
  • Тип прокси — резидентный или ISP.
  • Длительность сессии — закреплённые (sticky) сессии по 5–10 минут.
  • User-Agent — реалистичный, единый в рамках сессии.
  • SEC EDGAR — указывайте контактный email в User-Agent (обязательно).
  • Часы торгов — по возможности парсите вне пиковых часов.

С этими вводными переходим к коду.


Парсинг скринера акций: где сработает CAPTCHA

Ниже — рабочий класс на Python, который парсит скринер акций и решает reCAPTCHA v2, если она появляется в ответе:

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']})")

Выгрузка документов из SEC EDGAR

SEC EDGAR ограничивает частоту запросов и подключает CAPTCHA при аномально высоком трафике:

Прежде чем автоматизировать выгрузку из EDGAR в промышленных масштабах, стоит свериться и с требованиями к обработке данных на своей стороне — например, 152-ФЗ «О персональных данных» для читателей из РФ или GDPR-совместимая осмотрительность для остальных: собирайте только то, что вправе обрабатывать, и не путайте публичные рыночные данные с персональными данными инвесторов.

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")

Рыночные данные за Cloudflare Turnstile

Cloudflare Turnstile обычно решается быстрее чем за 10 секунд — заметно быстрее, чем reCAPTCHA v2 (до 60 секунд при высокой конкурентности запросов), поэтому для сайтов вроде Bloomberg он меньше сказывается на общей скорости парсинга:

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

Регулярный сбор данных по расписанию

Для ежедневного среза по списку тикеров используйте обёртку с паузами между запросами — так CAPTCHA срабатывает реже:

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)

Диагностика типичных ошибок

Проблема Причина Решение
403 на SEC EDGAR Нет User-Agent с email Добавьте заголовок вида CompanyName email@domain
CAPTCHA на каждый запрос Превышен лимит частоты запросов Добавьте задержку 3–5 секунд между запросами
Устаревшие цены в ответе Закэшированный ответ Добавьте параметр запроса для сброса кеша
Ошибка парсинга JSON Вместо данных вернулась страница с CAPTCHA Проверяйте наличие CAPTCHA до парсинга ответа
IP заблокирован Слишком много запросов с одного IP Переключитесь на ротацию резидентных прокси

Часто задаваемые вопросы

Сколько потоков нужно для сбора данных по одной бирже в реальном времени?

Для одного тикера в минуту обычно хватает 1–2 занятых потоков — reCAPTCHA v2 решается в пределах минуты, Cloudflare Turnstile заметно быстрее. Для параллельного опроса 20–30 тикеров закладывайте план STANDARD (15 потоков) или ADVANCE ($90/мес, 50 потоков) — с запасом на пиковые часы торгов.

Что делать, если ответ вместо JSON возвращает HTML-страницу с CAPTCHA?

Значит, защита сайта сработала раньше, чем вы ожидали. Проверяйте Content-Type и наличие data-sitekey в ответе до того, как передавать тело в JSON-парсер, — так вы отличите настоящий ответ API от страницы проверки и сразу отправите её на решение через CaptchaAI, вместо того чтобы ловить исключение парсинга.

Разрешено ли автоматически собирать данные SEC EDGAR и биржевые котировки?

Публичные данные — отчётность SEC, рыночные котировки — как правило, можно собирать, если вы соблюдаете условия обслуживания сайта и лимиты частоты запросов. SEC EDGAR прямо разрешает автоматический доступ для исследовательских целей при условии корректного User-Agent с контактным email.

Как часто можно опрашивать скринер акций, не вызывая CAPTCHA на каждый запрос?

Для цен акций — не чаще одного запроса в минуту в часы торгов. Для отчётности обычно достаточно раз в день. Более частый опрос заметно повышает шанс получить CAPTCHA на каждой странице.


Связанные руководства


Собирайте финансовые данные без остановок на CAPTCHA — оформите API-ключ CaptchaAI и автоматизируйте сбор рыночных данных.

Комментарии для этой статьи отключены.