
本教程演示如何使用python高效爬取nba选秀体测数据。针对网页前端动态渲染的挑战,我们摒弃传统的beautifulsoup解析html方法,转而通过分析网络请求,直接调用nba官方api接口,以json格式获取结构化数据,并利用pandas进行数据处理,从而实现稳定可靠的数据抓取。
传统HTML解析的局限性
在进行网页数据抓取时,许多开发者习惯使用requests库获取网页内容,再结合BeautifulSoup库解析HTML结构来提取所需数据。然而,对于现代的动态网页,尤其是那些数据通过JavaScript在客户端渲染的页面,这种方法往往会遇到瓶颈。
例如,当我们尝试从NBA官网的选秀体测数据页面(如https://www.nba.com/stats/draft/combine-anthro?SeasonYear=2022-23)抓取表格数据时,如果仅使用requests.get()获取HTML内容,然后用BeautifulSoup.find_all("table")来查找表格,很可能会发现返回的表格列表是空的。这是因为页面上的数据表格并非直接嵌入在初始HTML文档中,而是通过JavaScript异步请求(AJAX)从后端API获取数据后,再由前端动态生成。
识别API数据源
解决动态网页数据抓取的关键在于绕过前端渲染,直接找到数据背后的API接口。这通常可以通过浏览器的开发者工具(通常按F12打开)来完成:
- 打开目标网页: 访问https://www.nba.com/stats/draft/combine-anthro?SeasonYear=2022-23。
- 打开开发者工具: 切换到“Network”(网络)选项卡。
- 刷新页面: 重新加载页面,观察网络请求。
- 筛选XHR/Fetch请求: 在“Network”选项卡中,通常会有“XHR”或“Fetch/XHR”过滤器,点击它以只显示异步数据请求。
- 查找数据请求: 仔细查看这些请求的URL和响应内容。我们会发现一个指向https://stats.nba.com/stats/draftcombineplayeranthro的请求,其响应类型为JSON,且包含了页面上显示的所有体测数据。
通过分析这个API请求,我们可以确定以下关键信息:
立即学习“Python免费学习笔记(深入)”;
- API URL: https://stats.nba.com/stats/draftcombineplayeranthro
- 请求方法: GET
- 查询参数(Payload): LeagueID (通常为"00"表示NBA), SeasonYear (例如"2022-23")。
- 请求头(Headers): 为了模拟浏览器行为,通常需要包含Referer(引用页)和User-Agent(用户代理)等。
Python实现数据抓取
一旦确定了API接口及其调用方式,就可以使用Python的requests库来直接获取JSON数据,并利用pandas库将其转换为结构化的DataFrame。
import requests
import pandas as pd
# 1. 定义API接口URL
api_url = "https://stats.nba.com/stats/draftcombineplayeranthro"
# 2. 定义请求参数 (Payload)
# 这些参数对应了API请求中的查询字符串,用于指定获取哪个赛季的数据等
payload = {
"LeagueID": "00", # 00通常代表NBA联盟
"SeasonYear": "2022-23" # 指定要查询的赛季年份
}
# 3. 定义请求头 (Headers)
# 模拟浏览器行为,避免被服务器识别为爬虫或拒绝访问
headers = {
"Referer": "https://www.nba.com/", # 引用页,模拟从NBA官网跳转
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" # 用户代理,模拟主流浏览器
}
# 4. 发送GET请求并获取JSON响应
# 使用params参数传递payload,headers参数传递请求头
try:
response = requests.get(api_url, params=payload, headers=headers)
response.raise_for_status() # 检查HTTP请求是否成功 (状态码200)
data = response.json() # 将响应内容解析为JSON格式
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
exit()
# 5. 解析JSON数据并创建Pandas DataFrame
# NBA API的JSON结构通常包含'resultSets',其中每个元素包含'headers'和'rowSet'
if data and "resultSets" in data and len(data["resultSets"]) > 0:
# 提取列名
columns = data["resultSets"][0]["headers"]
# 提取数据行
rows = data["resultSets"][0]["rowSet"]
# 使用Pandas创建DataFrame
df = pd.DataFrame(rows, columns=columns)
print("成功获取并处理数据:")
print(df.head()) # 打印DataFrame的前5行
print(f"\nDataFrame形状: {df.shape}")
else:
print("JSON数据结构不符合预期或无数据。")
代码解释:
- api_url: NBA选秀体测数据的API端点。
- payload: 一个字典,包含了发送给API的查询参数。LeagueID用于指定联赛,SeasonYear用于指定需要查询的赛季。
- headers: 一个字典,包含了HTTP请求头信息。Referer告知服务器请求来源于哪个页面,User-Agent则模拟了浏览器的身份。这些头信息对于成功访问某些API至关重要。
- requests.get(): 发送GET请求到API URL,并传入params和headers。
- response.raise_for_status(): 这是一个良好的实践,它会在HTTP请求返回错误状态码(如4xx或5xx)时抛出异常,便于错误处理。
- response.json(): 将API返回的JSON格式响应体解析成Python字典或列表。
- 数据解析: NBA API的数据通常封装在resultSets列表中,每个resultSets元素包含headers(列名)和rowSet(数据行)。通过索引[0]可以访问第一个结果集。
- pd.DataFrame(): 使用解析出的列名和数据行创建pandas.DataFrame,方便后续的数据分析和处理。
输出示例
运行上述代码,你将得到一个包含NBA选秀体测数据的Pandas DataFrame,其部分输出可能如下:
TEMP_PLAYER_ID PLAYER_ID FIRST_NAME ... BODY_FAT_PCT HAND_LENGTH HAND_WIDTH 0 1630534 1630534 Ochai ... 5.40 8.75 9.50 1 1631116 1631116 Patrick ... 8.90 8.75 9.50 2 1631094 1631094 Paolo ... NaN NaN NaN 3 1630599 1630599 Jaden ... 4.80 9.00 9.75 4 1631100 1631100 Dyson ... 4.90 9.50 10.00 [5 rows x 18 columns] DataFrame形状: (83, 18)
注意事项与最佳实践
- API稳定性: 官方API接口可能会发生变化,导致上述代码失效。在实际应用中,应定期检查API的可用性和响应结构。
- 请求频率: 避免在短时间内发送大量请求,以免被服务器封禁IP。可以引入time.sleep()进行延迟,或遵循API提供方的速率限制策略。
- 错误处理: 使用try-except块捕获网络请求和JSON解析可能出现的错误,增强代码的健壮性。
- 动态参数: 如果需要抓取不同赛季的数据,可以通过循环或修改payload字典中的SeasonYear参数来实现。
- 数据清洗: 抓取到的数据可能包含NaN值或需要进一步格式化。pandas提供了丰富的数据清洗和转换功能。
总结
对于前端动态渲染的网页,直接解析HTML往往效率低下或根本无法获取数据。通过利用浏览器的开发者工具分析网络请求,我们可以发现隐藏在背后的API接口。这种直接调用API的方式,不仅能够稳定、高效地获取结构化数据,而且避免了处理复杂的HTML解析逻辑,是现代网页数据抓取的一种主流且推荐的方法。掌握这种技巧,将大大提升数据获取的成功率和效率。










