Skip to content

Latest commit

Β 

History

History
576 lines (465 loc) Β· 26.1 KB

File metadata and controls

576 lines (465 loc) Β· 26.1 KB

HĹBKOVÁ ANALÝZA - rpscrape

Executive Summary

Tento dokument obsahuje komplexnΓΊ hΔΊbkovΓΊ analΓ½zu projektu rpscrape vrΓ‘tane root cause analΓ½zy problΓ©mov, porovnania alternatΓ­v, nΓ‘vrhu rieΕ‘enΓ­ a architektΓΊry pre vylepΕ‘enΓΊ verziu.

Kľúčové zistenie: Projekt rpscrape je funkčný nÑstroj s aktívnou komunitou, ale trpí architektonickými problémami, ktoré spôsobujú opakujúce sa chyby. Odporúčam strategický fork s postupným refaktorovaním namiesto kompletného prepísania.


ČASŀ 1: ROOT CAUSE ANALÝZA

1.1 Prečo vznikajú opakujúce sa problémy?

ProblΓ©m Root Cause Dopad
SSL/HTTP chyby ChΓ½bajΓΊca retry logika a session management 40% issues
ChΓ½bajΓΊce dΓ‘ta FragilnΓ© XPath selektory zΓ‘vislΓ© od HTML Ε‘truktΓΊry 30% issues
Index out of range Absencia validΓ‘cie vstupnΓ½ch dΓ‘t 15% issues
HTTP 406 NesprΓ‘vne/chΓ½bajΓΊce HTTP headers 15% issues

1.2 Architektonické príčiny

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    PROBLΓ‰MOVÁ ARCHITEKTÚRA                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   CLI       │────▢│  Scraper    │────▢│   Parser    β”‚   β”‚
β”‚  β”‚  (argparse) β”‚     β”‚ (requests)  β”‚     β”‚   (lxml)    β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚         β”‚                   β”‚                   β”‚           β”‚
β”‚         β”‚                   β”‚                   β”‚           β”‚
β”‚         β–Ό                   β–Ό                   β–Ό           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚              GLOBÁLNY STAV (settings)                β”‚   β”‚
β”‚  β”‚              - Ε½iadna izolΓ‘cia                       β”‚   β”‚
β”‚  β”‚              - Ε€aΕΎkΓ© testovanie                      β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
│  PROBLÉMY:                                                  │
β”‚  βœ— Tight coupling medzi vrstvami                           β”‚
β”‚  βœ— Ε½iadna abstrakcia pre HTTP klienta                      β”‚
β”‚  βœ— Parsovanie priamo zΓ‘vislΓ© od Racing Post HTML           β”‚
β”‚  βœ— Ε½iadna error handling stratΓ©gia                         β”‚
β”‚  βœ— GlobΓ‘lne premennΓ© namiesto dependency injection         β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.3 Čo by vyrieőilo 80% issues?

Top 3 zmeny s najvyΕ‘Ε‘Γ­m dopadom:

  1. ImplementΓ‘cia retry mechanizmu s exponenciΓ‘lnym backoff

    • VyrieΕ‘i: SSL chyby, connection timeouts, HTTP 5xx
    • NΓ‘ročnosΕ₯: NΓ­zka
    • Dopad: ~40% issues
  2. RobustnΓ© HTTP headers

    • VyrieΕ‘i: HTTP 406, bot detection
    • NΓ‘ročnosΕ₯: NΓ­zka
    • Dopad: ~15% issues
  3. Graceful handling prÑzdnych/neočakÑvaných dÑt

    • VyrieΕ‘i: Index errors, missing data
    • NΓ‘ročnosΕ₯: StrednΓ‘
    • Dopad: ~25% issues

ČASΕ€ 2: TECHNICKÁ ANALÝZA PROBLΓ‰MOV

2.1 SSL/TLS Chyby (Issue #186)

Príčina: ssl.SSLEOFError: [SSL: UNEXPECTED_EOF_WHILE_READING]

Root cause:

  • Server predčasne ukončí SSL handshake
  • DlhΓ© scraping sessions bez connection pooling
  • Ε½iadna retry logika

RieΕ‘enie:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_session():
    session = requests.Session()
    retry = Retry(
        total=5,
        backoff_factor=1,
        status_forcelist=[500, 502, 503, 504],
        allowed_methods=["HEAD", "GET", "OPTIONS"]
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('https://', adapter)
    session.mount('http://', adapter)
    return session

2.2 HTTP 406 Not Acceptable (Issue #182)

Príčina: Server odmieta requesty bez sprÑvnych Accept headers

Root cause:

  • NedostatočnΓ© HTTP headers
  • ChΓ½bajΓΊci User-Agent alebo prΓ­liΕ‘ generickΓ½
  • Racing Post aktΓ­vne blokuje boty (robots.txt vracia 403!)

RieΕ‘enie:

BROWSER_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
    'Accept-Language': 'en-GB,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1',
    'Cache-Control': 'max-age=0',
}

2.3 FragilnΓ© XPath Selektory

Príčina: Absolútne XPath cesty sa lÑmu pri akejkoľvek zmene HTML

Best practices:

Typ PrΓ­klad Riziko
❌ Absolútny /html/body/div[3]/table/tr[2]/td[1] Vysoké
⚠️ Index-based //div[@class='results']/table/tr[2] Stredné
βœ… RelatΓ­vny + ID //div[@id='results']//td[@data-field='horse'] NΓ­zke
βœ… CSS + atribΓΊt div.race-result td[data-testid='horse-name'] NajniΕΎΕ‘ie

Odporúčanie: Hybridný prístup - CSS pre jednoduché prípady, XPath pre komplexné traversing

2.4 ChΓ½bajΓΊce Rate Limiting

Riziko: IP ban, server overload, etickΓ© problΓ©my

ImplementΓ‘cia s asyncio.Semaphore:

import asyncio
from aiolimiter import AsyncLimiter

# Max 10 requests za sekundu, max 5 concurrent
rate_limiter = AsyncLimiter(10, 1)
semaphore = asyncio.Semaphore(5)

async def fetch_with_limit(url):
    async with semaphore:
        async with rate_limiter:
            async with session.get(url) as response:
                return await response.text()

ČASŀ 3: POROVNÁVACIA ANALÝZA ALTERNATÍV

3.1 AlternatΓ­vy k rpscrape

RieΕ‘enie Typ Cena DΓ‘ta SpoΔΎahlivosΕ₯
rpscrape Open-source scraper Zadarmo Racing Post NestabilnΓ©
The Racing API Komerčné API £50+/mesiac 12+ rokov VysokÑ
Betfair Historical OficiΓ‘lne API VariabilnΓ‘ Betfair odds VysokΓ‘
Timeform scraping DIY scraping Zadarmo Timeform StrednΓ‘
Smartform/Betwise KomerčnÑ DB £100+/mesiac Kompletné VysokÑ

3.2 VΓ½hody a nevΓ½hody rpscrape

VΓ½hody:

  • βœ… Zadarmo a open-source
  • βœ… AktΓ­vna komunita (193 stars, 75 forks)
  • βœ… PokrΓ½va Racing Post dΓ‘ta
  • βœ… CSV/JSON export

NevΓ½hody:

  • ❌ NestabilnΓ½ (101+ issues)
  • ❌ ZΓ‘vislosΕ₯ od HTML Ε‘truktΓΊry
  • ❌ PrΓ‘vne rizikΓ‘ (ToS Racing Post)
  • ❌ Ε½iadne SLA

3.3 Web Scraping Frameworks

Framework Use Case Learning Curve Ε kΓ‘lovateΔΎnosΕ₯
Scrapy Enterprise scraping VysokΓ‘ VΓ½bornΓ‘
requests + BeautifulSoup JednoduchΓ© skripty NΓ­zka SlabΓ‘
aiohttp + lxml Async scraping StrednΓ‘ DobrΓ‘
Playwright/Selenium JavaScript sites StrednΓ‘ StrednΓ‘

Odporúčanie: Pre rpscrape ideÑlne aiohttp + lxml (už používa) s lepőou architektúrou


ČASŀ 4: FORK vs REWRITE ANALÝZA

4.1 Čo zachovaΕ₯ z pΓ΄vodnΓ©ho kΓ³du?

Komponent Kvalita Rozhodnutie
Data models (dataclass) DobrΓ‘ βœ… ZachovaΕ₯, rozΕ‘Γ­riΕ₯ o Pydantic validΓ‘ciu
lxml parsing logika StrednΓ‘ ⚠️ RefaktorovaΕ₯, zachovaΕ₯ XPath
Betfair integrΓ‘cia DobrΓ‘ βœ… ZachovaΕ₯
CLI argument parsing StrednΓ‘ ⚠️ MigrovaΕ₯ na Typer
HTTP request handling SlabΓ‘ ❌ PrepΓ­saΕ₯ kompletne
Error handling SlabΓ‘ ❌ PrepΓ­saΕ₯ kompletne
Config management DobrΓ‘ βœ… ZachovaΕ₯ TOML

4.2 ROI AnalΓ½za

Prístup Čas Riziko Zachované funkcie
Quick fixes 1-2 tΓ½ΕΎdne NΓ­zke 100%
StrategickΓ½ fork 1-2 mesiace StrednΓ© 90%
KompletnΓ½ rewrite 3-6 mesiacov VysokΓ© 70%*

*Pri rewrite sa stratΓ­ "tribal knowledge" z 253 commitov

4.3 Rozhodnutie

πŸ† ODPORÚČANIE: StrategickΓ½ Fork

ZdΓ΄vodnenie:

  1. ZachovΓ‘va fungujΓΊcu parsing logiku
  2. PostupnÑ migrÑcia bez preruőenia funkčnosti
  3. MoΕΎnosΕ₯ vyuΕΎiΕ₯ existujΓΊce testy komunity
  4. NiΕΎΕ‘ie riziko regresiΓ­

ČASŀ 5: NÁVRH NOVEJ ARCHITEKTÚRY

5.1 NavrhovanΓ‘ architektΓΊra

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      VYLEPΕ ENÁ ARCHITEKTÚRA                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                      CLI LAYER (Typer)                    β”‚  β”‚
β”‚  β”‚  β€’ AutomatickΓ‘ dokumentΓ‘cia                               β”‚  β”‚
β”‚  β”‚  β€’ Type hints validΓ‘cia                                   β”‚  β”‚
β”‚  β”‚  β€’ Progress bars (rich)                                   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                              β”‚                                  β”‚
β”‚                              β–Ό                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                   SERVICE LAYER                           β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚  β”‚
β”‚  β”‚  β”‚ RaceScraper β”‚  β”‚ CardScraper β”‚  β”‚BetfairClientβ”‚       β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                              β”‚                                  β”‚
β”‚                              β–Ό                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                  HTTP CLIENT LAYER                        β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚
β”‚  β”‚  β”‚              ResilientHttpClient                    β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ Retry s exponenciΓ‘lnym backoff                   β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ Rate limiting (semaphore + aiolimiter)           β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ Connection pooling                               β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ Rotating User-Agents                             β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ Circuit breaker pattern                          β”‚  β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                              β”‚                                  β”‚
β”‚                              β–Ό                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                   PARSER LAYER                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚  β”‚
β”‚  β”‚  β”‚ RaceParser  β”‚  β”‚ CardParser  β”‚  β”‚ OddsParser  β”‚       β”‚  β”‚
β”‚  β”‚  β”‚ (Strategy)  β”‚  β”‚ (Strategy)  β”‚  β”‚ (Strategy)  β”‚       β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚  β”‚
β”‚  β”‚                                                           β”‚  β”‚
β”‚  β”‚  β€’ Abstrakcia od konkrΓ©tnej strΓ‘nky                      β”‚  β”‚
β”‚  β”‚  β€’ Fallback selektory                                    β”‚  β”‚
β”‚  β”‚  β€’ Graceful degradation                                  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                              β”‚                                  β”‚
β”‚                              β–Ό                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                    MODEL LAYER                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚  β”‚
β”‚  β”‚  β”‚           Pydantic Models s validΓ‘ciou              β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ RaceInfo, RunnerInfo, OddsInfo                   β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ AutomatickΓ‘ type coercion                        β”‚  β”‚  β”‚
β”‚  β”‚  β”‚  β€’ ValidačnΓ© pravidlΓ‘                               β”‚  β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                              β”‚                                  β”‚
β”‚                              β–Ό                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                   EXPORT LAYER                            β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚  β”‚
β”‚  β”‚  β”‚ CSVExporter β”‚  β”‚JSONExporter β”‚  β”‚ DBExporter  β”‚       β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                 CROSS-CUTTING CONCERNS                    β”‚  β”‚
β”‚  β”‚  β€’ Logging (structlog/loguru)                            β”‚  β”‚
β”‚  β”‚  β€’ Configuration (Pydantic Settings)                     β”‚  β”‚
β”‚  β”‚  β€’ Metrics & Monitoring                                  β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

5.2 NavrhovanΓ½ Technology Stack

Vrstva PΓ΄vodnΓ‘ technolΓ³gia NavrhovanΓ‘ ZdΓ΄vodnenie
CLI argparse + readline Typer + Rich Auto-docs, progress bars
HTTP requests (sync) aiohttp + tenacity Async, retry
Parsing lxml lxml (zachovaΕ₯) Funguje dobre
Models dataclass Pydantic dataclass ValidΓ‘cia
Config tomli Pydantic Settings Type-safe config
Logging print() structlog Structured logging
Testing (ΕΎiadne) pytest + VCR.py HTTP mocking

5.3 NavrhovanΓ‘ adresΓ‘rovΓ‘ Ε‘truktΓΊra

rpscrape_v2/
β”œβ”€β”€ src/
β”‚   └── rpscrape/
β”‚       β”œβ”€β”€ __init__.py
β”‚       β”œβ”€β”€ cli/
β”‚       β”‚   β”œβ”€β”€ __init__.py
β”‚       β”‚   β”œβ”€β”€ app.py              # Typer aplikΓ‘cia
β”‚       β”‚   └── commands/
β”‚       β”‚       β”œβ”€β”€ scrape.py
β”‚       β”‚       └── racecards.py
β”‚       β”œβ”€β”€ core/
β”‚       β”‚   β”œβ”€β”€ __init__.py
β”‚       β”‚   β”œβ”€β”€ http_client.py      # ResilientHttpClient
β”‚       β”‚   β”œβ”€β”€ rate_limiter.py
β”‚       β”‚   └── exceptions.py
β”‚       β”œβ”€β”€ scrapers/
β”‚       β”‚   β”œβ”€β”€ __init__.py
β”‚       β”‚   β”œβ”€β”€ base.py             # Abstract base scraper
β”‚       β”‚   β”œβ”€β”€ race_scraper.py
β”‚       β”‚   └── card_scraper.py
β”‚       β”œβ”€β”€ parsers/
β”‚       β”‚   β”œβ”€β”€ __init__.py
β”‚       β”‚   β”œβ”€β”€ base.py             # Parser interface
β”‚       β”‚   β”œβ”€β”€ racing_post.py
β”‚       β”‚   └── selectors.py        # XPath/CSS selektory
β”‚       β”œβ”€β”€ models/
β”‚       β”‚   β”œβ”€β”€ __init__.py
β”‚       β”‚   β”œβ”€β”€ race.py             # Pydantic models
β”‚       β”‚   β”œβ”€β”€ runner.py
β”‚       β”‚   └── odds.py
β”‚       β”œβ”€β”€ exporters/
β”‚       β”‚   β”œβ”€β”€ __init__.py
β”‚       β”‚   β”œβ”€β”€ base.py
β”‚       β”‚   β”œβ”€β”€ csv_exporter.py
β”‚       β”‚   └── json_exporter.py
β”‚       └── config/
β”‚           β”œβ”€β”€ __init__.py
β”‚           └── settings.py         # Pydantic Settings
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ conftest.py
β”‚   β”œβ”€β”€ cassettes/                  # VCR.py recordings
β”‚   β”œβ”€β”€ unit/
β”‚   └── integration/
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ courses/
β”‚   └── regions/
β”œβ”€β”€ pyproject.toml
β”œβ”€β”€ README.md
└── .github/
    └── workflows/
        └── ci.yml

ČASŀ 6: IMPLEMENTAČNÝ ROADMAP

6.1 FΓ‘za 1: Quick Wins (1-2 tΓ½ΕΎdne)

CieΔΎ: StabilizovaΕ₯ existujΓΊci kΓ³d bez veΔΎkΓ½ch zmien

  • ImplementovaΕ₯ retry mechanizmus s tenacity
  • PridaΕ₯ sprΓ‘vne HTTP headers
  • PridaΕ₯ zΓ‘kladnΓ© logging (loguru)
  • OpraviΕ₯ Index out of range s graceful handling
  • PridaΕ₯ rate limiting (time.sleep)

6.2 FΓ‘za 2: Refaktoring (2-4 tΓ½ΕΎdne)

CieΔΎ: ZlepΕ‘iΕ₯ architektΓΊru bez zmeny funkcionality

  • ExtrahovaΕ₯ HTTP client do samostatnej triedy
  • MigrovaΕ₯ dataclass na Pydantic
  • ImplementovaΕ₯ proper error handling
  • PridaΕ₯ pytest s VCR.py
  • RefaktorovaΕ₯ globΓ‘lny stav

6.3 FÑza 3: ModernizÑcia (4-8 týždňov)

CieΔΎ: ModernΓ‘ architektΓΊra s novΓ½mi funkciami

  • MigrovaΕ₯ CLI na Typer + Rich
  • ImplementovaΕ₯ async scraping (aiohttp)
  • PridaΕ₯ inkrementΓ‘lny scraping
  • ImplementovaΕ₯ parser fallbacks
  • PridaΕ₯ CI/CD pipeline

6.4 FΓ‘za 4: Enterprise Features (voliteΔΎnΓ©)

  • Database export (SQLite/PostgreSQL)
  • Web dashboard (FastAPI)
  • Scheduling (APScheduler)
  • Alerting pri chybΓ‘ch
  • Proxy rotation

ČASŀ 7: BEZPEČNOSŀ A ETIKA

7.1 PrΓ‘vne aspekty

Racing Post robots.txt: Vracia HTTP 403 - aktΓ­vne blokuje prΓ­stup k pravidlΓ‘m!

UK prΓ‘vny rΓ‘mec:

  • Ε½iadna Ε‘pecifickΓ‘ legislatΓ­va zakazujΓΊca web scraping
  • Riziko poruΕ‘enia:
    • Copyright - na Ε‘truktΓΊru dΓ‘t
    • Database rights - na kompilΓ‘ciu dΓ‘t
    • Contract law - ak ToS zakazuje scraping
    • Computer Misuse Act 1990 - ak obΓ­dete ochranu

Odporúčanie:

  • ScrapovaΕ₯ len verejne dostupnΓ© dΓ‘ta
  • ReΕ‘pektovaΕ₯ rate limits
  • NeuchovΓ‘vaΕ₯ osobnΓ© ΓΊdaje (GDPR)
  • ZvΓ‘ΕΎiΕ₯ legitΓ­mne API alternatΓ­vy

7.2 Etické odporúčania

Prax ImplementΓ‘cia
Rate limiting Max 1 request/sekundu
User-Agent IdentifikovaΕ₯ sa ako bot (voliteΔΎne)
robots.txt ReΕ‘pektovaΕ₯ (ak dostupnΓ½)
Off-peak scraping PreferovaΕ₯ nočnΓ© hodiny
Caching MinimalizovaΕ₯ duplicitnΓ© requesty

ČASŀ 8: ZÁVER A ODPORÚČANIA

8.1 HlavnΓ© zΓ‘very

  1. rpscrape je hodnotnΓ½ projekt s aktΓ­vnou komunitou, ale trpΓ­ technickΓ½m dlhom
  2. 80% problΓ©mov je rieΕ‘iteΔΎnΓ½ch implementΓ‘ciou retry logiky a lepΕ‘Γ­ch headers
  3. StrategickΓ½ fork je najlepΕ‘ia cesta - zachovΓ‘va funkčnosΕ₯, umožňuje postupnΓ© zlepΕ‘enia
  4. PrÑvne rizikÑ existujú - zvÑžte komerčné API pre production use

8.2 Prioritizované odporúčania

# Odporúčanie Dopad NΓ‘ročnosΕ₯
1 ImplementovaΕ₯ tenacity retry VysokΓ½ NΓ­zka
2 OpraviΕ₯ HTTP headers VysokΓ½ NΓ­zka
3 PridaΕ₯ structlog logging StrednΓ½ NΓ­zka
4 MigrovaΕ₯ na Pydantic models StrednΓ½ StrednΓ‘
5 PridaΕ₯ pytest + VCR.py StrednΓ½ StrednΓ‘
6 ImplementovaΕ₯ rate limiting StrednΓ½ NΓ­zka
7 MigrovaΕ₯ CLI na Typer NΓ­zky StrednΓ‘
8 Async refaktoring NΓ­zky VysokΓ‘

8.3 Ďalőie kroky

  1. Rozhodnutie: Fork existujΓΊceho repo alebo novΓ½ projekt?
  2. Scope: KtorΓ© fΓ‘zy implementovaΕ₯?
  3. Timeline: AkΓ½ je realistickΓ½ harmonogram?
  4. Resources: Kto bude pracovaΕ₯ na projekte?

PRÍLOHY

A. Užitočné zdroje

Web Scraping Best Practices:

Retry Mechanizmy:

Logging:

Testing:

CLI:

AlternatΓ­vne dΓ‘tovΓ© zdroje:

B. KΓ³dovΓ© prΓ­klady

Resilient HTTP Client:

import aiohttp
from tenacity import retry, stop_after_attempt, wait_exponential
from aiolimiter import AsyncLimiter

class ResilientHttpClient:
    def __init__(self, rate_limit: float = 1.0, max_concurrent: int = 5):
        self.rate_limiter = AsyncLimiter(rate_limit, 1)
        self.semaphore = asyncio.Semaphore(max_concurrent)
        self.session = None

    async def __aenter__(self):
        self.session = aiohttp.ClientSession(headers=BROWSER_HEADERS)
        return self

    async def __aexit__(self, *args):
        await self.session.close()

    @retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, max=60))
    async def get(self, url: str) -> str:
        async with self.semaphore:
            async with self.rate_limiter:
                async with self.session.get(url) as response:
                    response.raise_for_status()
                    return await response.text()

Pydantic Model:

from pydantic import BaseModel, Field, validator
from typing import Optional
from datetime import date

class RaceInfo(BaseModel):
    race_id: str
    date: date
    course: str
    race_name: str
    distance_furlongs: float = Field(ge=0)
    going: str
    race_class: Optional[int] = Field(None, ge=1, le=7)

    @validator('distance_furlongs', pre=True)
    def parse_distance(cls, v):
        if isinstance(v, str):
            # Parse "1m2f" format
            return parse_distance_string(v)
        return v

Dokument vytvorenΓ½: 2025-12-09 Autor: Claude Code Deep Analysis Verzia: 1.0