Portfolio-grade Python scraping system for Canadian school sports data.
This project demonstrates practical, production-minded scraping:
- multi-source extraction adapters
- robots-aware request policy
- per-domain throttling and retry behavior
- normalized snapshot storage in SQLite
- operational CLI and Streamlit analytics UI
Current source adapters:
rseq-stats.ca(basketball + football leagues configured inconfig.yaml)hmhshl.com(HTML-visible data only, API access intentionally disabled)bchighschoolfootball.combcboysbasketball.com
Data is normalized and written into snapshot tables, with current-state SQL views for analytics and exports.
- Structured ETL layering (
extract -> transform -> load) - Defensive ingestion (errors captured into
scrape_issues, run-level status tracking) - Config-driven source control and domain-specific throttling
- Repeatable local runtime with CLI entrypoints and tests
- Clear operational docs and Windows scheduler scripts
Source Adapters (canadastats/sources/*.py)
-> HttpClient + RobotsPolicy + Throttler
-> Normalized payload models
-> Repository upserts + snapshot inserts (SQLite)
-> Current views (standings, games, leaders, source health)
-> Streamlit dashboard / CSV export
Core modules:
canadastats/extract/request client, robots checks, throttlingcanadastats/sources/source-specific parsing logiccanadastats/load/repository.pyschema, upserts, viewscanadastats/sync_service.pyorchestration and persistencecanadastats/cli.pycommand surfacecanadastats/app/streamlit_app.pylocal dashboard
- Python 3.11+
- Requests + BeautifulSoup + lxml
- SQLite
- Pandas
- Streamlit
- Pytest
py -3.11 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e .[dev]
Copy-Item config.example.yaml config.yaml
python -m canadastats sync all
streamlit run canadastats/app/streamlit_app.py# full sync
python -m canadastats sync all
# single source
python -m canadastats sync source --name rseq
# single league in source
python -m canadastats sync league --source bc_football --league-id 6713
# diagnostics
python -m canadastats doctor
# export current snapshots
python -m canadastats export csv --sport basketball --out .\exportsscrape_runs: run-level status and summaryscrape_issues: non-fatal and fatal extraction issuesleagues,teams,players: normalized entitiesstanding_snapshots,game_snapshots,player_stat_snapshots: historical snapshotscurrent_standings,current_games,current_leaders,source_health: analytics views
- Targets only public, non-login pages
- Enforces robots policy checks before requests
- Uses domain-level delays and retry limits
- Leaves HMHSHL API disabled (
sources.hmhshl.allow_api: false)
Review source and platform terms before enabling additional coverage.
python -m pip install -e .[dev]
pytestExisting tests validate parser behavior, robots policy logic, repository integration, and dashboard smoke flow.
canadastats/
app/ # Streamlit UI
extract/ # HTTP client, robots, throttle
load/ # SQLite repository + current views
sources/ # source adapters and parsers
transform/ # normalization helpers
docs/ # operations + setup notes
scripts/ # Windows scheduler automation
tests/ # pytest suite
This repository includes:
LICENSE(MIT)CONTRIBUTING.mdSECURITY.md- GitHub issue and PR templates
- GitHub Actions CI for tests
- Add schema migration strategy for long-lived deployments
- Add richer source-level metrics and alerting
- Expand league coverage with per-source feature flags
This project is for educational and portfolio demonstration purposes. Respect robots directives, source terms, and legal boundaries in your jurisdiction.