Finansal veri toplayan bir otomasyon yazıyorsanız asıl darboğaz HTML'i ayrıştırmak değildir; SEC EDGAR, hisse tarama araçları ve piyasa platformlarının araya soktuğu reCAPTCHA ve Turnstile doğrulamalarıdır. CaptchaAI bu doğrulamaları API üzerinden çözer, böylece boru hattınız bir insanı beklemek zorunda kalmadan çalışmaya devam eder. Bu rehber, bir hisse tarama sayfasından SEC başvurularına ve Turnstile korumalı piyasa uç noktalarına kadar tipik bir finansal kazıma iş akışını uçtan uca gösterir.
Türk geliştiriciler için ek bir avantaj var: CaptchaAI fiyatlandırması USD ve thread bazlıdır; kur dalgalanmasından bağımsız, aylık öngörülebilir bir maliyet anlamına gelir. Küçük bir hisse listesini günde bir kez tarıyorsanız BASIC ($15/ay, 5 thread) yeterli olur.
Finansal sitelerde CAPTCHA nerede karşınıza çıkar
Aşağıdaki tablo, finansal kaynaklarda en sık karşılaşacağınız doğrulama türlerini ve bunları neyin tetiklediğini özetler. Kaynağa göre yaklaşımınızı buna göre planlayın.
| Kaynak | CAPTCHA türü | Tetikleyici | Veri değeri |
|---|---|---|---|
| SEC EDGAR | reCAPTCHA v2 | Yüksek hacimli istekler | Şirket başvuruları |
| Yahoo Finance | reCAPTCHA v2 | Kazıma tespiti | Hisse fiyatları, geçmiş |
| Bloomberg | Cloudflare Turnstile | Tüm otomatik erişim | Piyasa verileri |
| Finviz | reCAPTCHA v2 | Screener erişimi | Tarama sonuçları |
| TradingView | Cloudflare doğrulama akışı | Hız sınırı | Grafikler, göstergeler |
| Morningstar | reCAPTCHA v3 | Veri dışa aktarma sayfaları | Fon analitiği |
CaptchaAI bu tablodaki türlerin tamamını çözer:
- reCAPTCHA v2 (görünmez varyant dahil) ve reCAPTCHA v3
- Cloudflare Turnstile
- Cloudflare doğrulama akışı
hCaptcha ve FunCaptcha ise henüz desteklenmez; bir kaynak bu türlerden birini kullanıyorsa iş akışınızı ona göre planlayın.
Hisse tarama (screener) sayfalarını kazıma
En yaygın senaryo bir screener sayfasıdır: sayfayı çekersiniz, data-sitekey varsa reCAPTCHA'yı çözersiniz ve token'ı geri gönderirsiniz. Aşağıdaki sınıf, tetiklenen bir CAPTCHA'yı otomatik olarak ele alarak tablo satırlarını ayrıştırır.
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']})")
solve_captcha yardımcı fonksiyonu tüm örneklerde ortaktır: görevi in.php uç noktasına gönderir, sonucu res.php üzerinden CAPCHA_NOT_READY dönmeyene kadar sorgular ve token'ı döndürür.
SEC EDGAR başvurularını çekme
SEC EDGAR yüksek hacimli erişimde hem hız sınırı hem de CAPTCHA uygular ve ek olarak sizi tanımlayan bir User-Agent (iletişim e-postası dahil) ister. Bu başlık eksikse istekleriniz 403 döner. Aşağıdaki sınıf 403 veya CAPTCHA belirtisi gördüğünde reCAPTCHA'yı çözüp isteği yineler.
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")
SEC verisi kamuya açık olsa da, EDGAR'ın erişim kurallarına ve User-Agent gereksinimine uymak hem nezaket hem de engellenmemenin koşuludur.
Turnstile korumalı piyasa verilerini çözme
Bloomberg gibi bazı kaynaklar reCAPTCHA yerine Cloudflare Turnstile kullanır. Akış aynıdır; yalnızca method olarak turnstile geçer ve token'ı cf-turnstile-response alanında geri gönderirsiniz.
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
Her CAPTCHA türü için doğru token alanını kullanmak kritiktir: reCAPTCHA'da g-recaptcha-response, Turnstile'da cf-turnstile-response. Yanlış alan, geçerli bir token'ı bile reddettirir.
Zamanlanmış günlük veri toplama
Tek tek istekler yerine çoğu ekip günlük bir anlık görüntü işi kurar: bir sembol listesini dolaşır, her biri için screener'ı çağırır ve sonucu CSV'ye yazar. İstekler arasına gecikme koymak, CAPTCHA tetiklenmesini belirgin biçimde azaltır.
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)
Bu işi bir zamanlayıcıya (cron veya bir görev kuyruğu) bağlayıp Europe/Istanbul saatine göre yoğun olmayan bir saatte çalıştırabilirsiniz.
Hız sınırlarına takılmadan çalışma
Finansal siteler otomatik erişime diğer sektörlerden daha katıdır. Aşağıdaki alışkanlıklar, hem engellenme hem de gereksiz CAPTCHA tetiklenmesini azaltır.
| Konu | Öneri |
|---|---|
| İstekler arası gecikme | Sayfalar arasında 2–5 saniye |
| Eşzamanlı bağlantı | Alan başına en fazla 3–5 |
| Proxy yapılandırması | Kararlı bir proxy havuzu kullanın |
| Oturum uzunluğu | Kısa oturumlar, oturum başına tutarlı IP |
| User-Agent | Gerçekçi ve oturum boyunca tutarlı |
| SEC EDGAR | User-Agent içine iletişim e-postasını ekleyin (zorunlu) |
| Piyasa saatleri | Mümkün oldukça yoğun olmayan saatlerde toplayın |
KVKK, yasal çerçeve ve maliyet planlama
Kamuya açık finansal veri (SEC başvuruları, hisse fiyatları) genellikle toplanabilir; yine de her kaynağın hizmet şartlarına ve hız sınırlarına uymak gerekir. Topladığınız veri kişisel veri içeriyorsa Türkiye'de KVKK (Kişisel Verilerin Korunması Kanunu) kapsamına girer; bu tür veriyi yalnızca yetkili ve amaca bağlı iş akışları içinde işleyin.
Maliyet tarafında thread bazlı model işi kolaylaştırır: her plan thread başına sınırsız çözüm içerir, yani solve başına ücret ya da günlük kota yoktur. İş yükünüze göre planı şöyle seçebilirsiniz:
- BASIC ($15/ay, 5 thread): birkaç sembollü listeyi günde bir kez tarayan işler için fazlasıyla yeterli.
- STANDARD ($30/ay, 15 thread): orta ölçekli, düzenli çalışan boru hatları.
- ADVANCE ($90/ay, 50 thread): onlarca sembolü paralel tarayan geniş evrenler.
Fiyatlar USD ve thread bazlı olduğu için aylık maliyetiniz kur dalgalanmasından etkilenmez.
Sorun giderme
| Sorun | Sebep | Çözüm |
|---|---|---|
| SEC EDGAR'da 403 | E-posta içeren User-Agent eksik |
CompanyName email@domain başlığını ekleyin |
| Her istekte CAPTCHA | Hız sınırı aşıldı | İstekler arasına 3–5 saniye gecikme koyun |
| Eski fiyat verisi | Önbelleğe alınmış yanıt | Önbellek bozan bir sorgu parametresi ekleyin |
| JSON ayrıştırma hatası | Yanıt olarak CAPTCHA sayfası döndü | Ayrıştırmadan önce CAPTCHA olup olmadığını kontrol edin |
| IP engellendi | Aynı IP'den çok fazla istek | Çıkış IP'sini değiştirin, istek hızını düşürün |
Sık sorulan sorular
CaptchaAI hangi finansal site CAPTCHA türlerini çözer?
reCAPTCHA v2 (görünmez varyant dahil), reCAPTCHA v3, Cloudflare Turnstile ve Cloudflare doğrulama akışını çözer. hCaptcha ve FunCaptcha henüz desteklenmez; bir kaynak bunlardan birini kullanıyorsa iş akışınızı buna göre planlayın.
Günlük hisse verisi toplamak için hangi plan yeterli?
Küçük bir sembol listesini günde bir kez tarıyorsanız BASIC ($15/ay, 5 thread) yeterlidir. Daha geniş bir evreni paralel tarayacaksanız STANDARD ($30/ay, 15 thread) ya da ADVANCE ($90/ay, 50 thread) eşzamanlı yükü karşılar; tüm planlarda thread başına çözüm sınırsızdır.
Her istekte CAPTCHA çıkıyorsa ne yapmalıyım?
Bu neredeyse her zaman hız sınırının aşıldığının işaretidir. İstekler arasına 3–5 saniye gecikme koyun, eşzamanlılığı düşürün ve User-Agent başlığını oturum boyunca tutarlı tutun. Yükü zamana yayınca CAPTCHA sıklığı belirgin biçimde düşer.
Finansal veri kazımak Türkiye'de yasal mı?
Kamuya açık veride genellikle sorun yoktur, ancak kaynağın hizmet şartlarına ve hız sınırlarına uymalısınız. Veri kişisel bilgi içeriyorsa KVKK yükümlülükleri devreye girer; toplamayı yetkili ve amaca bağlı bir kapsamda tutun.
İlgili rehberler
Finansal veri boru hattınızı CAPTCHA kesintisi olmadan çalıştırın —CaptchaAI API anahtarınızı alınve piyasa araştırmasını otomatikleştirin.