Files
swehockey/testdata/schedule_legacy.html
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

40 lines
6.3 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>SHL | stats.swehockey.se</title></head>
<body>
<h1>SHL</h1>
<div id="groupStandingResultContent" class="row"><table class="tblWrapper table table-borderless"><tr><td class="tdWrapper"><table class="tblBorderNoPad"><tr><td class="tdTblBorder"><table class="tblContent"><tr><th colspan="3" class="tdTitle"><h2>Schedule and Results</h2></th><th class="tdTitle d-none d-sm-table-cell" /><th class="tdTitle d-none d-md-table-cell" /><th colspan="2" class="tdTitleRight d-none d-sm-table-cell">
Last update: 2019-05-02 22:02</th><th colspan="2" class="tdTitleRight d-table-cell d-sm-none" /></tr><tr><th class="tdHeader d-none d-sm-table-cell">Round</th><th class="tdHeader">Date</th><th class="tdHeader">Game</th><th class="tdHeader">Result</th><th class="tdHeader d-none d-sm-table-cell" /><th class="tdHeader d-none d-md-table-cell" style="text-align:right">Spectators</th><th class="tdHeader">Venue</th></tr><tr><td class="tdOdd paddingTop d-none d-sm-table-cell">8</td><td class="tdOdd paddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90002023">2019-03-30 15:15</span></div></td><td class="tdOdd paddingTop">Färjestad BK
 - 
HV 71<br /><i>Kvartsfinal 1</i></td><td class="tdOdd paddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/423859','')&#xD;&#xA; ">3 - 2</a></td><td class="tdOdd paddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(0-0, 0-1, 2-1, 0-0, 0-0, 1-0)
</td><td class="tdOdd paddingTop d-none d-md-table-cell" style="text-align: right">8250</td><td class="tdOdd paddingTop">Löfbergs Arena</td></tr><tr><td class="tdNormal d-none d-sm-table-cell">
 
</td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90002026">2019-03-30 18:00</span></div></td><td class="tdNormal standardPaddingTop">Luleå HF
 - 
Växjö Lakers HC<br /><i>Kvartsfinal 4</i></td><td class="tdNormal standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/423862','')&#xD;&#xA; ">4 - 1</a></td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(1-0, 1-0, 2-1)
</td><td class="tdNormal standardPaddingTop d-none d-md-table-cell" style="text-align: right">6150</td><td class="tdNormal standardPaddingTop">Coop Norrbotten Arena </td></tr><tr><td class="tdOdd d-none d-sm-table-cell">
 
</td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90002024">2019-03-31 15:00</span></div></td><td class="tdOdd standardPaddingTop">Frölunda HC
 - 
IF Malmö Redhawks<br /><i>Kvartsfinal 2</i></td><td class="tdOdd standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/423860','')&#xD;&#xA; ">4 - 1</a></td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(0-0, 3-1, 1-0)
</td><td class="tdOdd standardPaddingTop d-none d-md-table-cell" style="text-align: right">6044</td><td class="tdOdd standardPaddingTop">Scandinavium</td></tr><tr><td class="tdNormal d-none d-sm-table-cell">
 
</td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90002025">2019-03-31 15:00</span></div></td><td class="tdNormal standardPaddingTop">Djurgårdens IF
 - 
Skellefteå AIK<br /><i>Kvartsfinal 3</i></td><td class="tdNormal standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/423861','')&#xD;&#xA; ">2 - 3</a></td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(0-0, 0-2, 2-1)
</td><td class="tdNormal standardPaddingTop d-none d-md-table-cell" style="text-align: right">8094</td><td class="tdNormal standardPaddingTop">Hovet, Johanneshov</td></tr><tr><td class="tdNormal paddingTop d-none d-sm-table-cell">22</td><td class="tdNormal paddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90002053">2019-04-30 19:30</span></div></td><td class="tdNormal paddingTop">Frölunda HC
 - 
Djurgårdens IF<br /><i>Final</i></td><td class="tdNormal paddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/423889','')&#xD;&#xA; ">6 - 2</a></td><td class="tdNormal paddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(1-0, 3-0, 2-2)
</td><td class="tdNormal paddingTop d-none d-md-table-cell" style="text-align: right">12044</td><td class="tdNormal paddingTop">Scandinavium</td></tr><tr><td class="tdOdd paddingTop d-none d-sm-table-cell">23</td><td class="tdOdd paddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90002054">2019-05-02 19:30</span></div></td><td class="tdOdd paddingTop">Djurgårdens IF
 - 
Frölunda HC<br /><i>Final</i></td><td class="tdOdd paddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/423890','')&#xD;&#xA; ">2 - 6</a></td><td class="tdOdd paddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(1-3, 0-1, 1-2)
</td><td class="tdOdd paddingTop d-none d-md-table-cell" style="text-align: right">13850</td><td class="tdOdd paddingTop">Ericsson Globe</td></tr></table></td></tr></table></td></tr></table></div>
</body>
</html>