如何抓取NBA网站中无ID且难以解析的表格数据?
NBA官网球员数据表格抓取问题
尝试抓取NBA官网的球员传统数据表格时,使用BeautifulSoup和pd.read_html()均无法正确解析提取目标表格。
所用代码
stars_url ="https://www.nba.com/stats/players/traditional?Season={}&dir=A&sort=NBA_FANTASY_PTS" season = '2010-11' url = stars_url.format(season) data = requests.get(url) soup = BeautifulSoup(data.text) stat = soup.select("table", class_ ="Crom_table__p1iZz") import pandas as pd from io import StringIO stat_tr = pd.read_html(StringIO(str(stat)), encoding='utf-8') stat_tr
错误结果
返回的是日历表格而非目标球员数据:
[ Sun Mon Tues Wed Thurs Fri Sat 0 31 1 2 3 4 5 6 1 7 8 9 10 11 12 13 2 14 15 16 17 18 19 20 3 21 22 23 24 25 26 27 4 28 29 30 31 1 2 3, Sun Mon Tues Wed Thurs Fri Sat 0 31 1 2 3 4 5 6 1 7 8 9 10 11 12 13 2 14 15 16 17 18 19 20 3 21 22 23 24 25 26 27 4 28 29 30 31 1 2 3]
已尝试的方法
- 使用
soup.find_all()查找目标表格 - 使用
pd.read_html(data.text, match="Crom_table__p1iZz")匹配表格类名
*注:目标表格(players_traditional_stats)没有id,增加了查找难度。
解决方法
1. 问题根源
NBA官网的球员数据表格是通过JavaScript动态加载的,直接用requests.get()获取的HTML里根本没有目标表格的内容,你拿到的日历表格是页面里其他静态渲染的元素。
2. 可行方案
方案一:模拟浏览器加载(用Selenium)
直接用Selenium模拟浏览器打开页面,等待JS加载完成后再提取表格:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import pandas as pd stars_url ="https://www.nba.com/stats/players/traditional?Season={}&dir=A&sort=NBA_FANTASY_PTS" season = '2010-11' url = stars_url.format(season) # 初始化浏览器(需提前下载对应版本的chromedriver) driver = webdriver.Chrome() driver.get(url) # 等待目标表格加载完成 wait = WebDriverWait(driver, 10) table = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "Crom_table__p1iZz"))) # 提取表格HTML并转成DataFrame html = table.get_attribute('outerHTML') df = pd.read_html(html)[0] # 关闭浏览器 driver.quit() print(df.head())
方案二:调用NBA官方API
NBA Stats页面的数据其实是通过API接口获取的,直接调用接口更高效,不需要解析HTML:
import requests import pandas as pd season = '2010-11' url = f"https://stats.nba.com/stats/leaguedashplayerstats?College=&Conference=&Country=&DateFrom=&DateTo=&Division=&DraftPick=&DraftYear=&GameScope=&GameSegment=&Height=&LastNGames=0&LeagueID=00&Location=&MeasureType=Base&Month=0&OpponentTeamID=0&Outcome=&PORound=0&PaceAdjust=N&PerMode=PerGame&Period=0&PlayerExperience=&PlayerPosition=&PlusMinus=N&Rank=N&Season={season}&SeasonSegment=&SeasonType=Regular+Season&ShotClockRange=&StarterBench=&TeamID=0&TwoWay=0&VsConference=&VsDivision=&Weight=" # 请求头要模拟浏览器,否则会被拦截 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36", "Referer": "https://www.nba.com/stats" } response = requests.get(url, headers=headers) data = response.json() # 提取数据和列名 columns = [col['name'] for col in data['resultSets'][0]['headers']] rows = data['resultSets'][0]['rowSet'] df = pd.DataFrame(rows, columns=columns) print(df[['PLAYER_NAME', 'PTS', 'REB', 'AST']].head())
3. 为什么之前的方法无效
- 直接
requests.get()只能拿到页面的静态HTML,动态加载的表格内容还没被渲染出来,所以你找不到目标表格,反而匹配到了页面里的日历表格。 pd.read_html()会解析HTML里所有的<table>标签,当目标表格不存在时,就会返回其他无关的表格。
内容的提问来源于stack exchange,提问作者KIZ-MAN
相关产品推荐
相关产品推荐

