背景介绍
在物联网与智能应用的浪潮下,实时天气数据的获取与可视化成为许多项目的基础需求。开发一个天气工具不仅能帮我们快速了解天气,更能锻炼网络请求、数据解析、可视化的综合编程能力。本文将带你实现一个基于OpenWeatherMap API的天气工具,涵盖从API调用到图表展示的完整流程。
思路分析
要实现这个工具,需解决四个核心问题:
1. 网络请求:向OpenWeatherMap API发送HTTP请求,传递城市和API密钥。
2. 数据解析:从JSON响应中提取温度、湿度等关键数据。
3. 数据可视化:用Matplotlib绘制柱状图(对比温度/湿度/气压)和雷达图(多维度展示)。
4. 异常处理:捕获网络错误、API错误码(如城市不存在),提供友好提示。
代码实现
1. 依赖安装
首先安装所需Python库:
pip install requests matplotlib
2. 核心代码:天气工具链
(1)天气数据获取函数
import requests
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
def get_weather(city, api_key, country_code=''):
"""
调用OpenWeatherMap API获取天气数据
:param city: 城市名称(英文),如"Beijing"
:param api_key: OpenWeatherMap API密钥
:param country_code: 国家代码(如"CN"),可选
:return: 天气数据(JSON格式)或None(出错时)
"""
# 拼接城市+国家代码(如"Beijing,CN")
if country_code:
city = f"{city},{country_code}"
base_url = "http://api.openweathermap.org/data/2.5/weather"
params = {
'q': city,
'appid': api_key,
'units': 'metric' # 温度(°C)、风速(m/s)、气压(hPa)
}
try:
response = requests.get(base_url, params=params, timeout=10)
response.raise_for_status() # 触发HTTP错误(如404)
return response.json()
except requests.exceptions.ConnectionError:
print("❌ 错误:网络连接失败,请检查网络或API服务状态。")
return None
except requests.exceptions.Timeout:
print("⌛ 错误:请求超时,请稍后重试。")
return None
except requests.exceptions.HTTPError as e:
if response.status_code == 404:
print(f"🔍 错误:城市「{city}」未找到,请检查名称或国家代码(如Beijing,CN)。")
else:
print(f"API错误(状态码{response.status_code}):{e}")
return None
except Exception as e:
print(f"⚠️ 未知错误:{str(e)}")
return None
(2)天气数据可视化函数
def plot_weather(data):
"""
可视化天气数据:柱状图(温度/湿度/气压)+ 雷达图(多维度对比)
:param data: 解析后的天气数据(字典格式)
"""
# 提取核心数据
city = data['name']
temp = data['main']['temp'] # 温度(°C)
humidity = data['main']['humidity'] # 湿度(%)
pressure = data['main']['pressure'] # 气压(hPa)
wind_speed = data['wind']['speed'] # 风速(m/s)
weather_desc = data['weather'][0]['description'] # 天气描述
# -------------- 柱状图:温度、湿度、气压 --------------
bar_labels = ['温度 (°C)', '湿度 (%)', '气压 (hPa)']
bar_values = [temp, humidity, pressure]
# -------------- 雷达图:多维度归一化展示 --------------
radar_labels = ['温度', '湿度', '气压', '风速']
radar_values = [temp, humidity, pressure, wind_speed]
max_radar = max(radar_values) # 归一化基准
radar_values = [v / max_radar for v in radar_values] # 缩放到0-1
radar_values.append(radar_values[0]) # 闭合雷达图
radar_angles = np.linspace(0, 2*np.pi, len(radar_labels), endpoint=True).tolist()
radar_angles.append(radar_angles[0]) # 闭合角度
# -------------- 绘制图表(GridSpec布局) --------------
fig = plt.figure(figsize=(12, 6))
gs = GridSpec(1, 2, width_ratios=[1, 1]) # 左右子图宽度1:1
# 子图1:柱状图
ax_bar = fig.add_subplot(gs[0, 0])
x = np.arange(len(bar_labels))
bars = ax_bar.bar(x, bar_values, color=['#FF6B6B', '#4ECDC4', '#FFD166'])
ax_bar.set_xticks(x)
ax_bar.set_xticklabels(bar_labels, rotation=15, ha='right')
ax_bar.set_title(f'{city} - 天气数据对比(柱状图)', fontsize=12)
ax_bar.set_ylabel('数值')
# 柱子上标注数值
for bar, val in zip(bars, bar_values):
ax_bar.text(
bar.get_x() + bar.get_width()/2,
bar.get_height() + 0.5,
f'{val:.1f}',
ha='center',
va='bottom'
)
# 子图2:雷达图
ax_radar = fig.add_subplot(gs[0, 1], polar=True)
ax_radar.plot(radar_angles, radar_values, 'o-', linewidth=2, color='#2A9D8F')
ax_radar.fill(radar_angles, radar_values, alpha=0.2, color='#2A9D8F')
ax_radar.set_thetagrids(
np.degrees(radar_angles[:-1]), # 角度转度数(排除最后一个闭合点)
radar_labels
)
ax_radar.set_title(f'{city} - 天气数据对比(雷达图)', fontsize=12, pad=20)
ax_radar.set_ylim(0, 1) # 归一化后范围0-1
# 底部显示天气描述
plt.figtext(0.5, 0.01, f'天气状况:{weather_desc}', ha='center', fontsize=10)
plt.tight_layout()
plt.show()
(3)主程序:用户交互与流程控制
def main():
"""主程序:处理用户输入,调用工具链"""
print("🌤️ 天气数据获取与可视化工具")
print("-------------------------")
# 1. 获取API密钥(用户需提前注册OpenWeatherMap)
api_key = input("请输入你的OpenWeatherMap API密钥:").strip()
if not api_key:
print("错误:API密钥不能为空!")
return
# 2. 获取城市名称(格式:城市名,国家代码,如Beijing,CN)
city = input("请输入城市名称(英文,格式:城市名,国家代码,如Beijing,CN):").strip()
if not city:
print("错误:城市名称不能为空!")
return
# 3. 调用API获取天气数据
weather_data = get_weather(city, api_key)
if not weather_data:
print("程序终止:天气数据获取失败。")
return
# 4. 打印文本信息
print("\n📊 天气数据(文本版):")
print(f"城市:{weather_data['name']}")
print(f"温度:{weather_data['main']['temp']}°C")
print(f"湿度:{weather_data['main']['humidity']}%")
print(f"气压:{weather_data['main']['pressure']}hPa")
print(f"风速:{weather_data['wind']['speed']}m/s")
print(f"天气状况:{weather_data['weather'][0]['description']}")
# 5. 可视化数据
plot_weather(weather_data)
if __name__ == "__main__":
main()
代码解析与扩展
1. API调用的鲁棒性
get_weather通过try-except捕获了网络错误(连接失败、超时)、HTTP错误(如404城市不存在)和未知异常,确保程序在异常下仍能给出友好提示。
2. 数据可视化的巧思
- 柱状图:直观对比温度、湿度、气压的数值差异,柱子上标注具体数值。
- 雷达图:通过归一化将多维度数据(温度、湿度、气压、风速)映射到同一尺度,展示数据的相对关系。
- 布局优化:使用
GridSpec实现左右分栏,底部添加天气描述,提升可读性。
3. 扩展方向
- 中文城市支持:通过
pinyin库转换中文为拼音,或调用支持中文的API(如和风天气)。 - 历史趋势:扩展API为
onecall接口,获取多日预报,绘制折线图展示趋势。 - 图形界面:用
Tkinter/PyQt封装为桌面应用,提升交互体验。
运行测试
- 安装依赖:
pip install requests matplotlib。 - 注册OpenWeatherMap,获取API密钥。
- 运行程序:
python weather_tool.py,输入密钥和城市(如Beijing,CN)。 - 预期输出:
- 文本信息:清晰展示天气数据。
- 可视化图表:柱状图(温度、湿度、气压)和雷达图(多维度对比)。
总结
本项目通过网络请求+数据解析+可视化+异常处理的组合,实现了一个实用的天气工具。从中级以下开发者的角度,你将学到:
– 如何与RESTful API交互,处理复杂的响应结构。
– 如何用Matplotlib创建多子图、多类型的可视化图表。
– 如何通过分层的异常处理,让程序在各种错误场景下保持健壮性。
这个项目代码结构清晰、依赖简单,适合作为网络编程+数据可视化的入门实践。如果你想进一步挑战,可以尝试扩展功能(如多城市对比、历史天气),或封装为Web服务(如Flask应用)。
现在,你可以动手实践,体验从“API调用”到“数据可视化”的完整流程,感受代码转化为实用工具的乐趣!