A command-line tool for scraping sports data from Flashscore.
This project provides a scraper to extract sports event information, odds, and other data from Flashscore website.
- Ensure you have Python 3.13 or higher installed.
- Clone the repository.
- Install dependencies:
uv sync uv run playwright install chromium
All commands print JSON to stdout and accept the common options described below
uv run main.py odds <url> [--odds <type> ...] [--bookmakers <id> ...] [--sport <sport>] [--engine <engine>] [--timeout <seconds>]
uv run main.py event <url> [--sport <sport>] [--engine <engine>] [--timeout <seconds>]
uv run main.py event-info <url> [--sport <sport>] [--engine <engine>] [--timeout <seconds>]
| Option | Description | Default |
|---|---|---|
url |
Flashscore event URL | (required) |
--sport |
Sport type (e.g. football). Auto-detected from URL if not provided. |
None |
--engine |
Scraping engine: playwright or curl |
playwright |
--timeout |
Request timeout in seconds | 10 |
-v, --verbose |
Enable debug logging | off |
| Option | Description | Default |
|---|---|---|
--odds |
One or more odds types to scrape. Scrapes all types when omitted. | all |
--bookmakers |
Bookmaker IDs to include (e.g. 2 16 17). Includes all bookmakers when omitted. |
all |
# Scrape all odds for a football match
uv run main.py odds https://www.flashscore.com/match/football/...
# Scrape only 1X2 and over/under odds, filtered to two bookmakers
uv run main.py odds https://www.flashscore.com/match/football/... --odds 1x2-odds over-under --bookmakers 2 16
# Scrape event summary
uv run main.py event https://www.flashscore.com/match/football/...
# Scrape extended event info with verbose logging
uv run main.py event-info https://www.flashscore.com/match/football/... -v| Odds Type | --odds value |
|---|---|
| 1X2 | 1x2-odds |
| Over/Under | over-under |
| Asian Handicap | asian-handicap |
| Both Teams to Score | both-teams-to-score |
| Double Chance | double-chance |
| European Handicap | european-handicap |
| Draw No Bet | draw-no-bet |
| Correct Score | correct-score |
| Half Time / Full Time | ht-ft |
| Odd/Even | odd-even |
Any sport added to the Sports enum in models/sports.py — even without a dedicated parser — is handled by universal parsers that auto-detect the page structure. Currently registered: volleyball.
To add a new sport (e.g. basketball), extend models/sports.py:
class Sports(Enum):
FOOTBALL = "football"
VOLLEYBALL = "volleyball"
BASKETBALL = "basketball" # add new sport hereOnce registered, the following works automatically:
- Event: Date, participants, score, status, and league.
- Odds: Column headers from the odds table (e.g.
1,2,Over,Under) are extracted and used astype/valuein eachOddsParserRowData. Handicap or total values (wcl-oddsValuespans) are used when present. Bookmaker names and IDs are always detected. Rows with no odds are skipped. - Event info (
event-infocommand): Available info types (draw,h2h,standings) are auto-detected from the page tabs. Each type is parsed independently — for example, requestingdrawnever accidentally returnsh2hdata. CSS wait selectors (.draw__cover,.h2h__section,.ui-table) prevent fetch timeouts on slow pages.
When using a non-football sport, pass --sport explicitly:
uv run main.py event-info <url> --sport volleyball
uv run main.py odds <url> --sport basketballThe universal parser may not always return correct or complete data — odds columns may be misaligned, event info may miss details, or the page structure may differ from expectations. In such cases, create dedicated parsers for the sport.
Use the football parsers in parsers/football/ as a reference:
| File | Purpose |
|---|---|
football_parser.py |
Top-level parser (registers with @BaseParser, sport_type="football") |
football_event_parser.py |
Event data (date, participants, score, status, league) |
football_event_info_parser.py |
Event info handlers (standings, draw, h2h) — uses @_handler(InfoType) decorator |
odds/*.py |
Per-odds-type parsers (e.g. one_x_two.py, over_under.py) — each extends BaseOddsParser with sport_type and odds_type |
Key registration patterns (all auto-discovered, no manual imports needed):
- Sport parser:
class MySportParser(BaseParser, sport_type="mysport") - Odds parser:
class MyOddsParser(BaseOddsParser)withsport_typeandodds_typeclass attributes - Odds enum:
@register_odds("mysport")on an odds enum class inmodels/odds/ - Event info enum:
@register_event_info("mysport")on an event info enum class inmodels/event_info/
# Example: registering a new sport parser
class MySportParser(BaseParser, sport_type="mysport"):
def _parse_event(self, url, data): ...
def _parse_odds(self, url, data, odds_type, odds_filter): ...
def _parse_event_info(self, url, data, info_type): ...uv run pytest