Kullanım Senaryoları

Spor İstatistikleri Veri Toplama için CAPTCHA Kullanımı

Spor istatistikleri portalları oyuncu veritabanlarını, oyun günlüklerini ve geçmiş kayıtlarını Cloudflare Turnstile ve reCAPTCHA ile korur. CAPTCHA'lar birden fazla oyuncu, sezon veya oyun genelinde (özellikle de verilerinden ödeme duvarları arkasında para kazanan sitelerde) yapılan hızlı sorgular sırasında tetiklenir. Spor verilerini güvenilir bir şekilde nasıl toplayacağınız burada açıklanmıştır.

Spor Portallarındaki CAPTCHA Desenleri

Veri türü Portal türü CAPTCHA Tetikleyici
Oyuncu istatistikleri Referans siteleri Cloudflare Turnstile Hızlı oynatıcı sayfası yüklemeleri
Oyun kutusu puanları Puan portalları Cloudflare doğrulama akışı Toplu oyun aramaları
Sezon sıralamaları Lig siteleri reCAPTCHA v2 Otomatik gezinme
Fantezi projeksiyonlar Fantezi platformlar reCAPTCHA v3 Sık API benzeri erişim
Bahis oranları/lines Oran portalları Cloudflare Turnstile Yüksek frekanslı yenilemeler
Tarihsel kayıtlar Arşiv siteleri Resim CAPTCHA'sı Veri dışa aktarma istekleri

Spor Veri Toplayıcı

import requests
import time
import re
from dataclasses import dataclass, field

@dataclass
class PlayerStats:
    name: str
    team: str
    position: str
    stats: dict = field(default_factory=dict)
    season: str = ""
    source: str = ""

class SportsDataCollector:
    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_player_stats(self, portal_url, player_slug, season=None):
        """Fetch player statistics, solving CAPTCHAs as needed."""
        url = f"{portal_url}/players/{player_slug}"
        if season:
            url += f"/{season}"

        response = self.session.get(url)

        if self._is_captcha_page(response):
            response = self._solve_turnstile_and_retry(response, url)

        return self._parse_player_stats(response.text)

    def get_game_scores(self, portal_url, date):
        """Fetch all game scores for a specific date."""
        url = f"{portal_url}/scores/{date}"
        response = self.session.get(url)

        if self._is_captcha_page(response):
            response = self._solve_turnstile_and_retry(response, url)

        return self._parse_scores(response.text)

    def collect_team_roster(self, portal_url, team_slug, season):
        """Collect stats for all players on a team roster."""
        roster_url = f"{portal_url}/teams/{team_slug}/{season}/roster"
        response = self.session.get(roster_url)

        if self._is_captcha_page(response):
            response = self._solve_turnstile_and_retry(response, roster_url)

        player_slugs = self._extract_player_links(response.text)

        all_stats = []
        for slug in player_slugs:
            try:
                stats = self.get_player_stats(portal_url, slug, season)
                all_stats.append(stats)
                time.sleep(2)  # Respectful delay
            except Exception as e:
                print(f"Failed for {slug}: {e}")

        return all_stats

    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_turnstile_and_retry(self, response, url):
        match = re.search(r'data-sitekey="(0x[^"]+)"', response.text)
        if not match:
            raise ValueError("Turnstile sitekey not 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("Turnstile solve timed out")

    def _parse_player_stats(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        # Extract stat rows from tables
        stats = {}
        stat_table = soup.select_one("table.stats, #stats-table")
        if stat_table:
            headers = [th.text.strip() for th in stat_table.select("thead th")]
            for row in stat_table.select("tbody tr"):
                cells = [td.text.strip() for td in row.select("td")]
                if len(cells) == len(headers):
                    for header, value in zip(headers, cells):
                        stats[header] = value

        def text_or_empty(node):
            return node.text.strip() if node and node.text else ""

        return PlayerStats(
            name=text_or_empty(soup.select_one("h1, .player-name")),
            team=text_or_empty(soup.select_one(".team-name, .team")),
            position=text_or_empty(soup.select_one(".position, .pos")),
            stats=stats
        )

    def _parse_scores(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        games = []

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        for game in soup.select(".game-card, .scoreboard-item"):
            games.append({
                "away": text_or_none(game.select_one(".away-team")),
                "home": text_or_none(game.select_one(".home-team")),
                "away_score": text_or_none(game.select_one(".away-score")),
                "home_score": text_or_none(game.select_one(".home-score")),
                "status": text_or_none(game.select_one(".game-status"))
            })

        return games

    def _extract_player_links(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        links = []
        for a in soup.select("a[href*='/players/']"):
            slug = a["href"].rstrip("/").split("/")[-1]
            if slug and slug not in links:
                links.append(slug)
        return links


# Usage
collector = SportsDataCollector("YOUR_API_KEY")

# Get player stats
stats = collector.get_player_stats(
    "https://sports.example.com", "lebron-james", "2024"
)
print(f"{stats.name} ({stats.team}): {stats.stats}")

# Get all scores for a date
scores = collector.get_game_scores("https://sports.example.com", "2024-12-25")
for game in scores:
    print(f"{game['away']} {game['away_score']} @ {game['home']} {game['home_score']}")

Sezon Veri Toplayıcı (JavaScript)

class SportsAggregator {
  constructor(apiKey) {
    this.apiKey = apiKey;
  }

  async collectSeasonData(portalUrl, sport, season, teams) {
    const allData = {};

    for (const team of teams) {
      try {
        const roster = await this.getTeamStats(portalUrl, team, season);
        allData[team] = roster;
      } catch (error) {
        allData[team] = { error: error.message };
      }
      // Rate limit between teams
      await new Promise(r => setTimeout(r, 3000));
    }

    return allData;
  }

  async getTeamStats(portalUrl, teamSlug, season) {
    const url = `${portalUrl}/teams/${teamSlug}/${season}`;
    const response = await fetch(url);
    const html = await response.text();

    if (html.includes('cf-turnstile') || response.status === 403) {
      return this.solveAndFetch(url, html);
    }

    return this.parseTeamPage(html);
  }

  async solveAndFetch(url, 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: url,
        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(url, {
          method: 'POST',
          body: new URLSearchParams({ 'cf-turnstile-response': data.request })
        });
        return this.parseTeamPage(await response.text());
      }
    }
    throw new Error('Turnstile solve timed out');
  }

  parseTeamPage(html) {
    const players = [];
    const rowMatches = html.matchAll(/<tr[^>]*class="[^"]*player[^"]*"[^>]*>([\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 >= 3) {
        players.push({
          name: cells[0],
          position: cells[1],
          stats: cells.slice(2)
        });
      }
    }

    return { players, count: players.length };
  }
}

// Usage
const aggregator = new SportsAggregator('YOUR_API_KEY');
const seasonData = await aggregator.collectSeasonData(
  'https://sports.example.com', 'basketball', '2024',
  ['lakers', 'celtics', 'warriors']
);

Spora Göre Toplama Stratejisi

Spor En yüksek veri hacmi CAPTCHA duyarlılığı Önerilen yaklaşım
Beyzbol Günlük oyun kayıtları Orta Oyunlar bittikten sonra topla
Basketbol Oyun geceleri Oyunlar sırasında yüksek Yoğun olmayan saatleri kullanın
Futbol Haftalık oyunlar Oyunlar arasında düşük Haftalık toplu tahsilat
Futbol Ligler arasında günlük Orta Lig başına oturumlar
Hokey her gece Orta Oyun sonrası koleksiyon

Sorun giderme

Sorun Sebep Düzeltme
Her sayfada Turnstile Çerez kalıcılığı yok Oturumda qa_session_cookie'yi koruyun
Oyuncu sayfası farklı istatistikleri gösteriyor Sezon/career geçiş Sezon parametresini URL'ye ekle
Puanlar sayfası boş Henüz oynanmamış oyunlar Sorgulamadan önce oyun programını kontrol edin
50 istekten sonra oran sınırlı Portalın günlük sınırı Zamana dağıtın, proxy kullanın

SSS

Canlı oyun verilerini ne sıklıkla toplamalıyım?

Aktif oyunlar sırasında puanlar birkaç dakikada bir güncellenir. CAPTCHA'ları en aza indirmek için oturum sürekliliği ile 2-5 dakikalık aralıklar kullanın.CaptchaAI, Turnstileyi %100 başarı oranıyla yönetir.

Birden fazla sezona giden geçmiş istatistikleri toplayabilir miyim?

Evet. Geçmiş veriler nadiren değişir, bu nedenle bunları bir kez toplayın ve yerel olarak önbelleğe alın. CAPTCHA'lar ilk toplu toplama sırasında tetiklenebilir ancak depolanan verileri etkilemez.

Spor API'leri CAPTCHA çözme ihtiyacını ortadan kaldırıyor mu?

Bazı ligler resmi API'ler sunar, ancak bunlar genellikle pahalıdır, hız sınırlıdır veya referans sitelerde mevcut olan gelişmiş istatistikler eksiktir. CaptchaAI, CAPTCHA'lar tarafından korunan ek veri kaynaklarına erişmenizi sağlar.

İlgili Makaleler

Sonraki Adımlar

Spor verilerini güvenilir bir şekilde toplayın —CaptchaAI API anahtarınızı alınve spor portalı CAPTCHA'larını otomatik olarak yönetin.

Bu makale için yorumlar devre dışı bırakılmıştır.