Python 用于比较足球运动员的大学母校与 NFL Fantasy Football 总产量的网络抓取方法
Python Webscraping Approach for Comparing Football Players' college alma maters with total NFL Fantasy Football output
我正在寻找一个数据科学项目,在该项目中我将能够按球员就读的大学汇总梦幻足球得分(例如,阿拉巴马州在 NFL 中有 56 名现役球员,所以我将通过一个数据库并将他们所有的幻想点加起来与其他学校进行比较。
我打算使用 Beautiful Soup 来收集球员和统计数据的行,并最终收集梦幻足球积分。
但是,我无法弄清楚如何提取玩家的大学母校。为此,我必须:
- 点击每个“玩家”的名字
- 为一行“大学”抓取数百名 NFL 球员的每一个个人资料
- 将所有这些信息放入其自己的栏中。
这里有什么建议吗?
不需要 Selenium 或其他无头的自动化浏览器。太过分了。
如果您查看浏览器的网络流量,您会注意到您的浏览器向此 REST API 端点发出 POST 请求:https://fantasydata.com/NFL_FantasyStats/FantasyStats_Read
如果 POST 请求格式正确,API 响应 JSON,其中包含每个玩家的信息。通常,此信息将用于使用 JavaScript 异步填充 DOM。那里有很多信息,但不幸的是,大学信息不是 JSON 响应的一部分。但是,有一个字段 PlayerUrlString
,它是给定玩家个人资料页面的相对 URL,它确实包含大学名称。所以:
- 向 API 发出 POST 请求以获取有关所有玩家的信息
对于响应中的每个玩家 JSON:
- 访问该玩家的个人资料
- 使用BeautifulSoup从当前文件中提取学院名称
玩家资料
代码:
def main():
import requests
from bs4 import BeautifulSoup
url = "https://fantasydata.com/NFL_FantasyStats/FantasyStats_Read"
data = {
"sort": "FantasyPoints-desc",
"pageSize": "50",
"filters.season": "2020",
"filters.seasontype": "1",
"filters.scope": "1",
"filters.subscope": "1",
"filters.aggregatescope": "1",
"filters.range": "3",
}
response = requests.post(url, data=data)
response.raise_for_status()
players = response.json()["Data"]
for player in players:
url = "https://fantasydata.com" + player["PlayerUrlString"]
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.content, "html.parser")
college = soup.find("dl", {"class": "dl-horizontal"}).findAll("dd")[-1].text.strip()
print(player["Name"] + " went to " + college)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
输出:
Patrick Mahomes went to Texas Tech
Kyler Murray went to Oklahoma
Aaron Rodgers went to California
Russell Wilson went to Wisconsin
Josh Allen went to Wyoming
Deshaun Watson went to Clemson
Ryan Tannehill went to Texas A&M
Lamar Jackson went to Louisville
Dalvin Cook went to Florida State
...
您还可以在 data
词典中编辑 pageSize
POST 参数。 50
对应于 JSON 响应中前 50 名玩家的信息(根据其他 POST 参数设置的过滤器)。更改此值将在 JSON 响应中产生更多或更少的玩家。
我同意,如果他们在那里,API 是必经之路。我的第二个“转到”是 pandas
' .read_html()
(它在后台使用 BeautifulSoup 来解析 <table>
标签。这是使用 ESPN api 的替代解决方案获取团队名单 links,然后使用 pandas 从每个 link 中提取 table。省去了必须遍历每个球员以获得大学的麻烦(我希望他们只是有一个 api 返回所有玩家。nfl.com 曾经有过,但不再公开,据我所知)。
代码:
import requests
import pandas as pd
url = 'https://site.web.api.espn.com/apis/common/v3/sports/football/nfl/athletes/101'
all_teams = []
roster_links = []
for i in range(1,35):
url = 'http://site.api.espn.com/apis/site/v2/sports/football/nfl/teams/{teamId}'.format(teamId=i)
jsonData = requests.get(url).json()
print (jsonData['team']['displayName'])
for link in jsonData['team']['links']:
if link['text'] == 'Roster':
roster_links.append(link['href'])
break
for link in roster_links:
print (link)
tables = pd.read_html(link)
df = pd.concat(tables).drop('Unnamed: 0',axis=1)
df['Jersey'] = df['Name'].str.replace("([A-Za-z.' ]+)", '')
df['Name'] = df['Name'].str.extract("([A-Za-z.' ]+)")
all_teams.append(df)
final_df = pd.concat(all_teams).reset_index(drop=True)
输出:
print (final_df)
Name POS Age HT WT Exp College Jersey
0 Matt Ryan QB 35 6' 4" 217 lbs 13 Boston College 2
1 Matt Schaub QB 39 6' 6" 245 lbs 17 Virginia 8
2 Todd Gurley II RB 26 6' 1" 224 lbs 6 Georgia 21
3 Brian Hill RB 25 6' 1" 219 lbs 4 Wyoming 23
4 Qadree Ollison RB 24 6' 1" 232 lbs 2 Pittsburgh 30
... .. ... ... ... .. ... ...
1772 Jonathan Owens S 25 5' 11" 210 lbs 2 Missouri Western 36
1773 Justin Reid S 23 6' 1" 203 lbs 3 Stanford 20
1774 Ka'imi Fairbairn PK 26 6' 0" 183 lbs 5 UCLA 7
1775 Bryan Anger P 32 6' 3" 205 lbs 9 California 9
1776 Jon Weeks LS 34 5' 10" 242 lbs 11 Baylor 46
[1777 rows x 8 columns]
我正在寻找一个数据科学项目,在该项目中我将能够按球员就读的大学汇总梦幻足球得分(例如,阿拉巴马州在 NFL 中有 56 名现役球员,所以我将通过一个数据库并将他们所有的幻想点加起来与其他学校进行比较。
我打算使用 Beautiful Soup 来收集球员和统计数据的行,并最终收集梦幻足球积分。
但是,我无法弄清楚如何提取玩家的大学母校。为此,我必须:
- 点击每个“玩家”的名字
- 为一行“大学”抓取数百名 NFL 球员的每一个个人资料
- 将所有这些信息放入其自己的栏中。
这里有什么建议吗?
不需要 Selenium 或其他无头的自动化浏览器。太过分了。
如果您查看浏览器的网络流量,您会注意到您的浏览器向此 REST API 端点发出 POST 请求:https://fantasydata.com/NFL_FantasyStats/FantasyStats_Read
如果 POST 请求格式正确,API 响应 JSON,其中包含每个玩家的信息。通常,此信息将用于使用 JavaScript 异步填充 DOM。那里有很多信息,但不幸的是,大学信息不是 JSON 响应的一部分。但是,有一个字段 PlayerUrlString
,它是给定玩家个人资料页面的相对 URL,它确实包含大学名称。所以:
- 向 API 发出 POST 请求以获取有关所有玩家的信息
对于响应中的每个玩家 JSON:
- 访问该玩家的个人资料
- 使用BeautifulSoup从当前文件中提取学院名称 玩家资料
代码:
def main():
import requests
from bs4 import BeautifulSoup
url = "https://fantasydata.com/NFL_FantasyStats/FantasyStats_Read"
data = {
"sort": "FantasyPoints-desc",
"pageSize": "50",
"filters.season": "2020",
"filters.seasontype": "1",
"filters.scope": "1",
"filters.subscope": "1",
"filters.aggregatescope": "1",
"filters.range": "3",
}
response = requests.post(url, data=data)
response.raise_for_status()
players = response.json()["Data"]
for player in players:
url = "https://fantasydata.com" + player["PlayerUrlString"]
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.content, "html.parser")
college = soup.find("dl", {"class": "dl-horizontal"}).findAll("dd")[-1].text.strip()
print(player["Name"] + " went to " + college)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
输出:
Patrick Mahomes went to Texas Tech
Kyler Murray went to Oklahoma
Aaron Rodgers went to California
Russell Wilson went to Wisconsin
Josh Allen went to Wyoming
Deshaun Watson went to Clemson
Ryan Tannehill went to Texas A&M
Lamar Jackson went to Louisville
Dalvin Cook went to Florida State
...
您还可以在 data
词典中编辑 pageSize
POST 参数。 50
对应于 JSON 响应中前 50 名玩家的信息(根据其他 POST 参数设置的过滤器)。更改此值将在 JSON 响应中产生更多或更少的玩家。
我同意,如果他们在那里,API 是必经之路。我的第二个“转到”是 pandas
' .read_html()
(它在后台使用 BeautifulSoup 来解析 <table>
标签。这是使用 ESPN api 的替代解决方案获取团队名单 links,然后使用 pandas 从每个 link 中提取 table。省去了必须遍历每个球员以获得大学的麻烦(我希望他们只是有一个 api 返回所有玩家。nfl.com 曾经有过,但不再公开,据我所知)。
代码:
import requests
import pandas as pd
url = 'https://site.web.api.espn.com/apis/common/v3/sports/football/nfl/athletes/101'
all_teams = []
roster_links = []
for i in range(1,35):
url = 'http://site.api.espn.com/apis/site/v2/sports/football/nfl/teams/{teamId}'.format(teamId=i)
jsonData = requests.get(url).json()
print (jsonData['team']['displayName'])
for link in jsonData['team']['links']:
if link['text'] == 'Roster':
roster_links.append(link['href'])
break
for link in roster_links:
print (link)
tables = pd.read_html(link)
df = pd.concat(tables).drop('Unnamed: 0',axis=1)
df['Jersey'] = df['Name'].str.replace("([A-Za-z.' ]+)", '')
df['Name'] = df['Name'].str.extract("([A-Za-z.' ]+)")
all_teams.append(df)
final_df = pd.concat(all_teams).reset_index(drop=True)
输出:
print (final_df)
Name POS Age HT WT Exp College Jersey
0 Matt Ryan QB 35 6' 4" 217 lbs 13 Boston College 2
1 Matt Schaub QB 39 6' 6" 245 lbs 17 Virginia 8
2 Todd Gurley II RB 26 6' 1" 224 lbs 6 Georgia 21
3 Brian Hill RB 25 6' 1" 219 lbs 4 Wyoming 23
4 Qadree Ollison RB 24 6' 1" 232 lbs 2 Pittsburgh 30
... .. ... ... ... .. ... ...
1772 Jonathan Owens S 25 5' 11" 210 lbs 2 Missouri Western 36
1773 Justin Reid S 23 6' 1" 203 lbs 3 Stanford 20
1774 Ka'imi Fairbairn PK 26 6' 0" 183 lbs 5 UCLA 7
1775 Bryan Anger P 32 6' 3" 205 lbs 9 California 9
1776 Jon Weeks LS 34 5' 10" 242 lbs 11 Baylor 46
[1777 rows x 8 columns]