Python抓取Pro-Football-Reference防守冲球统计表格遇列数不匹配错误
解决Pro-Football-Reference防守冲球统计转CSV的列不匹配问题
问题描述
我尝试将Pro-Football-Reference网站的防守冲球统计表格转换为CSV文件,此前处理跑卫统计数据时程序可正常获取10列数据,但处理防守统计数据时,出现错误:Assertion Error: 4 columns passed, passed data had 3 columns。
我的代码如下:
headers = [th.getText() for th in soup.findAll('tr')[1].findAll('th')] headers = headers[1:] print(headers[:5])
rows = soup.findAll('tr', class_ = lambda table_rows: table_rows != "thead") player_stats = [[td.getText() for td in rows[i].findAll('td')] for i in range(len(rows))] player_stats = player_stats[2:]
stats = pd.DataFrame(player_stats, columns = headers) stats.head()
经排查,问题源于表格存在双层表头(多个“Rush”),且程序无法正确抓取球队名称。目标表格示例(省略其余30支球队):
Rush Rush Rush Tm Att Yds TD Arizona Cardinals 25.8 119.2 0.82 Seattle Seahawks 24.0 106.8 1.12
问题根源
- 表格是双层表头结构:第一行是重复的“Rush”分类,第二行是具体统计项(Att/Yds/TD),但球队名称列(Tm)属于独立表头列,原代码未处理这种合并表头的情况,导致列数统计错误。
- 筛选数据行的逻辑无效:
lambda table_rows: table_rows != "thead"无法准确排除表头行,导致抓取到无效数据行,进一步打乱列数匹配关系。
修正后的代码
1. 正确处理双层表头
合并两层表头,生成清晰的列名:
# 获取表头的两行数据 header_rows = soup.select('thead tr') upper_headers = [th.getText().strip() for th in header_rows[0].findAll('th')] lower_headers = [th.getText().strip() for th in header_rows[1].findAll('th')] # 合并表头:Tm单独保留,Rush类的列合并为Rush_Att、Rush_Yds、Rush_TD headers = [] for upper, lower in zip(upper_headers, lower_headers): if lower == 'Tm': headers.append(lower) else: headers.append(f"{upper}_{lower}" if upper else lower) # 剔除表格最左侧的空序号列表头 headers = headers[1:]
2. 准确筛选数据行
利用网站内置的行类名,直接抓取有效数据行:
# 只抓取完整/部分数据行,排除表头、汇总行 data_rows = soup.select('tr.full_table, tr.partial_table') player_stats = [] for row in data_rows: # 抓取该行所有单元格的文本,包含球队名称 row_data = [td.getText().strip() for td in row.findAll('td')] player_stats.append(row_data)
3. 生成匹配的DataFrame
此时列数与数据行数完全匹配,可正常生成表格:
import pandas as pd stats = pd.DataFrame(player_stats, columns=headers) stats.head()
验证效果
修正后,表头会变为:['Tm', 'Rush_Att', 'Rush_Yds', 'Rush_TD'],每一行数据都包含4个值,彻底解决列数不匹配的错误。
内容的提问来源于stack exchange,提问作者Chance Hainey
相关产品推荐
相关产品推荐

