Python Tkinter天气项目API故障求助:OpenWeatherMap调用报KeyError
Tkinter天气项目API问题解决指南
问题概述
用Tkinter开发Python GUI天气项目时,使用OpenWeatherMap API出现KeyError: 'weather'错误,无法正常获取天气数据。
错误信息
Traceback (most recent call last): File "C:\Users\Hania\Desktop\ppp\WeatherApp.py", line 53, in <module> condition = response['weather'][0]['main'] KeyError: 'weather'
核心问题分析
你的代码存在几个关键问题,并非单纯API不可用:
- API接口调用错误:OpenWeatherMap的
/data/3.0/onecall接口需要传入经纬度(lat、lon)参数,而非直接拼接城市名,这会导致请求返回错误响应,自然没有weather字段。 - 请求时机错误:API请求代码写在全局作用域,程序启动时就会执行,此时输入框
textfield为空,请求的城市参数无效。 - 未处理响应错误:没有判断API返回的状态,比如当请求失败时,响应里会包含
cod错误码,而非天气数据。 - API Key未正确替换:代码里的
"my api key"需要替换成你在OpenWeatherMap官网申请的有效密钥。
修复方案
1. 修正API调用逻辑
将API请求移到getWeather函数内,先通过geopy获取城市经纬度,再调用接口;或者改用更适合按城市名查询的/data/2.5/weather接口。以下是完整修复代码:
2. 修正后的完整代码
from tkinter import * import tkinter as tk from geopy.geocoders import Nominatim from tkinter import ttk, messagebox from timezonefinder import TimezoneFinder from datetime import datetime import requests import pytz import json root = Tk() root.title("Weather") root.geometry("900x500+300+200") root.config(bg = "lightgrey") root.resizable(True, False) # search box Search_image = PhotoImage(file = "Copy of search.png") myimage = Label(image = Search_image) myimage.place(x = 20, y = 20) def getWeather(): try: city = textfield.get().strip() if not city: messagebox.showwarning("提示", "请输入城市名称") return # 获取时区和当地时间 geolocator = Nominatim(user_agent = "geopiExercises") location = geolocator.geocode(city) if not location: messagebox.showerror("错误", "未找到该城市") return obj = TimezoneFinder() result = obj.timezone_at(lng = location.longitude, lat = location.latitude) home = pytz.timezone(result) local_time = datetime.now(home) current_time = local_time.strftime("%I:%M:%p") clock.config(text = current_time) name.config(text = "CURRENT TIME") # 获取天气数据 api_key = "你的有效API密钥" # 替换为自己的API Key # 使用按城市名查询的weather接口,units=metric直接返回摄氏度 api = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric" json_data = requests.get(api) response = json_data.json() # 检查响应是否成功 if response.get("cod") != 200: messagebox.showerror("错误", response.get("message", "获取天气数据失败")) return # 提取天气数据 condition = response['weather'][0]['main'] description = response['weather'][0]['description'] temp = int(response['main']['temp']) pressure = response['main']['pressure'] humidity = response['main']['humidity'] wind = response['wind']['speed'] # 更新UI显示 t.config(text = f"{temp}°") c.config(text = f"{condition} | FEELS LIKE {temp}°") w.config(text = f"{wind} m/s") w2.config(text = f"{humidity} %") w3.config(text = description.title()) w4.config(text = f"{pressure} hPa") except Exception as e: messagebox.showerror("错误", f"程序出错:{str(e)}") textfield = tk.Entry(root, justify = "center", width = 22,font = ("poppins",25,"bold" ), bg = "#404040", border = 0, fg = "white") textfield.place(x = 54, y = 40) textfield.focus() Search_icon = PhotoImage(file = "Copy of search_icon.png") myimage2 = Button(image = Search_icon,borderwidth = 0,cursor = "hand2", bg = "#404040", command = getWeather ) myimage2.place(x = 380, y = 33) #logo Logo_image = PhotoImage(file = "logo.png") myimage3 = Label(image = Logo_image) myimage3.place(x = 150, y = 100) # Bottom box Frame_image = PhotoImage(file = "Copy of box.png") myimage4 = Label(image = Frame_image) myimage4.pack(padx = 5, pady = 5, side = BOTTOM) # time name = Label(root, font = ("arial",15 , "bold")) name.place(x = 30, y = 100) clock = Label(root, font = ("Helvetica", 20,"bold")) clock.place(x = 30, y = 120) # Label label1 = Label(root, text = "WIND", font = ("Helvetica",15,"bold"), fg = "white", bg = "#1ab5ef") label1.place(x = 120, y = 400) label2 = Label(root, text = "HUMIDITY", font = ("Helvetica",15,"bold"), fg = "white", bg = "#1ab5ef") label2.place(x = 250, y = 400) label3 = Label(root, text = "CONDITION", font = ("Helvetica",15,"bold"), fg = "white", bg = "#1ab5ef") label3.place(x = 430, y = 400) label4 = Label(root, text = "PRESSURE", font = ("Helvetica",15,"bold"), fg = "white", bg = "#1ab5ef") label4.place(x = 650, y = 400) t = Label(font = ("arial", 70, "bold"), fg = "#ee666d") t.place(x = 400, y = 150 ) c = Label(font = ("arial", 15, "bold")) c.place(x = 400, y = 250 ) w = Label(text = "...", font = ("arial", 20, "bold"), bg = "#1ab5ef") w.place(x = 130 , y = 430) w2 = Label(text = "...", font = ("arial", 20, "bold"), bg = "#1ab5ef") w2.place(x = 280 , y = 430) w3 = Label(text = "...", font = ("arial", 20, "bold"), bg = "#1ab5ef") w3.place(x = 450 , y = 430) w4 = Label(text = "...", font = ("arial", 20, "bold"), bg = "#1ab5ef") w4.place(x = 670 , y = 430) root.mainloop()
3. 关键修改点
- 将天气请求逻辑移入
getWeather函数,点击搜索按钮时才执行 - 使用
/data/2.5/weather接口,支持直接按城市名查询,添加units=metric直接获取摄氏度 - 添加错误处理:判断城市输入、地理编码结果、API响应状态
- 更新UI元素的动态赋值,原代码中天气数据是全局初始化时赋值,现在改为在获取数据后更新
替代免费API推荐
如果OpenWeatherMap仍有问题,可以尝试以下免费选项:
- Open-Meteo:无需API密钥,支持经纬度查询天气数据,免费额度充足
- 和风天气:国内城市数据更准确,免费版提供基础天气查询功能
内容的提问来源于stack exchange,提问作者Hania Asim
相关产品推荐
相关产品推荐

