Files
swehockey/testdata/schedule_20961.json
Mats Tornberg 6b701819ca Deterministic swehockey.se schedule crawler
One HTTP GET per season, no browser: the page is server-rendered, so every row
arrives in one response and the extraction becomes a pure function of the HTML.

- layouts[] declares one profile per version of the site's table (modern and
  legacy), each identified by its header row, so a new version of the page is a
  new profile plus a fixture instead of a guess.
- The table is located by header signature, not by a CSS path.
- Every cell is shape-checked, and facts the site renders twice (desktop and
  mobile date, time in the tooltip) must agree.
- Cross-field invariants: periods sum to the score, ids are unique, played games
  always have an event link and unplayed ones never do.
- Output is sorted by date, time and id, so it does not inherit the site's row
  order, and it carries no fetch timestamp: same HTML, same bytes.
- Drift is a typed error and exit code 3, with the header it actually found.
- Tests run on committed HTML fixtures and golden JSON; TestLive is opt-in.

Verified over the full SHL archive: 27/27 seasons, 2119 games, 1779 played.
2026-09-27 08:47:25 +00:00

278 lines
5.3 KiB
JSON

{
"season_id": 20961,
"layout": "modern",
"title": "Schedule and Results",
"last_update": "2026-09-26 22:41",
"games": [
{
"game_id": "90001001",
"event_id": "1109941",
"date": "2026-09-19",
"time": "15:15",
"home": "Frölunda HC",
"away": "Växjö Lakers HC",
"home_goals": 5,
"away_goals": 4,
"periods": [
{
"label": "1",
"home": 1,
"away": 1
},
{
"label": "2",
"home": 2,
"away": 1
},
{
"label": "3",
"home": 2,
"away": 2
}
],
"spectators": 12044,
"venue": "Scandinavium"
},
{
"game_id": "90001002",
"event_id": "1109942",
"date": "2026-09-19",
"time": "15:15",
"home": "HV 71",
"away": "IF Malmö Redhawks",
"home_goals": 4,
"away_goals": 5,
"periods": [
{
"label": "1",
"home": 2,
"away": 0
},
{
"label": "2",
"home": 1,
"away": 3
},
{
"label": "3",
"home": 1,
"away": 1
},
{
"label": "4",
"home": 0,
"away": 1
}
],
"spectators": 6800,
"venue": "Husqvarna Garden"
},
{
"game_id": "90001003",
"event_id": "1109943",
"date": "2026-09-19",
"time": "15:15",
"home": "Linköping HC",
"away": "Timrå IK",
"home_goals": 3,
"away_goals": 2,
"periods": [
{
"label": "1",
"home": 1,
"away": 0
},
{
"label": "2",
"home": 2,
"away": 2
},
{
"label": "3",
"home": 0,
"away": 0
}
],
"spectators": 7900,
"venue": "Saab Arena"
},
{
"game_id": "90001004",
"event_id": "1109944",
"date": "2026-09-19",
"time": "18:00",
"home": "Djurgårdens IF",
"away": "IF Björklöven",
"home_goals": 0,
"away_goals": 3,
"periods": [
{
"label": "1",
"home": 0,
"away": 1
},
{
"label": "2",
"home": 0,
"away": 1
},
{
"label": "3",
"home": 0,
"away": 1
}
],
"spectators": 14100,
"venue": "Avicii Arena"
},
{
"game_id": "90001005",
"event_id": "1109945",
"date": "2026-09-19",
"time": "18:00",
"home": "Färjestad BK",
"away": "Örebro HK",
"home_goals": 7,
"away_goals": 0,
"periods": [
{
"label": "1",
"home": 4,
"away": 0
},
{
"label": "2",
"home": 2,
"away": 0
},
{
"label": "3",
"home": 1,
"away": 0
}
],
"spectators": 8250,
"venue": "Löfbergs Arena"
},
{
"game_id": "90001006",
"event_id": "1109946",
"date": "2026-09-19",
"time": "18:00",
"home": "Luleå HF",
"away": "Rögle BK",
"home_goals": 4,
"away_goals": 2,
"periods": [
{
"label": "1",
"home": 0,
"away": 2
},
{
"label": "2",
"home": 3,
"away": 0
},
{
"label": "3",
"home": 1,
"away": 0
}
],
"spectators": 6150,
"venue": "Coop Norrbotten Arena"
},
{
"game_id": "90001008",
"event_id": "1109948",
"date": "2026-09-22",
"time": "19:00",
"home": "Frölunda HC",
"away": "Färjestad BK",
"home_goals": 3,
"away_goals": 2,
"periods": [
{
"label": "1",
"home": 1,
"away": 1
},
{
"label": "2",
"home": 0,
"away": 1
},
{
"label": "3",
"home": 1,
"away": 0
},
{
"label": "4",
"home": 0,
"away": 0
},
{
"label": "5",
"home": 1,
"away": 0
}
],
"spectators": 11449,
"venue": "Scandinavium"
},
{
"game_id": "90001009",
"event_id": "1109949",
"date": "2026-09-22",
"time": "19:00",
"home": "Linköping HC",
"away": "Brynäs IF",
"home_goals": 2,
"away_goals": 9,
"periods": [
{
"label": "1",
"home": 0,
"away": 2
},
{
"label": "2",
"home": 1,
"away": 3
},
{
"label": "3",
"home": 1,
"away": 4
}
],
"spectators": 6812,
"venue": "Saab Arena"
},
{
"game_id": "90001367",
"date": "2027-03-16",
"time": "19:00",
"home": "Brynäs IF",
"away": "Frölunda HC",
"home_goals": null,
"away_goals": null,
"periods": [],
"venue": "Monitor ERP Arena"
},
{
"game_id": "90001368",
"date": "2027-03-16",
"time": "19:00",
"home": "HV 71",
"away": "Färjestad BK",
"home_goals": null,
"away_goals": null,
"periods": [],
"venue": "Husqvarna Garden"
}
]
}