Python+OpenWeatherMap+Flet天气应用遇'daily' KeyError及日期获取问题
问题解决:OpenWeatherMap 无
daily字段报错+日期重复问题 核心问题拆解
- KeyError: 'daily':你当前调用的是
/weather接口(仅返回当前天气),这个接口的响应里根本没有daily字段。要获取多日预报数据,必须用专门的预报接口。 - 日期重复:之前用
response['dt']重复是因为误拿了当前天气的单一时间戳,或者用了每3小时更新的预报数据但没做去重。 - API无直接日期名称:OpenWeatherMap所有接口都不会返回“周六/周日”这类星期名称,只能通过返回的时间戳
dt转换,但我们可以通过正确接口+数据处理避免重复。
解决方案
步骤1:更换为正确的预报API端点
免费版有两种可选接口,按需选择:
方案A:用/forecast接口(无需经纬度,直接用城市名)
这个接口返回5天内每3小时一条的预报数据,适合快速适配城市名查询:
API_KEY = "你的API密钥" # 替换为预报接口地址 BASE_URL = "http://api.openweathermap.org/data/2.5/forecast?" CITY = "Minsk" COUNTRY = "BY" days_en = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] days = days_en # 拼接URL,cnt=40对应5天全量数据,units=metric可选(返回摄氏度) url = f"{BASE_URL}appid={API_KEY}&q={CITY},{COUNTRY}&cnt=40&units=metric" response = requests.get(url).json()
方案B:用/onecall接口(7天完整预报,需先获取经纬度)
如果需要7天完整数据,先调用/weather获取城市经纬度,再调用onecall接口:
API_KEY = "你的API密钥" CITY = "Minsk" COUNTRY = "BY" days_en = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] days = days_en # 第一步:获取城市经纬度 geo_url = f"http://api.openweathermap.org/data/2.5/weather?q={CITY},{COUNTRY}&appid={API_KEY}" geo_response = requests.get(geo_url).json() lat = geo_response['coord']['lat'] lon = geo_response['coord']['lon'] # 第二步:调用onecall获取7天预报,排除不需要的实时/分钟级数据 onecall_url = f"https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude=current,minutely,hourly&appid={API_KEY}" response = requests.get(onecall_url).json()
这个方案的响应里会直接出现daily字段,完美匹配你原来的循环逻辑。
步骤2:处理日期避免重复+转换为星期名称
如果用方案A(/forecast接口)
因为接口每3小时返回一条数据,需要过滤重复日期,只保留每天的第一条数据:
def _bot_data(): _bot_data = [] processed_dates = set() # 存已处理的日期,避免重复 for item in response['list']: dt_timestamp = item['dt'] date_obj = datetime.datetime.fromtimestamp(dt_timestamp) date_str = date_obj.strftime('%Y-%m-%d') # 用日期字符串判断是否重复 if date_str not in processed_dates: processed_dates.add(date_str) # 转换为星期名称(weekday()返回0=周一,对应days_en[0]) weekday_name = days_en[date_obj.weekday()] _bot_data.append( Row( spacing=5, alignment="spaceBetween", controls=[ Row( expand=1, alignment="start", controls=[ Container( alignment=alignment.center, content=Text(weekday_name) ) ] ) ] ) ) # 最多取7天,提前终止循环 if len(_bot_data) >=7: break return _bot_data
如果用方案B(/onecall接口)
接口返回的daily列表直接包含7天数据,直接修改原代码即可:
def _bot_data(): _bot_data = [] # range(0,7)包含当天到未来6天,要未来7天就用range(1,8) for index in range(0, 7): dt_timestamp = response['daily'][index]['dt'] date_obj = datetime.datetime.fromtimestamp(dt_timestamp) weekday_name = days_en[date_obj.weekday()] _bot_data.append( Row( spacing=5, alignment="spaceBetween", controls=[ Row( expand=1, alignment="start", controls=[ Container( alignment=alignment.center, content=Text(weekday_name) ) ] ) ] ) ) return _bot_data
步骤3:清理冗余依赖
你重复导入了datetime,删掉其中一个即可:
# 保留一个导入,比如 import datetime # 删掉 import datetime as dt
内容的提问来源于stack exchange,提问作者Vlach
相关产品推荐
相关产品推荐

