Reputation: 69
This is my first-time web scraping with beautiful soup and wanted to do a little project with hockey since I am a huge fan of the sport. I am a little stuck and wondering how to retrieve the header names of the stats for each player.
Here is my current code:
from bs4 import BeautifulSoup
import requests
import re
import pandas as pd
url = "http://www.espn.com/nhl/statistics/player/_/stat/points/year/2020/seasontype/2"
page = requests.get(url)
soup = BeautifulSoup(page.text, 'html.parser')
allStats = []
players = soup.find_all('tr', attrs={'class':re.compile('row player')})
for player in players:
stats = [stat.get_text() for stat in player.find_all('td')]
allStats += stats
body = soup.find_all('div', {"class":"wrapper"})
print(allStats)
allColumns = []
headers = soup.find_all('tr', attrs = {'class': 'colhead'})
for col in headers:
columns = [col.get_text() for col in headers.find_all('td')]
allColumns += columns
print(allColumns)
I am currently getting an error that says "ResultSet object has no attribute '%s' for the line
headers = soup.find_all('tr', attrs = {'class': 'colhead'})
Eventually, I want to get a list of all of the Stat Names being tracked and use that as the columns in a pandas dataframe that lists each player and their corresponding stats.
What's the best way to achieve this?
Thanks for your help!
Upvotes: 0
Views: 847
Reputation: 28630
Just as alternative, there is an espn api to get the statistics. But it's not documented as to how to pull a specicif season or season type:
http://site.api.espn.com/apis/site/v2/sports/football/nfl/statistics
However, you can pull out player stats from the nhl.com site with their api:
import requests
import pandas as pd
url = 'https://statsapi.web.nhl.com/api/v1/teams'
jsonDataTeams = requests.get(url).json()
teamsDict = {}
for team in jsonDataTeams['teams']:
rosterLink = 'https://statsapi.web.nhl.com' + team['link'] + '/roster'
teamsDict.update({team['name']:rosterLink})
playersDict = {}
for team, link in teamsDict.items():
jsonDataPlayers = requests.get(link).json()
for player in jsonDataPlayers['roster']:
playerLink = 'https://statsapi.web.nhl.com' + player['person']['link'] + '/stats'
playersDict.update({player['person']['fullName']:playerLink})
print ('Downloaded: %s' %team)
rows = []
count = 0
tot = len(playersDict)
for player, link in playersDict.items():
count+=1
payload = {'stats':'statsSingleSeason',
'season':'20192020'}
playerStats = requests.get(link, params=payload).json()
try:
row = playerStats['stats'][0]['splits'][0]['stat']
row.update({'player':player})
rows.append(row)
print ('%s of %s - Stats collected for: %s' %(count,tot,player))
except:
print ('%s of %s - No stats available for: %s' %(count,tot,player))
df = pd.DataFrame(rows)
Output:
print(df.head(20).to_string())
timeOnIce ot shutouts ties wins losses saves powerPlaySaves shortHandedSaves evenSaves shortHandedShots evenShots powerPlayShots savePercentage goalAgainstAverage games gamesStarted shotsAgainst goalsAgainst timeOnIcePerGame powerPlaySavePercentage shortHandedSavePercentage evenStrengthSavePercentage player assists goals pim shots hits powerPlayGoals powerPlayPoints powerPlayTimeOnIce evenTimeOnIce penaltyMinutes faceOffPct shotPct gameWinningGoals overTimeGoals shortHandedGoals shortHandedPoints shortHandedTimeOnIce blocked plusMinus points shifts evenTimeOnIcePerGame shortHandedTimeOnIcePerGame powerPlayTimeOnIcePerGame
0 2340:03 3.0 1.0 0.0 16.0 20.0 1186.0 132.0 26.0 1028.0 29.0 1110.0 155.0 0.917 2.7692 40 39.0 1294.0 108.0 58:30 85.161290 89.655172 92.612613 Corey Crawford NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 1179:36 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 69 NaN NaN NaN 17:05 NaN NaN NaN Travis Zajac 16.0 9.0 28.0 72.0 58.0 1.0 1.0 39:55 942:36 28 52.84 12.5 1.0 0.0 1.0 3.0 197:05 39.0 -12.0 25.0 1476.0 13:39 02:51 00:34
2 1504:25 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 68 NaN NaN NaN 22:07 NaN NaN NaN P.K. Subban 11.0 7.0 79.0 151.0 88.0 2.0 6.0 160:14 1232:41 79 0.00 4.6 3.0 0.0 0.0 0.0 111:30 90.0 -21.0 18.0 1883.0 18:07 01:38 02:21
3 1112:11 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 65 NaN NaN NaN 17:06 NaN NaN NaN Kyle Palmieri 20.0 25.0 41.0 155.0 74.0 11.0 18.0 206:17 891:54 41 32.43 16.1 3.0 1.0 0.0 0.0 14:00 36.0 -4.0 45.0 1374.0 13:43 00:12 03:10
4 1020:59 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 51 NaN NaN NaN 20:01 NaN NaN NaN Dmitry Kulikov 8.0 2.0 32.0 54.0 104.0 0.0 0.0 06:20 944:51 32 0.00 3.7 1.0 0.0 0.0 0.0 69:48 77.0 -4.0 10.0 1241.0 18:31 01:22 00:07
5 538:48 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 27 NaN NaN NaN 19:57 NaN NaN NaN Ryan Murray 7.0 2.0 4.0 32.0 12.0 0.0 2.0 23:15 460:25 4 0.00 6.3 0.0 0.0 0.0 0.0 55:08 50.0 -9.0 9.0 708.0 17:03 02:02 00:51
6 1584:26 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 69 NaN NaN NaN 22:57 NaN NaN NaN Damon Severson 23.0 8.0 52.0 114.0 81.0 3.0 10.0 137:22 1277:50 52 0.00 7.0 2.0 0.0 0.0 0.0 169:14 97.0 -20.0 31.0 1924.0 18:31 02:27 01:59
7 417:53 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 29 NaN NaN NaN 14:24 NaN NaN NaN Connor Carrick 5.0 1.0 17.0 25.0 27.0 0.0 0.0 02:29 402:01 17 0.00 4.0 1.0 0.0 0.0 0.0 13:23 39.0 -6.0 6.0 564.0 13:51 00:27 00:05
8 969:45 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 66 NaN NaN NaN 14:41 NaN NaN NaN Nikita Gusev 31.0 13.0 12.0 158.0 12.0 5.0 15.0 164:10 805:25 12 33.33 8.2 2.0 0.0 0.0 0.0 00:10 7.0 -15.0 44.0 1173.0 12:12 00:00 02:29
9 677:27 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 43 NaN NaN NaN 15:45 NaN NaN NaN Andreas Johnsson 13.0 8.0 14.0 78.0 43.0 4.0 5.0 86:53 581:07 14 0.00 10.3 1.0 0.0 0.0 0.0 09:27 21.0 0.0 21.0 897.0 13:30 00:13 02:01
10 1003:22 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 56 NaN NaN NaN 17:55 NaN NaN NaN Will Butcher 17.0 4.0 6.0 62.0 31.0 0.0 1.0 72:35 918:16 6 0.00 6.5 1.0 0.0 0.0 0.0 12:31 75.0 -8.0 21.0 1269.0 16:23 00:13 01:17
11 899:52 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 68 NaN NaN NaN 13:14 NaN NaN NaN Miles Wood 12.0 11.0 57.0 141.0 102.0 0.0 0.0 53:47 841:58 57 0.00 7.8 1.0 0.0 0.0 0.0 04:07 26.0 -19.0 23.0 1169.0 12:22 00:03 00:47
12 1057:23 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 65 NaN NaN NaN 16:16 NaN NaN NaN Pavel Zacha 24.0 8.0 14.0 97.0 62.0 3.0 8.0 106:40 818:37 14 43.27 8.2 1.0 0.0 2.0 4.0 132:06 19.0 -12.0 32.0 1343.0 12:35 02:01 01:38
13 2684:01 8.0 3.0 0.0 22.0 14.0 1328.0 160.0 36.0 1132.0 42.0 1225.0 185.0 0.915 2.7720 47 43.0 1452.0 124.0 57:06 86.486486 85.714286 92.408163 Mackenzie Blackwood NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
14 838:58 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 60 NaN NaN NaN 13:58 NaN NaN NaN Jesper Bratt 16.0 16.0 6.0 101.0 29.0 1.0 6.0 120:47 716:17 6 23.07 15.8 2.0 0.0 0.0 0.0 01:54 22.0 -6.0 32.0 1096.0 11:56 00:01 02:00
15 122:45 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 12 NaN NaN NaN 10:13 NaN NaN NaN Michael McLeod 2.0 0.0 4.0 11.0 19.0 0.0 0.0 00:12 120:51 4 50.00 0.0 0.0 0.0 0.0 0.0 01:42 6.0 4.0 2.0 158.0 10:04 00:08 00:01
16 1047:32 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 58 NaN NaN NaN 18:03 NaN NaN NaN Nico Hischier 22.0 14.0 12.0 123.0 48.0 2.0 10.0 155:56 838:15 12 51.73 11.4 1.0 0.0 1.0 2.0 53:21 37.0 -16.0 36.0 1278.0 14:27 00:55 02:41
17 967:47 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 61 NaN NaN NaN 15:51 NaN NaN NaN Jack Hughes 14.0 7.0 10.0 123.0 12.0 4.0 9.0 193:42 772:59 10 36.14 5.7 2.0 1.0 0.0 0.0 01:06 21.0 -26.0 21.0 1104.0 12:40 00:01 03:10
18 1523:44 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 68 NaN NaN NaN 22:24 NaN NaN NaN Ryan Pulock 25.0 10.0 14.0 163.0 110.0 3.0 8.0 120:08 1302:12 14 0.00 6.1 3.0 1.0 0.0 0.0 101:24 139.0 8.0 35.0 1788.0 19:09 01:29 01:46
19 1106:16 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 64 NaN NaN NaN 17:17 NaN NaN NaN Johnny Boychuk 9.0 2.0 14.0 96.0 135.0 1.0 1.0 05:24 968:51 14 0.00 2.1 0.0 0.0 0.0 0.0 132:01 128.0 -11.0 11.0 1420.0 15:08 02:03 00:05
Upvotes: 1
Reputation: 6677
There's a typo in your headers
iteration that's why you're getting the error,
AttributeError: ResultSet object has no attribute 'find_all'. You're probably treating a list of elements like a single element. Did you call find_all() when you meant to call find()?
I suppose the expected result is as follows.
allColumns = []
headers = soup.find_all('tr', attrs = {'class': 'colhead'})
for header in headers:
columns = [head.get_text() for head in header.find_all('td')]
allColumns += columns
>>>
['', 'PP', 'SH', 'RK', 'PLAYER', 'TEAM', 'GP', 'G', 'A', 'PTS', '+/-', 'PIM', 'PTS/G', 'SOG', 'PCT', 'GWG', 'G', 'A', 'G', 'A', '', 'PP', 'SH', 'RK', 'PLAYER', 'TEAM', 'GP', 'G', 'A', 'PTS', '+/-', 'PIM', 'PTS/G', 'SOG', 'PCT', 'GWG', 'G', 'A', 'G', 'A', '', 'PP', 'SH', 'RK', 'PLAYER', 'TEAM', 'GP', 'G', 'A', 'PTS', '+/-', 'PIM', 'PTS/G', 'SOG', 'PCT', 'GWG', 'G', 'A', 'G', 'A', '', 'PP', 'SH', 'RK', 'PLAYER', 'TEAM', 'GP', 'G', 'A', 'PTS', '+/-', 'PIM', 'PTS/G', 'SOG', 'PCT', 'GWG', 'G', 'A', 'G', 'A']
Upvotes: 1