Discord Bot开发:Pandas DataFrame读取谷歌表格数据遇KeyError报错及优化方案咨询
问题分析与解决方案
先帮你拆解下碰到的KeyError: 0问题,再给你几个更优的实现思路:
1. 核心错误原因
你代码里的问题主要有两个:
- 命令参数错误:Discord.py的命令函数第一个参数必须是
ctx(上下文对象),你写成了message,导致你把上下文对象当成用户ID去匹配表格数据,自然找不到对应行,最后取索引0就抛出了KeyError。 - 多余的
await关键字:Pandas的DataFrame操作是同步的,不需要加await,这属于误用异步语法。
2. 修复后的基础代码
先把核心问题修正,假设你表格里的Discord ID是字符串类型(如果是数字类型,把str(ctx.author.id)改成ctx.author.id即可):
import aiohttp import discord import asyncio from discord.ext import commands from oauth2client.service_account import ServiceAccountCredentials as SAC import gspread import pandas as pd bot = commands.Bot(command_prefix='!') # 补充Drive权限,避免部分访问受限问题 scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive'] Json = 'xxx.json' Connect = SAC.from_json_keyfile_name(Json, scope) GoogleSheets = gspread.authorize(Connect) sheet = GoogleSheets.open_by_key('your_spreadsheet_key') Sheets = sheet.sheet1 # 初始加载表格数据 df = pd.DataFrame(Sheets.get_all_records()) @bot.event async def on_ready(): await bot.wait_until_ready() bot.session = aiohttp.ClientSession() print('Logged in as') print(bot.user.name) print(bot.user.id) print('------------') @bot.command(name='LList') async def llist(ctx, a: str): # 获取当前调用命令的用户ID,转换为表格中存储的类型 user_id = str(ctx.author.id) # 筛选匹配的行 matching_data = df[df['Discord ID'] == user_id] # 处理用户不存在的情况 if matching_data.empty: await ctx.send("未找到你的票务信息!") return # 用iloc[0]替代直接[0],避免索引混乱问题 ticket_count = matching_data['ticket\n(total)'].iloc[0] await ctx.send(f"```\n{a}\n You have {ticket_count} ticket```") bot.run('your_bot_token')
3. 更优的实现方案
如果你的表格数据量大或需要实时更新,直接加载整个DataFrame到内存不是最优解,推荐以下几种思路:
方案一:按需查询,不加载全量数据
利用gspread的原生查询功能,直接在Google Sheets中定位目标行,减少本地数据处理:
@bot.command(name='LList') async def llist(ctx, a: str): user_id = str(ctx.author.id) try: # 假设Discord ID在表格第1列,查找匹配单元格 target_cell = Sheets.find(user_id, in_column=1) # 假设ticket列在第2列,获取对应值 ticket_value = Sheets.cell(target_cell.row, 2).value await ctx.send(f"```\n{a}\n You have {ticket_value} ticket```") except gspread.exceptions.CellNotFound: await ctx.send("未找到你的票务信息!")
方案二:定时刷新缓存的DataFrame
如果需要频繁查询,定时刷新DataFrame缓存,避免每次请求都调用Google Sheets API:
async def refresh_data_cache(): global df while True: # 每5分钟刷新一次数据 df = pd.DataFrame(Sheets.get_all_records()) await asyncio.sleep(300) @bot.event async def on_ready(): await bot.wait_until_ready() bot.session = aiohttp.ClientSession() # 启动定时刷新任务 bot.loop.create_task(refresh_data_cache()) print('Logged in as') print(bot.user.name) print(bot.user.id) print('------------')
方案三:使用Google Sheets API直接查询
如果熟悉Google Sheets API,可以用spreadsheets.values.get配合查询参数,只拉取匹配的行数据,进一步减少传输量。
额外注意事项
- 确保你的服务账号邮箱已被添加到目标表格的共享列表中,否则会出现权限错误。
- 表格列名要和代码中的完全一致,比如
ticket\n(total)带换行的列名,要保证DataFrame加载后列名匹配。 - 注意用户ID的数据类型一致性,表格中存数字就用数字匹配,存字符串就用字符串匹配。
内容的提问来源于stack exchange,提问作者Mo Carson
相关产品推荐
相关产品推荐

