Os portais financeiros protegem cotações de ações, dados de lucros e relatórios de analistas com Cloudflare Turnstile e reCAPTCHA. CAPTCHAs são acionados durante pesquisas rápidas de símbolos, downloads de dados históricos e consultas de triagem. Veja como manter uma coleta de dados confiável em sites financeiros.
Padrões CAPTCHA em portais financeiros
| Tipo de dados | Exemplos de portais | Tipo CAPTCHA | Gatilho |
|---|---|---|---|
| Cotações em tempo real | Portais financeiros | Cloudflare Turnstile | Pesquisas rápidas de símbolos |
| Preços históricos | Provedores de dados | reCAPTCHA v2 | Downloads de CSV em massa |
| Demonstrações financeiras | Sites de arquivamento da SEC | Imagem CAPTCHA | Consultas EDGAR repetidas |
| Resultados da triagem | Analistas de estoque | Cloudflare Turnstile em staging | Consultas de filtro complexas |
| Avaliações de analistas | Portais de pesquisa | reCAPTCHA v3 | Várias visualizações de página |
Coletor de dados de estoque
import requests
import time
import re
from datetime import datetime, timedelta
class StockDataCollector:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def get_quote(self, portal_url, symbol):
"""Get current stock quote, solving CAPTCHAs if needed."""
url = f"{portal_url}/quote/{symbol}"
response = self.session.get(url)
if self._is_captcha_page(response):
response = self._solve_and_retry(response, url)
return self._parse_quote(response.text, symbol)
def get_historical(self, portal_url, symbol, days=365):
"""Download historical price data."""
url = f"{portal_url}/history/{symbol}"
params = {
"period": f"{days}d",
"interval": "1d"
}
response = self.session.get(url, params=params)
if self._is_captcha_page(response):
response = self._solve_and_retry(response, url)
return self._parse_historical(response.text)
def scan_symbols(self, portal_url, symbols, delay=2):
"""Collect quotes for multiple symbols."""
results = {}
for symbol in symbols:
try:
results[symbol] = self.get_quote(portal_url, symbol)
time.sleep(delay)
except Exception as e:
results[symbol] = {"error": str(e)}
return results
def _is_captcha_page(self, response):
return (
response.status_code == 403 or
"cf-turnstile" in response.text or
"challenges.cloudflare.com" in response.text
)
def _solve_and_retry(self, response, url):
match = re.search(r'data-sitekey="(0x[^"]+)"', response.text)
if not match:
# Fall back to reCAPTCHA detection
match = re.search(r'data-sitekey="([^"]+)"', response.text)
if match:
return self._solve_recaptcha_and_retry(match.group(1), url)
raise ValueError("No CAPTCHA sitekey found")
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "turnstile",
"sitekey": match.group(1),
"pageurl": url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return self.session.post(url, data={
"cf-turnstile-response": data["request"]
})
raise TimeoutError("CAPTCHA solve timed out")
def _solve_recaptcha_and_retry(self, site_key, url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return self.session.post(url, data={
"g-recaptcha-response": data["request"]
})
raise TimeoutError("reCAPTCHA solve timed out")
def _parse_quote(self, html, symbol):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
return {
"symbol": symbol,
"price": text_or_none(soup.select_one("[data-field='regularMarketPrice'], .price")),
"change": text_or_none(soup.select_one("[data-field='regularMarketChange'], .change")),
"volume": text_or_none(soup.select_one("[data-field='regularMarketVolume'], .volume")),
"market_cap": text_or_none(soup.select_one("[data-field='marketCap'], .market-cap")),
"timestamp": datetime.now().isoformat()
}
def _parse_historical(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
rows = []
for row in soup.select("table tr")[1:]: # Skip header
cells = [td.text.strip() for td in row.select("td")]
if len(cells) >= 6:
rows.append({
"date": cells[0],
"open": cells[1],
"high": cells[2],
"low": cells[3],
"close": cells[4],
"volume": cells[5]
})
return rows
# Usage
collector = StockDataCollector("YOUR_API_KEY")
# Single quote
quote = collector.get_quote("https://finance.example.com", "AAPL")
print(f"AAPL: ${quote['price']} ({quote['change']})")
# Scan multiple symbols
portfolio = collector.scan_symbols(
"https://finance.example.com",
["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"]
)
Analisador de mercado com tratamento CAPTCHA (JavaScript)
class MarketScreener {
constructor(apiKey) {
this.apiKey = apiKey;
}
async screenStocks(portalUrl, filters) {
const params = new URLSearchParams(filters);
const response = await fetch(`${portalUrl}/screener?${params}`);
const html = await response.text();
if (html.includes('cf-turnstile') || response.status === 403) {
return this.solveAndScreen(portalUrl, filters, html);
}
return this.parseScreenerResults(html);
}
async solveAndScreen(portalUrl, filters, html) {
const match = html.match(/data-sitekey="(0x[^"]+)"/);
if (!match) throw new Error('Turnstile sitekey not found');
const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
method: 'POST',
body: new URLSearchParams({
key: this.apiKey,
method: 'turnstile',
sitekey: match[1],
pageurl: portalUrl,
json: '1'
})
});
const { request: taskId } = await submitResp.json();
for (let i = 0; i < 60; i++) {
await new Promise(r => setTimeout(r, 3000));
const result = await fetch(
`https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
);
const data = await result.json();
if (data.status === 1) {
const response = await fetch(`${portalUrl}/screener`, {
method: 'POST',
body: new URLSearchParams({
...filters,
'cf-turnstile-response': data.request
})
});
return this.parseScreenerResults(await response.text());
}
}
throw new Error('Turnstile solve timed out');
}
parseScreenerResults(html) {
const rows = [];
const tableMatch = html.match(/<table[^>]*>[\s\S]*?<\/table>/i);
if (!tableMatch) return rows;
const rowMatches = tableMatch[0].matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi);
for (const row of rowMatches) {
const cells = [...row[1].matchAll(/<td[^>]*>([\s\S]*?)<\/td>/gi)]
.map(m => m[1].replace(/<[^>]+>/g, '').trim());
if (cells.length >= 4) {
rows.push({
symbol: cells[0],
price: cells[1],
change: cells[2],
volume: cells[3]
});
}
}
return rows;
}
}
// Usage
const screener = new MarketScreener('YOUR_API_KEY');
const results = await screener.screenStocks('https://finance.example.com', {
sector: 'technology',
marketCap: 'large',
peRatio: '<25'
});
Frequência de coleta por tipo de dados
| Tipo de dados | Intervalo recomendado | Frequência CAPTCHA |
|---|---|---|
| Cotações em tempo real | 1–5 minutos | Alto – use API, se disponível |
| Preços de fim de dia | Uma vez por dia após fechar | Baixo |
| Demonstrações financeiras | Trimestralmente | Mínimo |
| Resultados da triagem | Diariamente | Moderado |
| Avaliações de analistas | Semanalmente | Baixo |
Solução de problemas
| Problema | Causa | Correção |
|---|---|---|
| Cloudflare Turnstile em cada solicitação | Nova sessão a cada vez | Persistir cookies em solicitações |
| Dados históricos incompletos | Paginação por trás do CAPTCHA | Resolva por página, siga links paginados |
| Cotar dados obsoletos | Resposta em cache veiculada | Adicionar parâmetro de consulta para impedir cache |
| Limite de taxa 429 | Muitos pedidos | Aumente o atraso, gire os proxies |
Perguntas frequentes
As APIs financeiras são mais adequado do que a raspagem com CAPTCHAs?
APIs gratuitas (como os níveis básicos de alguns provedores) cobrem dados comuns, mas têm limites de taxa. coleta autorizada com CaptchaAI dá acesso a dados não disponíveis por meio de APIs – filtros de triagem, comentários de analistas e dados financeiros de nicho.
Como lidar com CAPTCHAs de cotação em tempo real sem demora?
Pré-autentique sua sessão durante o horário de mercado. O cookie cookie_qa_validacao da Cloudflare dura de 15 a 30 minutos, então resolva uma vez e faça várias solicitações nessa janela.
Posso coletar dados de vários portais financeiros simultaneamente?
Sim. Use sessões separadas por portal com envios de tarefas CaptchaAI independentes. CaptchaAI lida com soluções simultâneas em diferentes sites.
Artigos relacionados
- Coleta de dados de pesquisa de mercado
- Comparação de Geetest vs Cloudflare Turnstile
- Cloudflare Turnstile 403 Após correção do token
Próximas etapas
Colete dados de mercado de forma confiável -obtenha sua chave API CaptchaAIe lidar automaticamente com CAPTCHAs de portais financeiros.