跳到主要内容

天气查询

收藏0

查询任意城市或地点的当前天气与未来数日预报(温度、天气状况、降水概率、风速、湿度),无需 API Key,数据来自 Open-Meteo 免费接口。

让 AI 安装

Minsaroun最新版本 0.1.0更新于 2026-11-01下载 0 次

weather.py

4.9 KB
1#!/usr/bin/env python3
2# 天气查询(Open-Meteo 免费 API,无需 Key)
3import argparse, json, sys, urllib.parse, urllib.request
4
5GEO_URL = 'https://geocoding-api.open-meteo.com/v1/search'
6WX_URL = 'https://api.open-meteo.com/v1/forecast'
7
8# WMO weather code -> 中文 / 英文
9WMO = {
10 0: ('晴', 'Clear sky'),
11 1: ('基本晴', 'Mainly clear'),
12 2: ('多云', 'Partly cloudy'),
13 3: ('阴', 'Overcast'),
14 45: ('雾', 'Fog'),
15 48: ('冻雾', 'Depositing rime fog'),
16 51: ('毛毛雨', 'Light drizzle'),
17 53: ('小到中雨', 'Drizzle'),
18 55: ('雨', 'Dense drizzle'),
19 56: ('冻毛毛雨', 'Freezing drizzle'),
20 57: ('强冻毛毛雨', 'Dense freezing drizzle'),
21 61: ('小雨', 'Slight rain'),
22 63: ('中雨', 'Moderate rain'),
23 65: ('大雨', 'Heavy rain'),
24 66: ('冻雨', 'Freezing rain'),
25 67: ('强冻雨', 'Heavy freezing rain'),
26 71: ('小雪', 'Slight snow'),
27 73: ('中雪', 'Moderate snow'),
28 75: ('大雪', 'Heavy snow'),
29 77: ('雪粒', 'Snow grains'),
30 80: ('阵雨', 'Slight showers'),
31 81: ('阵雨', 'Moderate showers'),
32 82: ('强阵雨', 'Violent showers'),
33 85: ('阵雪', 'Slight snow showers'),
34 86: ('强阵雪', 'Heavy snow showers'),
35 95: ('雷雨', 'Thunderstorm'),
36 96: ('雷雨伴冰雹', 'Thunderstorm with hail'),
37 99: ('强雷雨伴冰雹', 'Thunderstorm with heavy hail'),
38}
39
40
41def fetch(url):
42 req = urllib.request.Request(url, headers={'User-Agent': 'weather-skill/1.0'})
43 with urllib.request.urlopen(req, timeout=15) as r:
44 return json.loads(r.read().decode())
45
46
47def geocode(name):
48 q = urllib.parse.quote(name)
49 data = fetch(f'{GEO_URL}?name={q}&count=1&language=zh&format=json')
50 results = data.get('results') or []
51 if not results:
52 return None
53 r = results[0]
54 return r.get('latitude'), r.get('longitude'), r.get('name'), r.get('country'), r.get('admin1')
55
56
57def weather(lat, lon, days, lang):
58 params = {
59 'latitude': lat, 'longitude': lon,
60 'current': 'temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m,wind_direction_10m',
61 'daily': 'weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max',
62 'timezone': 'auto', 'forecast_days': days,
63 }
64 q = urllib.parse.urlencode(params)
65 data = fetch(f'{WX_URL}?{q}')
66 cur = data.get('current', {})
67 daily = data.get('daily', {})
68 lines = []
69 # 当前
70 code = cur.get('weather_code', -1)
71 if lang == 'zh':
72 desc = WMO.get(code, ('未知', 'Unknown'))[0]
73 lines.append(f"当前:{desc} {cur.get('temperature_2m')}°C(体感 {cur.get('apparent_temperature')}°C) "
74 f"湿度 {cur.get('relative_humidity_2m')}% 风速 {cur.get('wind_speed_10m')} km/h")
75 else:
76 desc = WMO.get(code, ('未知', 'Unknown'))[1]
77 lines.append(f"Now: {desc} {cur.get('temperature_2m')}°C (feels like {cur.get('apparent_temperature')}°C) "
78 f"humidity {cur.get('relative_humidity_2m')}% wind {cur.get('wind_speed_10m')} km/h")
79 # 预报
80 times = daily.get('time', [])
81 for i, t in enumerate(times):
82 d = daily.get('weather_code', [])[i] if daily.get('weather_code') else -1
83 dmax = daily.get('temperature_2m_max', [])[i] if daily.get('temperature_2m_max') else '?'
84 dmin = daily.get('temperature_2m_min', [])[i] if daily.get('temperature_2m_min') else '?'
85 pop = daily.get('precipitation_probability_max', [])[i] if daily.get('precipitation_probability_max') else 0
86 if lang == 'zh':
87 desc = WMO.get(d, ('未知', 'Unknown'))[0]
88 tag = '今天' if i == 0 else t[5:]
89 lines.append(f"{tag}:{desc} {dmin}~{dmax}°C 降水概率 {pop}%")
90 else:
91 desc = WMO.get(d, ('未知', 'Unknown'))[1]
92 lines.append(f"{t}: {desc} {dmin}~{dmax}°C precip {pop}%")
93 return '\n'.join(lines)
94
95
96def main():
97 p = argparse.ArgumentParser(description='天气查询')
98 p.add_argument('place', nargs='?', help='城市名(中英文均可)')
99 p.add_argument('--lat', type=float)
100 p.add_argument('--lon', type=float)
101 p.add_argument('--days', type=int, default=1)
102 p.add_argument('--lang', choices=['zh', 'en'], default='zh')
103 a = p.parse_args()
104 if a.place:
105 g = geocode(a.place)
106 if not g:
107 print(f'找不到地点: {a.place}')
108 sys.exit(1)
109 lat, lon, name, country, admin1 = g
110 label = f'{name}({admin1}, {country})' if a.lang == 'zh' else f'{name} ({admin1}, {country})'
111 elif a.lat is not None and a.lon is not None:
112 lat, lon, label = a.lat, a.lon, f'{a.lat},{a.lon}'
113 else:
114 p.error('需要城市名或 --lat/--lon')
115 return
116 try:
117 out = weather(lat, lon, a.days, a.lang)
118 print(f'{label}\n{out}')
119 except Exception as e:
120 print(f'查询失败: {e}')
121 sys.exit(1)
122
123
124if __name__ == '__main__':
125 main()

文件内容是平台检查时读出的文本,只供查看。平台不会运行包里的任何脚本或程序。