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

47 lines
9.8 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 class="ad"><a href="/Ad/Object/99">annons</a></div>
<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: 2026-09-26 22:41</th><th colspan="2" class="tdTitleRight d-table-cell d-sm-none" /></tr><tr><th class="tdHeader">Date</th><th class="tdHeader d-none d-sm-table-cell">Time</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 standardPaddingTop d-none d-sm-table-cell">2026-09-19</td><td class="tdOdd standardPaddingTop d-table-cell d-sm-none"><bold>2026-09-19</bold><br />15:15</td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001001">15:15</span></div></td><td class="tdOdd standardPaddingTop">Frölunda HC
 - 
Växjö Lakers HC</td><td class="tdOdd standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109941','')&#xD;&#xA; ">5 - 4</a></td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(1-1, 2-1, 2-2)
</td><td class="tdOdd standardPaddingTop d-none d-md-table-cell" style="text-align: right">12044</td><td class="tdOdd standardPaddingTop">Scandinavium</td></tr><tr><td class="tdNormal standardPaddingTop d-table-cell d-sm-none">15:15</td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" /><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001002">15:15</span></div></td><td class="tdNormal standardPaddingTop">HV 71
 - 
IF Malmö Redhawks</td><td class="tdNormal standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109942','')&#xD;&#xA; ">4 - 5</a></td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(2-0, 1-3, 1-1, 0-1)
</td><td class="tdNormal standardPaddingTop d-none d-md-table-cell" style="text-align: right">6800</td><td class="tdNormal standardPaddingTop">Husqvarna Garden</td></tr><tr><td class="tdOdd standardPaddingTop d-table-cell d-sm-none">15:15</td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" /><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001003">15:15</span></div></td><td class="tdOdd standardPaddingTop">Linköping HC
 - 
Timrå IK</td><td class="tdOdd standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109943','')&#xD;&#xA; ">3 - 2</a></td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(1-0, 2-2, 0-0)
</td><td class="tdOdd standardPaddingTop d-none d-md-table-cell" style="text-align: right">7900</td><td class="tdOdd standardPaddingTop">Saab Arena</td></tr><tr><td class="tdNormal standardPaddingTop d-table-cell d-sm-none">18:00</td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" /><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001004">18:00</span></div></td><td class="tdNormal standardPaddingTop">Djurgårdens IF
 - 
IF Björklöven</td><td class="tdNormal standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109944','')&#xD;&#xA; ">0 - 3</a></td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(0-1, 0-1, 0-1)
</td><td class="tdNormal standardPaddingTop d-none d-md-table-cell" style="text-align: right">14100</td><td class="tdNormal standardPaddingTop">Avicii Arena</td></tr><tr><td class="tdOdd standardPaddingTop d-table-cell d-sm-none">18:00</td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" /><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001005">18:00</span></div></td><td class="tdOdd standardPaddingTop">Färjestad BK
 - 
Örebro HK</td><td class="tdOdd standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109945','')&#xD;&#xA; ">7 - 0</a></td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(4-0, 2-0, 1-0)
</td><td class="tdOdd standardPaddingTop d-none d-md-table-cell" style="text-align: right">8250</td><td class="tdOdd standardPaddingTop">Löfbergs Arena</td></tr><tr><td class="tdNormal standardPaddingTop d-table-cell d-sm-none">18:00</td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" /><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001006">18:00</span></div></td><td class="tdNormal standardPaddingTop">Luleå HF
 - 
Rögle BK</td><td class="tdNormal standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109946','')&#xD;&#xA; ">4 - 2</a></td><td class="tdNormal standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(0-2, 3-0, 1-0)
</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="tdNormal paddingTop d-none d-sm-table-cell">2026-09-22</td><td class="tdNormal paddingTop d-table-cell d-sm-none"><bold>2026-09-22</bold><br />19:00</td><td class="tdNormal paddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001008">19:00</span></div></td><td class="tdNormal paddingTop">Frölunda HC
 - 
Färjestad BK</td><td class="tdNormal paddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109948','')&#xD;&#xA; ">3 - 2</a></td><td class="tdNormal paddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(1-1, 0-1, 1-0, 0-0, 1-0)
</td><td class="tdNormal paddingTop d-none d-md-table-cell" style="text-align: right">11449</td><td class="tdNormal paddingTop">Scandinavium</td></tr><tr><td class="tdOdd standardPaddingTop d-table-cell d-sm-none">19:00</td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" /><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001009">19:00</span></div></td><td class="tdOdd standardPaddingTop">Linköping HC
 - 
Brynäs IF</td><td class="tdOdd standardPaddingTop" style="white-space: nowrap;"><a href="&#xD;&#xA; javascript:openonlinewindow('/Game/Events/1109949','')&#xD;&#xA; ">2 - 9</a></td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;">
(0-2, 1-3, 1-4)
</td><td class="tdOdd standardPaddingTop d-none d-md-table-cell" style="text-align: right">6812</td><td class="tdOdd standardPaddingTop">Saab Arena</td></tr><tr><td class="tdNormal paddingTop d-none d-sm-table-cell">2027-03-16</td><td class="tdNormal paddingTop d-table-cell d-sm-none"><bold>2027-03-16</bold><br />19:00</td><td class="tdNormal paddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001367">19:00</span></div></td><td class="tdNormal paddingTop">Brynäs IF
 - 
Frölunda HC</td><td class="tdNormal paddingTop" colspan="1"> </td><td class="tdNormal paddingTop d-none d-sm-table-cell" /><td class="tdNormal paddingTop d-none d-md-table-cell" /><td class="tdNormal paddingTop">Monitor ERP Arena</td></tr><tr><td class="tdOdd standardPaddingTop d-table-cell d-sm-none">19:00</td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" /><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" style="white-space: nowrap;"><div class="dateLink"><span href="#" class="lnkTooltip" title="90001368">19:00</span></div></td><td class="tdOdd standardPaddingTop">HV 71
 - 
Färjestad BK</td><td class="tdOdd standardPaddingTop" colspan="1"> </td><td class="tdOdd standardPaddingTop d-none d-sm-table-cell" /><td class="tdOdd standardPaddingTop d-none d-md-table-cell" /><td class="tdOdd standardPaddingTop">Husqvarna Garden</td></tr></table></td></tr></table></td></tr></table></div>
</body>
</html>