import requests
import matplotlib.pyplot as plt
def plot_weather():
# 中国气象局官方公开API,返回标准JSON数据,极其稳定
# 101190101 是南京的城市代码,可替换为其他城市
url = "http://t.weather.sojson.com/api/weather/city/101190101"
headers = {"User-Agent": "Mozilla/5.0"}
print("正在从气象局API获取天气数据...")
try:
res = requests.get(url, headers=headers, timeout=10)
res.raise_for_status()
data = res.json() # 直接解析JSON数据
except Exception as e:
print(f"数据获取失败: {e}")
return
# 校验API返回的状态
if data.get("status") != 200:
print(f"API返回错误: {data.get('message')}")
return
# 提取未来7天的预报数据
forecast = data.get("data", {}).get("forecast", [])[:7]
if not forecast:
print("未获取到预报数据!")
return
days = []
high_temps = []
low_temps = []
# 解析每天的数据
for day in forecast:
# 日期,格式如 "28日星期六",我们只取前面的日期部分
date_str = day.get("ymd", "")
if date_str:
# 截取 MM-DD 格式,更美观
days.append(date_str[5:])
else:
days.append(day.get("date", ""))
# 最高温和最低温
high = day.get("high")
low = day.get("low")
# API返回的温度格式可能是 "高温 31℃" 或纯数字,做兼容处理
if isinstance(high, str):
high = high.replace("高温 ", "").replace("℃", "")
if isinstance(low, str):
low = low.replace("低温 ", "").replace("℃", "")
try:
high_temps.append(int(high))
low_temps.append(int(low))
except ValueError:
print(f"温度数据格式异常: {day}")
return
# --- 绘制图表 ---
plt.figure(figsize=(10, 5))
# 绘制最高温折线
plt.plot(days, high_temps, marker="o", color="red", label="最高温", linewidth=2)
# 绘制最低温折线
plt.plot(days, low_temps, marker="o", color="blue", label="最低温", linewidth=2)
# 在数据点上标注具体温度数值
for i, temp in enumerate(high_temps):
plt.text(i, temp + 0.8, f"{temp}°", ha='center', va='bottom', color='red', fontweight='bold')
for i, temp in enumerate(low_temps):
plt.text(i, temp - 0.8, f"{temp}°", ha='center', va='top', color='blue', fontweight='bold')
plt.title("南京近7日温度变化趋势", fontsize=16, pad=15)
plt.xlabel("日期", fontsize=12)
plt.ylabel("温度 (℃)", fontsize=12)
plt.legend(loc='lower right') # 显示图例
plt.grid(True, linestyle='--', alpha=0.5) # 添加虚线网格
plt.tight_layout() # 自动调整布局防遮挡
plt.show()
if __name__ == "__main__":
plot_weather()
这段Python代码实现了一个**天气数据获取与可视化**的小工具。它通过调用第三方天气API获取南京未来7天的天气预报数据,并使matplotlib库将最高温和最低温绘制成直观的折线图。
下面我将从实现原理、用途和注意事项三个方面为您详细解释:
### 一、 实现原理
代码的执行流程可以分为三个核心步骤:**数据获取**、**数据清洗与解析**、**数据可视化**。
1. 数据获取(网络请求)
- 使用 requests.get() 向指定的URL发送HTTP GET请求。
- 添加 headers 模拟浏览器访问,防止被部分具有反爬机制的服务器拒绝。
- 设置 timeout=10 防止网络不畅时程序无限期挂起。
- 使用 res.raise_for_status() 检查HTTP状态码,若请求失败(如404、500)则主动抛出异常。
- 使用 res.json() 将返回的JSON格式字符串直接解析为Python字典。
2. 数据清洗与解析(提取有效信息)
- 校验API返回的JSON字典中 status 是否为200,确认接口调用成功。
- 通过字典的 get() 方法逐层安全获取数据data -> forecast,并通过切片 [:7] 只取未来7天的数据。
- 日期处理:优先提取 ymd 字段(如"2023-10-28"),并通过切片 [5:] 截取月日部分(如"10-28")使图表横轴更简洁。
- 温度处理:由于API返回的温度字段可能是字符串(如 "高温 31℃"),代码使用了 replace() 方法剔除中文字符和符号,并通过 int() 转换为整型try...except ValueError 捕获了可能的转换异常,增强了代码的鲁棒性。
3. 数据可视化(绘制图表)
- 使用 plt.plot() 绘制最高温(红色)和最低温(蓝色)两条折线,设置 marker="o" 显示数据点。
- 使用 plt.text() 在每个数据点的上方(最高温)和下方(最低温)标注具体的温度数值,通过 ha 和 va 参数控制文字对齐方式,避免遮挡折线。
- 添加标题、坐标轴标签、图例和虚线网格,最后调用 plt.tight_layout() 自动调整子图参数,使之填充整个图像区域,防止标签被截断。
---
### 二、 用途
1. 日常天气直观查看:相比于阅读纯文本的天气预报,折线图能更直观地展现温度的起伏趋势,方便出行穿搭和行程规划。
2. 数据可视化学习案例:这是一个非常经典的“数据采集+数据清洗+数据可视化”的端到端示例,适合Python初学者学习如何将杂乱的数据转化为美观的图表。
3. 模板代码:只需修改URL中的城市代码(如将 101190101 改为 101010100 即为北京),就可以快速复用为其他城市的天气可视化工具。
---
### 三、 注意事项
1. API的稳定性与合法性:
- 代码注释中写的是“中国气象局官方公开API”,但实际上 t.weather.sojson.com 是一个**第三方聚合API**,并非官方直连接口。第三方接口存在限流、停服或数据格式突然变更的风险。
- 此类接口通常仅供个人学习测试使用,请勿用于商业项目或高频爬取,以免IP被封禁。
2. 中文字体显示问题(常见Bug):
- 代码中使用了中文标题和标签(如 "南京近7日温度变化趋势")。在部分操作系统(如Windows的默认matplotlib配置)下,由于缺少中文字体支持,图表上的中文会显示为**方块或乱码**。
- 解决方法:在绘图前添加以下代码强制指定中文字体:
```python
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
```
3. 数据格式的强依赖性:
- 代码的数据清洗逻辑(如 replace("高温 ", ""))是针对当前API的返回格式定制的。如果API提供方修改了返回格式(例如不再返回 "高温 " 而是直接返回数字),代码中的 replace 虽然不会报错,但如果格式变化过大,可能导致 int() 转换失败。因此try...except 的异常捕获在这里显得尤为重要。
4. 网络环境依赖:
- 该脚本必须在内网连通的环境下运行。如果在无网络或受限的网络环境中requests 会抛出 ConnectionError 等异常,代码中顶层的 try...except Exception as e 已经做了兜底处理,会友好地提示“数据获取失败”。
好的!我为你替换了城市代码,并修改了图表标题。
在这个API中:
- 苏州的城市代码是 101190401
- 台北(台湾代表城市)的城市代码是 101340101
import requests
import matplotlib.pyplot as plt
def plot_weather_comparison():
# 解决 matplotlib 中文显示成方块的问题
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows用黑体
# plt.rcParams['font.sans-serif'] = ['PingFang HK'] # Mac用户请使用这一行
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
# 城市配置
cities = {
"苏州": "101190401",
"台北": "101340101"
}
headers = {"User-Agent": "Mozilla/5.0"}
# 存储两个城市的数据
all_data = {
"苏州": {"days": [], "high": [], "low": []},
"台北": {"days": [], "high": [], "low": []}
}
# 1. 依次获取两个城市的数据
for city_name, city_code in cities.items():
url = f"http://t.weather.sojson.com/api/weather/city/{city_code}"
print(f"正在获取 {city_name} 天气数据...")
try:
res = requests.get(url, headers=headers, timeout=10)
res.raise_for_status()
data = res.json()
except Exception as e:
print(f"{city_name} 数据获取失败: {e}")
return
if data.get("status") != 200:
print(f"{city_name} API返回错误: {data.get('message')}")
return
forecast = data.get("data", {}).get("forecast", [])[:7]
if not forecast:
print(f"{city_name} 未获取到预报数据!")
return
# 解析数据
for day in forecast:
# 日期处理(两个城市的日期应该是一致的,覆盖写入即可)
date_str = day.get("ymd", "")
if date_str:
all_data[city_name]["days"].append(date_str[5:])
else:
all_data[city_name]["days"].append(day.get("date", ""))
# 温度处理
high = day.get("high")
low = day.get("low")
if isinstance(high, str):
high = high.replace("高温 ", "").replace("℃", "")
if isinstance(low, str):
low = low.replace("低温 ", "").replace("℃", "")
try:
all_data[city_name]["high"].append(int(high))
all_data[city_name]["low"].append(int(low))
except ValueError:
print(f"{city_name} 温度数据格式异常: {day}")
return
# 取任一城市的日期作为X轴(两个城市日期相同)
days = all_data["苏州"]["days"]
# 2. 绘制对比图表
plt.figure(figsize=(12, 6))
# 苏州:实线
plt.plot(days, all_data["苏州"]["high"], marker="o", color="red", linestyle="-", label="苏州最高温", linewidth=2)
plt.plot(days, all_data["苏州"]["low"], marker="o", color="blue", linestyle="-", label="苏州最低温", linewidth=2)
# 台北:虚线
plt.plot(days, all_data["台北"]["high"], marker="^", color="darkorange", linestyle="--", label="台北最高温", linewidth=2)
plt.plot(days, all_data["台北"]["low"], marker="^", color="teal", linestyle="--", label="台北最低温", linewidth=2)
# 在数据点上标注具体温度数值
for i in range(len(days)):
# 苏州标注
plt.text(i, all_data["苏州"]["high"][i] + 0.8, f'{all_data["苏州"]["high"][i]}°', ha='center', va='bottom', color='red', fontsize=9)
plt.text(i, all_data["苏州"]["low"][i] - 0.8, f'{all_data["苏州"]["low"][i]}°', ha='center', va='top', color='blue', fontsize=9)
# 台北标注 (稍微左右偏移一点防止重叠)
plt.text(i+0.15, all_data["台北"]["high"][i] + 0.8, f'{all_data["台北"]["high"][i]}°', ha='center', va='bottom', color='darkorange', fontsize=9)
plt.text(i+0.15, all_data["台北"]["low"][i] - 0.8, f'{all_data["台北"]["low"][i]}°', ha='center', va='top', color='teal', fontsize=9)
# 美化图表
plt.title("苏州 vs 台北 近7日温度变化对比", fontsize=18, pad=15, fontweight='bold')
plt.xlabel("日期", fontsize=12)
plt.ylabel("温度 (℃)", fontsize=12)
plt.legend(loc='best', fontsize=10) # 自动选择最佳位置显示图例
plt.grid(True, linestyle='--', alpha=0.5) # 添加虚线网格
plt.tight_layout() # 自动调整布局防遮挡
plt.show()
if __name__ == "__main__":
plot_weather_comparison()

之前使用的 t.weather.sojson.com 接口是基于中国气象局数据的,**只支持国内城市,没有国外城市的代码**。
为了实现苏州与纽约的跨国对比,我们需要更换一个支持全球城市的天气API。这里我为你挑选了 Open-Meteo,这是一个完全免费、无需注册API Key、支持全球城市且极其稳定的开源气象API。
因为API变了,数据获取和解析的逻辑需要完全重写。同时,由于纽约和苏州有**12个小时的时差**,为了公平对比同一天的天气,我在代码中加入了时区处理逻辑。
以下是全新的全球城市对比代码:
import requests
import matplotlib.pyplot as plt
from datetime import datetime
from dateutil import parser
def plot_global_weather_comparison():
# 解决 matplotlib 中文显示成方块的问题
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows用黑体
# plt.rcParams['font.sans-serif'] = ['PingFang HK'] # Mac用户请使用这一行
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
# 城市配置:使用经纬度定位全球任何地方
cities = {
"苏州": {"latitude": 31.30, "longitude": 120.62, "timezone": "Asia/Shanghai"},
"纽约": {"latitude": 40.71, "longitude": -74.01, "timezone": "America/New_York"}
}
headers = {"User-Agent": "Mozilla/5.0"}
all_data = {}
# 1. 依次获取两个城市的数据
for city_name, config in cities.items():
# Open-Meteo API 地址 (daily=temperature_2m_max,temperature_2m_min 获取最高最低温)
url = (
f"https://api.open-meteo.com/v1/forecast?"
f"latitude={config['latitude']}&longitude={config['longitude']}"
f"&daily=temperature_2m_max,temperature_2m_min"
f"&timezone={config['timezone']}"
)
print(f"正在获取 {city_name} 天气数据...")
try:
res = requests.get(url, headers=headers, timeout=10)
res.raise_for_status()
data = res.json()
except Exception as e:
print(f"{city_name} 数据获取失败: {e}")
return
# 解析数据
daily = data.get("daily", {})
dates = daily.get("time", [])
high_temps = daily.get("temperature_2m_max", [])
low_temps = daily.get("temperature_2m_min", [])
if not dates or len(dates) < 7:
print(f"{city_name} 获取到的数据不足7天!")
return
# 只取前7天数据,并格式化日期为 MM-DD
formatted_days = []
for date_str in dates[:7]:
# 将 "2023-10-28" 转换为 "10-28"
dt = parser.parse(date_str)
formatted_days.append(dt.strftime("%m-%d"))
all_data[city_name] = {
"days": formatted_days,
"high": high_temps[:7],
"low": low_temps[:7]
}
# 取苏州的日期作为X轴
days = all_data["苏州"]["days"]
# 2. 绘制对比图表
plt.figure(figsize=(12, 6))
# 苏州:实线
plt.plot(days, all_data["苏州"]["high"], marker="o", color="red", linestyle="-", label="苏州最高温", linewidth=2)
plt.plot(days, all_data["苏州"]["low"], marker="o", color="blue", linestyle="-", label="苏州最低温", linewidth=2)
# 纽约:虚线
plt.plot(days, all_data["纽约"]["high"], marker="^", color="darkorange", linestyle="--", label="纽约最高温", linewidth=2)
plt.plot(days, all_data["纽约"]["low"], marker="^", color="teal", linestyle="--", label="纽约最低温", linewidth=2)
# 在数据点上标注具体温度数值
for i in range(len(days)):
# 苏州标注
plt.text(i, all_data["苏州"]["high"][i] + 0.5, f'{all_data["苏州"]["high"][i]}°', ha='center', va='bottom', color='red', fontsize=9)
plt.text(i, all_data["苏州"]["low"][i] - 0.5, f'{all_data["苏州"]["low"][i]}°', ha='center', va='top', color='blue', fontsize=9)
# 纽约标注 (稍微左右偏移一点防止重叠)
plt.text(i+0.15, all_data["纽约"]["high"][i] + 0.5, f'{all_data["纽约"]["high"][i]}°', ha='center', va='bottom', color='darkorange', fontsize=9)
plt.text(i+0.15, all_data["纽约"]["low"][i] - 0.5, f'{all_data["纽约"]["low"][i]}°', ha='center', va='top', color='teal', fontsize=9)
# 美化图表
plt.title("苏州 vs 纽约 近7日温度变化对比", fontsize=18, pad=15, fontweight='bold')
plt.xlabel("日期 (苏州时区)", fontsize=12)
plt.ylabel("温度 (℃)", fontsize=12)
plt.legend(loc='best', fontsize=10) # 自动选择最佳位置显示图例
plt.grid(True, linestyle='--', alpha=0.5) # 添加虚线网格
plt.tight_layout() # 自动调整布局防遮挡
plt.show()
if __name__ == "__main__":
plot_global_weather_comparison()
