NHL.com Goal Simulations

russ99

Registered User
Jun 9, 2011
3,968
2,964
Check out the new goal simulation feature on NHL.com


I wonder if this data can be scraped. About time puck and player tracker data gets used to create new, more accurate analytics.
 

morehockeystats

Unusual hockey stats
Dec 13, 2016
635
314
Columbus
morehockeystats.com
Yes you can!

Get the boxscore JSON:
Assume GAME_ID = 2024020008 (8th game of 2024/25 season)

Then in the boxscore['plays'] there are events, which have key 'typeDescKey'. For those of them that are of type 'goal', note the url appearing in the field pptReplayUrl :
E.g.

The result will be a 120-item collection of when who was onIce where. A sample entry of an on-ice entry is like that:
JSON:
"52004": {
        "id": 52004,
        "playerId": 8480145,
        "x": 2299.7315,
        "y": 212.9376,
        "sweaterNumber": 4,
        "teamId": 52,
        "teamAbbrev": "WPG"
},
Note that the ID is team Id and the sweater number padded to 3 digits with zeroes.
But really you should work with the playerId, which is the universal nhl player ID. The items are separated by 0.1 seconds, so the whole collection covers the 12 seconds preceding the goal. Note that the timeStamp is in REAL EPOCH TIME, not in game time at all (use the event data from PBP for that)

The PUCK object is the one indexed as "1"

Attempting to scrape other (than goal) events leads to error 'Access denied'.
 
Last edited:

Michael Farkas

Celebrate 68
Jun 28, 2006
14,978
10,450
NYC
www.youtube.com
Not that I could find.
I figured. That's what the private firms are getting...but our access is denied. Oh well, that's life. But this has some interesting upside.

A good goalie is aware of all the shooting options and he has to track them. So, if we have the player positioning, we could adjust that for shooting bias and prowess (approximately) and make some in-roads with that.
 

morehockeystats

Unusual hockey stats
Dec 13, 2016
635
314
Columbus
morehockeystats.com
I figured. That's what the private firms are getting...but our access is denied. Oh well, that's life. But this has some interesting upside.

A good goalie is aware of all the shooting options and he has to track them. So, if we have the player positioning, we could adjust that for shooting bias and prowess (approximately) and make some in-roads with that.
I wonder if the coordinates in the sprite will correspond to the coordinates in the boxscore. But I won't make that test myself yet, I've my hands full with other things, I just got everything seemingly stable with this season changes, and ARI -> UTA shift.
 
  • Like
Reactions: DatsyukToZetterberg

Foxy

Registered User
Oct 5, 2020
168
272
Is anyone else having trouble accessing the sprites data? It seems like the NHL realized the value of the dataset, and it's no longer freely available.
 

morehockeystats

Unusual hockey stats
Dec 13, 2016
635
314
Columbus
morehockeystats.com
Is anyone else having trouble accessing the sprites data? It seems like the NHL realized the value of the dataset, and it's no longer freely available.
It still is. You just have to emulate a full browser call, e.g.

Bash:
curl 'https://wsr.nhle.com/sprites/20242025/2024020096/ev77.json' \
  -H 'accept: */*' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'origin: https://www.nhl.com' \
  -H 'priority: u=1, i' \
  -H 'referer: https://www.nhl.com/' \
  -H 'sec-ch-ua: "Brave";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'sec-gpc: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'

Open the Web Inspector, choose Network tab, load a goal simulation, and then right click on the evNNN.json file. Select copy -> copy as cURL.

Through trial and error you may figure out which of these headers may be omitted.

Everything your browser accesses can be "robotized", it just takes more time sometimes to reverse engineer that sequence.
 
  • Like
Reactions: Bear of Bad News

Foxy

Registered User
Oct 5, 2020
168
272
It still is. You just have to emulate a full browser call, e.g.

Bash:
curl 'https://wsr.nhle.com/sprites/20242025/2024020096/ev77.json' \
  -H 'accept: */*' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'origin: https://www.nhl.com' \
  -H 'priority: u=1, i' \
  -H 'referer: https://www.nhl.com/' \
  -H 'sec-ch-ua: "Brave";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'sec-gpc: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'

Open the Web Inspector, choose Network tab, load a goal simulation, and then right click on the evNNN.json file. Select copy -> copy as cURL.

Through trial and error you may figure out which of these headers may be omitted.

Everything your browser accesses can be "robotized", it just takes more time sometimes to reverse engineer that sequence.

Thanks for the help! As you suggested, I was able to remove some of the headers and got the call to work using just the referer and user-agent.
Python:
    url = "https://wsr.nhle.com/sprites/20242025/2024020096/ev77.json"
    headers = {
        "referer": "https://www.nhl.com/",
        "user-agent": "Chrome/130.0.0.0"
    }
    response = requests.get(url, headers=headers)
    print(response.json())
 
  • Like
Reactions: Bear of Bad News

Ad

Upcoming events

Ad

Ad