本文深入探讨了在Python中从嵌套API响应中提取数据时,因循环缩进不当导致字典追加不完整的问题。通过一个具体的PGA高尔夫赛事数据抓取案例,详细分析了错误原因,并提供了正确的代码结构和解决方案。教程强调了Python中缩进的重要性,指导读者如何确保在多层嵌套循环中正确构建并累加所有目标数据记录,最终生成完整的DataFrame。
在处理来自外部API的复杂JSON数据时,开发者经常需要遍历多层嵌套结构来提取所需信息。然而,不正确的代码缩进是导致数据丢失或不完整的一个常见陷阱。本教程将通过一个实际案例,详细剖析这一问题及其解决方案。
假设我们需要从ESPN的PGA高尔夫赛事API中获取所有参赛选手的信息。API返回的JSON数据结构通常是多层嵌套的,例如:events -> competitions -> competitors -> athlete。我们的目标是从中提取每个赛事(event)的ID、日期、名称以及所有参赛选手(playerName)。
初次尝试的代码可能如下所示,旨在遍历赛事和选手并收集数据:
import requests import pandas aspd # 定义API端点 api = 'https://site.web.api.espn.com/apis/site/v2/sports/golf/leaderboard?league=pga' # 发送GET请求并获取JSON响应 response = requests.get(api) data = response.json() # 提取事件数据 events = data['events'] # 创建一个空列表来存储游戏数据 games = [] # 遍历每个事件并获取相关信息 for event in events: game_id = event['id'] date = event['date'] name = event['name'] for competition in event['competitions']: for competitor in competition['competitors']: athlete = competitor['athlete'] playerName = athlete['displayName'] # 每次循环都会更新playerName变量 # 在循环外部创建字典并追加,此时playerName已是最后一个competitor的值 game = { 'game_id': game_id, 'date': date, 'name': name, 'playerName': playerName } games.append(game) # 转换为pandas DataFrame df = pd.DataFrame(games) print(df)
运行上述代码,我们可能会得到类似以下的结果:
game_id date name playerName 0 401580329 2025-01-04T05:00Z The Sentry Sam Burns
从结果中可以看出,尽管API响应中包含多名选手,但最终的DataFrame中却只显示了最后一位选手的记录(例如 "Sam Burns"),并且每场赛事只有一条记录,这显然不是我们期望的。
出现上述问题的原因在于Python的缩进规则和变量作用域。在原始代码中:
因此,对于每个赛事,无论有多少参赛者,我们最终只会得到一条记录,其中包含该赛事下最后一个参赛者的信息。
要解决这个问题,我们需要确保在每次遍历到新的 competitor 时,都创建一个新的 game 字典并将其追加到 games 列表中。这需要将 game 字典的创建和 games.append(game) 操作移动到 for competitor 循环的 内部,使其成为该循环的一部分。
以下是修正后的代码:
import requests
import pandas as pd
# 定义API端点
api = 'https://site.web.api.espn.com/apis/site/v2/sports/golf/leaderboard?league=pga'
# 发送GET请求并获取JSON响应
response = requests.get(api)
data = response.json()
# 提取事件数据
events = data['events']
# 创建一个空列表来存储游戏数据
games = []
# 遍历每个事件并获取相关信息
for event in events:
game_id = event['id']
date = event['date']
name = event['name']
for competition in event['competitions']:
for competitor in competition['competitors']:
athlete = competitor['athlete']
playerName = athlete['displayName']
# 关键修改:将字典创建和追加操作移到最内层循环内部
game = {
'game_id': game_id,
'date': date,
'name': name,
'playerName': playerName
}
games.append(game) # 每次循环一个competitor,就创建一个字典并追加
# 转换为pandas DataFrame
df = pd.DataFrame(games)
print(df)通过这次修改,每当代码遍历到一个新的 competitor 时,它都会立即创建一个包含当前 game_id、date、name 和 playerName 的字典,并将其添加到 games 列表中。这样,所有参赛者的信息都将被正确捕获和存储。
在Python中处理API响应并提取嵌套数据时,正确理解和运用缩进是避免数据丢失的关键。本教程通过一个具体的案例,强调了将字典创建和列表追加操作放置在正确循环层级的重要性。掌握这些基本原则,将有助于你更高效、准确地处理复杂数据,构建健壮的数据处理流程。