You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas追加DataFrame引发for loop异常,寻求技术帮助

哈哈,这个问题我之前踩过一模一样的坑!核心原因是变量作用域的陷阱——你在apply_test函数里修改全局的game_df时,没有明确声明它是全局变量,导致Python在函数内部创建了一个同名的局部变量,这直接干扰了你的循环逻辑。

为什么会出现循环变量重复的怪异现象?

当你执行game_df = game_df.append(...)这行代码时,Python会默认把game_df当作函数内的局部变量处理。但你在赋值前先引用了game_df(也就是game_df.append这部分),这会触发隐式的变量绑定,而这种绑定会干扰函数内循环变量yr的解析逻辑——这就是为什么注释掉这行后,循环立刻恢复正常的原因:没有了局部变量的干扰,Python能正确解析循环的迭代值。

解决方案1:明确声明全局变量(快速修复)

在apply_test函数的开头加上global game_df,告诉Python你要操作的是外部的全局DataFrame,而不是创建局部变量:

def apply_test(row):
    global game_df  # 关键!必须声明这是全局变量
    url = row['url']
    for yr in range(int(row['startYear']),int(row['endYear'])+1):
        print(yr)
        content = requests.get(url.split(".htm")[0]+"/gamelog/"+str(yr)).content
        soup = bs(content,'html.parser').find("div",{"id":"all_stats"})
        # ... 后面的表头处理代码不变 ...
        for row in soup.find("tbody").find_all("tr"):
            temp_row = {}
            for i,col in enumerate(row.find_all("td")):
                temp_row[all_headers[i]] = col.text
            game_df = game_df.append(temp_row,ignore_index=True)

修改后,循环的yr就会正常输出2013和2014,game_df也能正确追加数据。

解决方案2:无全局变量的优雅写法(推荐)

使用全局变量很容易引发各种作用域问题,更符合Pandas最佳实践的方式是让apply_test返回每个玩家的比赛数据,最后用pd.concat批量合并所有结果:

import pandas as pd
from bs4 import BeautifulSoup as bs
import requests
import string
import numpy as np

# 获取球员列表的代码保持不变
players = pd.DataFrame({"name":[],"url":[],"positions":[],"startYear":[],"endYear":[]})
letters = list(string.ascii_uppercase)
for letter in letters:
    print(letter)
    players_html = requests.get("https://www.pro-football-reference.com/players/"+letter+"/")
    soup = bs(players_html.content,"html.parser")
    for player in soup.find("div",{"id":"div_players"}).find_all("p"):
        temp_row = {}
        temp_row["url"] = "https://www.pro-football-reference.com"+player.find("a")["href"]
        temp_row["name"] = player.text.split("(")[0].strip()
        years = player.text.split(")")[1].strip()
        temp_row["startYear"] = int(years.split("-")[0])
        temp_row["endYear"] = int(years.split("-")[1])
        temp_row["positions"] = player.text.split("(")[1].split(")")[0]
        players = players.append(temp_row,ignore_index=True)

players = players[players.endYear > 2000]
players.reset_index(inplace=True,drop=True)

def apply_test(row):
    url = row['url']
    player_game_data = []  # 用列表收集当前玩家的所有比赛数据
    for yr in range(int(row['startYear']),int(row['endYear'])+1):
        print(yr)
        content = requests.get(url.split(".htm")[0]+"/gamelog/"+str(yr)).content
        soup = bs(content,'html.parser').find("div",{"id":"all_stats"})
        # 处理表头逻辑不变
        over_headers = []
        for over in soup.find("thead").find("tr").find_all("th"):
            if("colspan" in over.attrs.keys()):
                over_headers.extend([over.text]*int(over['colspan']))
            else:
                over_headers.append(over.text)
        headers = [header.text for header in soup.find("thead").find_all("tr")[1].find_all("th")]
        all_headers = [f"{a}___"+b for a,b in zip(over_headers,headers)][1:]  # 移除无意义的第一列
        
        # 收集单场比赛数据
        for game_row in soup.find("tbody").find_all("tr"):
            temp_row = {}
            for i,col in enumerate(game_row.find_all("td")):
                temp_row[all_headers[i]] = col.text
            # 额外添加球员名和年份,方便后续分析
            temp_row['player_name'] = row['name']
            temp_row['year'] = yr
            player_game_data.append(temp_row)
    
    # 返回当前玩家的比赛数据DataFrame
    return pd.DataFrame(player_game_data)

# 合并所有玩家的比赛数据
all_game_data = pd.concat(players.apply(apply_test, axis=1).tolist(), ignore_index=True)

这个方案的优势很明显:

  • 彻底避免了全局变量的作用域问题
  • 代码逻辑更清晰,每个函数只负责单一职责
  • 效率更高(append是逐行添加,而列表收集+concat是批量合并,速度提升明显)
  • 额外添加的player_name和year字段,能让后续的统计分析更方便

额外提醒:Pandas的append已弃用

从Pandas 2.0版本开始,DataFrame.append()已经被正式弃用,官方推荐用pd.concat()替代。所以不管你选择哪种方案,都尽量避免使用append,改用列表收集数据后再合并,既符合官方规范,也能避免潜在的bug。

内容的提问来源于stack exchange,提问作者zachvac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:50:10