我希望从以下NCAA统计信息页面中刮取3部大学篮球的统计数据:
https://stats.ncaa.org/rankings/change_sport_year_div
要转到我所在的页面,请单击链接,然后依次选择“体育” =“男篮”,“年= 2019-2020”和“分部= III”
点击链接后,左上角表格上方会出现一个下拉列表。它被标记为“其他统计信息”。对于每个统计信息,都有一个表,您可以从中获得一个excel文件,但我想提高效率。我在想可能有一种方法可以使用BeautifulSoup(甚至pd.read_html)遍历下拉栏,以获得列出的每个统计数据的数据框。有没有办法做到这一点?手动检查每个统计信息,下载excel文件,然后将excel文件读入熊猫会很痛苦。谢谢。
答案 0 :(得分:1)
这是我的建议,使用requests
,beautifulsoup
和Scott Rome中的html表解析器的组合(我对parse_html_table
函数进行了一些修改以删除\n
并去除空格。
首先,您可以在检查页面的源代码时看到它的格式:"https://stats.ncaa.org/rankings/national_ranking?academic_year=2020.0&division=3.0&ranking_period=110.0&sport_code=MBB&stat_seq=145.0"
例如统计数据145,即“得分得分”。
因此,您可以通过将145.0
替换为与不同统计信息相对应的值来在每个这些url上使用以下代码,您可以在检查页面的源代码时看到这些统计信息。
# <option value="625">3-pt Field Goal Attempts</option>
# <option value="474">Assist Turnover Ratio</option>
# <option value="216">Assists Per Game</option>
# ...
对于特定的统计信息,例如评分违规,您可以使用以下代码将表提取为pandas DataFrame:
import pandas as pd
from bs4 import BeautifulSoup
import requests
el = "https://stats.ncaa.org/rankings/national_ranking?academic_year=2020.0&division=3.0&ranking_period=110.0&sport_code=MBB&stat_seq=145.0"
page = requests.get(el).content.decode('utf-8')
soup = BeautifulSoup(page, "html.parser")
ta = soup.find_all('table', {"id": "rankings_table"})
# Scott Rome function tweaked a bit
def parse_html_table(table):
n_columns = 0
n_rows = 0
column_names = []
# Find number of rows and columns
# we also find the column titles if we can
for row in table.find_all('tr'):
# Determine the number of rows in the table
td_tags = row.find_all('td')
if len(td_tags) > 0:
n_rows += 1
if n_columns == 0:
# Set the number of columns for our table
n_columns = len(td_tags)
# Handle column names if we find them
th_tags = row.find_all('th')
if len(th_tags) > 0 and len(column_names) == 0:
for th in th_tags:
column_names.append(th.get_text())
# Safeguard on Column Titles
if len(column_names) > 0 and len(column_names) != n_columns:
raise Exception("Column titles do not match the number of columns")
columns = column_names if len(column_names) > 0 else range(0, n_columns)
df = pd.DataFrame(columns=columns,
index=range(0, n_rows))
row_marker = 0
for row in table.find_all('tr'):
column_marker = 0
columns = row.find_all('td')
for column in columns:
df.iat[row_marker, column_marker] = column.get_text()
column_marker += 1
if len(columns) > 0:
row_marker += 1
# remove \n
for col in df:
try:
df[col] = df[col].str.replace("\n", "")
df[col] = df[col].str.strip()
except ValueError:
pass
# Convert to float if possible
for col in df:
try:
df[col] = df[col].astype(float)
except ValueError:
pass
return df
example = parse_html_table(ta[0])
结果是
Rank Team GM W-L PTS PPG
0 1 Greenville (SLIAC) 27.0 14-13 3,580 132.6
1 2 Grinnell (Midwest Conference) 25.0 13-12 2,717 108.7
2 3 Pacific (OR) (NWC) 25.0 7-18 2,384 95.4
3 4 Whitman (NWC) 28.0 20-8 2,646 94.5
4 5 Valley Forge (ACAA) 22.0 12-11 2,047 93.0
...
现在,您要做的就是将其应用于上述所有统计信息值。
您可以利用上面的代码,并将其在for循环中应用于网址"https://stats.ncaa.org/rankings/national_ranking?academic_year=2020.0&division=3.0&ranking_period=110.0&sport_code=MBB&stat_seq={}".format(stat)
,其中stat
在所有可能值的列表中。
希望有帮助。
答案 1 :(得分:0)
也许是更简洁的方法:
import requests as rq
from bs4 import BeautifulSoup as bs
import pandas as pd
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0"}
params = {"sport_code": "MBB", "stat_seq": "518", "academic_year": "2020.0", "division":"3.0", "ranking_period":"110.0"}
url = "https://stats.ncaa.org/rankings/national_ranking"
resp = rq.post(url, headers=headers, params=params)
soup = bs(resp.content)
colnames = [th.text.strip() for th in soup.find_all("thead")[0].find_all("th")]
data = [[td.text.strip() for td in tr.find_all('td')] for tr in soup.find_all('tbody')[0].find_all("tr")]
df = pd.DataFrame(data, columns=colnames)
df.astype({"GM": 'int32'}).dtypes # convert column in type u want
您必须查看XHR请求[在Mozilla:F12->网络-> XHR]。
当您从下拉列表中选择一项时,这将通过以下URL发出发布请求: https://stats.ncaa.org/rankings/national_ranking 。
发出此发布请求需要一些参数,其中之一是“ stat_seq”。该值对应于下拉选项的“值”。
检查器为您提供“值” -StatName对应关系的列表:
<option value="625" selected="selected">3-pt Field Goal Attempts</option>
<option value="474">Assist Turnover Ratio</option>
<option value="216">Assists Per Game</option>
<option value="214">Blocked Shots Per Game</option>
<option value="859">Defensive Rebounds per Game</option>
<option value="642">Fewest Fouls</option>
...
...
...