name: weather-forecast description: "Use when users ask about weather, weather forecast, temperature, today's weather, tomorrow's weather, this week's weather, or city weather conditions in China. Supports real-time weather, 7-day 3-hour interval forecasts, 15-day forecasts, and 40-day long-term forecasts. Trigger phrases include: 'weather', 'forecast', 'temperature', 'will it rain', 'hourly weather', 'next N days', 'next month'."
查询中国城市的实时天气和未来天气预报。支持实时天气、逐3小时预报、7天预报、15天预报和40天长期预报。
使用中国天气网 (weather.com.cn) 的数据:
| 数据类型 | URL | 说明 |
|---|---|---|
| 实时天气 | https://www.weather.com.cn/data/sk/{城市代码}.html |
当前温度、风向、湿度 |
| 7天预报 | https://www.weather.com.cn/weather/{城市代码}.shtml |
含逐3小时预报 |
| 15天预报 | https://www.weather.com.cn/weather15d/{城市代码}.shtml |
较详细的15天预报 |
| 40天预报 | https://d1.weather.com.cn/calendar_new/{年份}/{城市代码}_{年份月份}.html |
长期预报 |
| 城市代码 | https://j.i8tq.com/weather2020/search/city.js |
城市代码查询 |
| 城市 | 代码 | 城市 | 代码 |
|---|---|---|---|
| 北京 | 101010100 | 武汉 | 101200101 |
| 上海 | 101020100 | 西安 | 101110101 |
| 广州 | 101280101 | 杭州 | 101210101 |
| 深圳 | 101280601 | 南京 | 101190101 |
| 成都 | 101270101 | 重庆 | 101040100 |
优先使用已知城市代码(见上表)。如果城市不在列表中,从远程获取:
import urllib.request, json, re
headers = {'User-Agent': 'Mozilla/5.0'}
req = urllib.request.Request('https://j.i8tq.com/weather2020/search/city.js', headers=headers)
resp = urllib.request.urlopen(req)
text = resp.read().decode('utf-8')
# 解析 city_data
match = re.search(r'var city_data\s*=\s*(\{.*\});', text, re.DOTALL)
city_data = json.loads(match.group(1))
# 搜索城市
def find_city(city_data, name):
for province, cities in city_data.items():
for city, districts in cities.items():
for district, info in districts.items():
if name in info.get('NAMECN', ''):
return info.get('AREAID')
return None
city_id = find_city(city_data, '北京') # 返回 '101010100'
根据查询类型选择合适的API:
import urllib.request, json
headers = {'User-Agent': 'Mozilla/5.0'}
url = 'https://www.weather.com.cn/data/sk/101010100.html'
req = urllib.request.Request(url, headers=headers)
resp = urllib.request.urlopen(req)
data = json.loads(resp.read().decode('utf-8'))
info = data['weatherinfo']
print(f"{info['city']}: {info['temp']}°C, {info['WD']} {info['WS']}, 湿度{info['SD']}")
import urllib.request, json, re, time
headers = {
'Referer': 'https://www.weather.com.cn/',
'User-Agent': 'Mozilla/5.0'
}
def get_40day_forecast(city_id, year_month):
"""获取指定月份的40天预报数据"""
url = f'https://d1.weather.com.cn/calendar_new/{year_month[:4]}/{city_id}_{year_month}.html?_={int(time.time()*1000)}'
req = urllib.request.Request(url, headers=headers)
resp = urllib.request.urlopen(req)
text = resp.read().decode('utf-8')
# 去掉 JSONP 前缀
text = re.sub(r'^var fc40\s*=\s*', '', text)
return json.loads(text)
# 示例:获取2026年6月数据
data = get_40day_forecast('101010100', '202606')
# 过滤有效预报数据(排除历史数据)
valid = [d for d in data if d.get('cla') not in ('history', 'obs')]
# 按日期去重(同一日期可能有多条数据,优先保留有w1的)
seen = {}
for d in valid:
date = d.get('date', '')
if date not in seen:
seen[date] = d
elif not seen[date].get('w1') and d.get('w1'):
seen[date] = d
# 查找特定日期
target_date = '20260627'
if target_date in seen:
day = seen[target_date]
print(f"{day['date']} 周{day['wk']}: {day.get('w1','--')} {day.get('min','--')}~{day.get('max','--')}°C")
数据字段说明:
| 字段 | 说明 | 示例 |
|---|---|---|
| date | 日期 (YYYYMMDD) | "20260627" |
| wk | 星期 | "六" |
| w1 | 天气现象(白天) | "多云" |
| w2 | 天气现象(夜间) | "晴" |
| c1 | 天气图标代码(白天) | "1" |
| c2 | 天气图标代码(夜间) | "0" |
| max/min | 最高/最低温度 | "34"/"24" |
| hmax/hmin | 历史均值温度 | "32"/"22" |
| hgl | 降水概率 | "33%" |
| rain1 | 降水量(mm) | "0.0" |
| wd1/ws1 | 风向/风力 | "东南风"/"3级" |
| cla | 数据类型 | 见下方说明 |
cla字段含义:
| cla值 | 说明 | 数据完整性 |
|---|---|---|
d15 pre |
15天预报(今天~3天内) | ✅ 完整(含w1/w2/c1/c2/wd1/ws1) |
d15 |
15天预报(当天) | ✅ 完整 |
d15 next |
15天预报(跨月前几天) | ✅ 完整 |
d40 |
40天预报 | ⚠️ 无w1/w2,但有hgl/rain1 |
d40 next |
40天预报(跨月) | ⚠️ 无w1/w2,但有hgl/rain1 |
history |
历史均值 | ❌ 过滤掉 |
obs |
已观测数据 | ❌ 过滤掉 |
重要:
- 16天后(d40/d40 next)没有天气现象文字(w1为空),但有降水概率(hgl)和降水量(rain1)
- 网页版根据 hgl 值显示雨图标:hgl > 50% 显示大雨,30-50% 显示中雨,< 30% 显示小雨或无雨
- 展示时可根据 hgl 推断降水可能性,如 hgl > 50% 提示"可能有雨"
import urllib.request, json, re
headers = {
'Referer': 'https://www.weather.com.cn/',
'User-Agent': 'Mozilla/5.0'
}
def get_hourly_forecast(city_id):
"""获取7天逐3小时预报"""
url = f'https://www.weather.com.cn/weather/{city_id}.shtml'
req = urllib.request.Request(url, headers=headers)
resp = urllib.request.urlopen(req)
html = resp.read().decode('utf-8')
# 精确提取 hour3data JSON(通过括号匹配)
start = html.index('var hour3data')
brace_start = html.index('{', start)
depth = 0
end = brace_start
for i in range(brace_start, len(html)):
if html[i] == '{': depth += 1
elif html[i] == '}':
depth -= 1
if depth == 0:
end = i + 1
break
return json.loads(html[brace_start:end])
data = get_hourly_forecast('101010100')
# data['7d'] 是7天数据,每天8个时间点
# data['7d'][0] = 今天, data['7d'][1] = 明天, ...
# 解析单条数据
# 格式: "27日11时,d01,多云,31℃,东南风,<3级,2"
# 日期时间,天气代码,天气现象,温度,风向,风力,降水概率
for item in data['7d'][0]: # 今天
parts = item.split(',')
print(f"{parts[0]} {parts[2]} {parts[3]} {parts[4]} {parts[5]}")
📅 2026年6月27日 周六
🌤️ 多云转晴
🌡️ 温度: 24°C ~ 34°C
💨 风向: 东南风
💧 降水概率: 33%
======================================================================
北京天气预报 | 2026年6月27日 ~ 7月10日
======================================================================
日期 星期 天气 温度 降水概率
----------------------------------------------------------------------
6月27日 周六 多云 24~34°C 33%
6月28日 周日 晴 24~35°C 50%
...
======================================================================
说明: 16天后为长期预报,仅有温度范围
======================================================================
杭州今天逐小时天气:
08时 多云 26°C 东南风 <3级
11时 多云 29°C 东南风 <3级
14时 晴 32°C 南风 <3级
17时 晴 31°C 南风 <3级
20时 晴 28°C 南风 <3级
23时 晴 26°C 西南风 <3级
根据查询类型选择数据源:
| 查询类型 | 数据源 | 说明 |
|---|---|---|
| 今天天气 | 实时天气 + 7天预报 | 优先使用实时天气 |
| 明天/后天 | 7天逐3小时预报 | 包含详细天气现象 |
| 未来一周 | 7天预报 | 每天一条汇总 |
| 未来15天 | 40天预报(当月API) | 有完整天气现象 |
| 未来30天 | 40天预报(当月+下月API合并,自动计算月份) | 前15天有天气现象,16天后只有温度 |
| 未来40天 | 40天预报(当月+下月+下下月API合并,自动计算月份) | 前15天有天气现象,16天后只有温度 |
| 逐小时天气 | 7天逐3小时预报 | 每3小时一条 |
跨月查询处理(完整方案):
from datetime import datetime, timedelta def get_40day_forecast(city_id, year_month): """获取指定月份的40天预报数据""" import urllib.request, json, re, time headers = { 'Referer': 'https://www.weather.com.cn/', 'User-Agent': 'Mozilla/5.0' } url = f'https://d1.weather.com.cn/calendar_new/{year_month[:4]}/{city_id}_{year_month}.html?_={int(time.time()*1000)}' req = urllib.request.Request(url, headers=headers) resp = urllib.request.urlopen(req) text = resp.read().decode('utf-8') text = re.sub(r'^var fc40\s*=\s*', '', text) return json.loads(text) def get_forecast_data(city_id, start_date, end_date): """ 获取指定日期范围的预报数据。 关键逻辑: - 计算需要请求的月份(可能跨2-3个月) - 合并所有月份的数据 - 过滤历史数据(cla=history/obs) - 按日期去重:同一日期有多条时,优先保留有天气现象(w1)的 - 筛选目标日期范围 注意:40天预报API返回的数据范围有限: - 当月API:当月剩余天数 + 下月初约4-5天(共约35天) - 下月API:上月末3天 + 下月全部 + 再下月初约2天 - 最远可覆盖约40天,超出范围的数据不存在 """ # 计算需要的月份 months = set() current = datetime.strptime(start_date, '%Y%m%d') end = datetime.strptime(end_date, '%Y%m%d') while current <= end: months.add(current.strftime('%Y%m')) # 移到下个月 if current.month == 12: current = datetime(current.year + 1, 1, 1) else: current = datetime(current.year, current.month + 1, 1) # 获取所有月份数据并合并 all_data = [] for month in sorted(months): data = get_40day_forecast(city_id, month) all_data.extend(data) # 过滤和去重 valid = [d for d in all_data if d.get('cla') not in ('history', 'obs')] seen = {} for d in valid: date = d.get('date', '') if date not in seen: seen[date] = d elif not seen[date].get('w1') and d.get('w1'): seen[date] = d # 筛选日期范围 result = [] for date_str in sorted(seen.keys()): if start_date <= date_str <= end_date: result.append(seen[date_str]) return result def get_forecast_months(start_date, days): """ 计算指定天数范围内需要获取的月份。 例如:5月31日开始,40天后是7月10日,需要5月、6月、7月共3个月。 """ start = datetime.strptime(start_date, '%Y%m%d') end = start + timedelta(days=days) months = set() current = start while current <= end: months.add(current.strftime('%Y%m')) if current.month == 12: current = datetime(current.year + 1, 1, 1) else: current = datetime(current.year, current.month + 1, 1) return sorted(months) def get_30day_forecast(city_id): """ 获取从今天开始的30天预报。 自动计算需要请求的月份(当月+下月,可能需要3个月),合并数据。 返回去重后的日期->数据字典。 """ today = datetime.now() today_str = today.strftime('%Y%m%d') # 计算需要的月份(30天可能跨2-3个月) months_needed = get_forecast_months(today_str, 30) all_data = [] for month in months_needed: data = get_40day_forecast(city_id, month) all_data.extend(data) valid = [d for d in all_data if d.get('cla') not in ('history', 'obs')] seen = {} for d in valid: date = d.get('date', '') if date not in seen: seen[date] = d elif not seen[date].get('w1') and d.get('w1'): seen[date] = d return seen def get_40day_forecast_complete(city_id): """ 获取从今天开始的40天完整预报。 自动计算需要请求的月份(当月+下月+下下月),合并数据。 返回去重后的日期->数据字典。 注意:API返回的数据范围有限,最远约40天。 16天后(d40类型)只有温度范围,没有天气现象(w1)。 """ today = datetime.now() today_str = today.strftime('%Y%m%d') # 计算需要的月份(40天可能跨3个月) months_needed = get_forecast_months(today_str, 40) all_data = [] for month in months_needed: data = get_40day_forecast(city_id, month) all_data.extend(data) valid = [d for d in all_data if d.get('cla') not in ('history', 'obs')] seen = {} for d in valid: date = d.get('date', '') if date not in seen: seen[date] = d elif not seen[date].get('w1') and d.get('w1'): seen[date] = d return seen def infer_weather_from_hgl(hgl): """根据降水概率推断天气状况(用于d40数据,没有w1字段时)""" try: val = int(hgl.replace('%', '')) if val >= 70: return '🌧️大雨' elif val >= 50: return '🌦️中雨' elif val >= 30: return '🌦️小雨' elif val >= 10: return '☁️阴' else: return '☀️晴' except (ValueError, AttributeError): return '—' def display_forecast(city_name, city_id, days=30): """获取并展示N天天气预报(通用函数,支持30天或40天)""" import sys, io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') if days == 40: seen = get_40day_forecast_complete(city_id) else: seen = get_30day_forecast(city_id) dates = sorted(seen.keys()) # 只取前N天 display_dates = dates[:days] print(f"{'=' * 72}") print(f" {city_name}{days}天天气预报") print(f" {display_dates[0][:4]}年{int(display_dates[0][4:6])}月{int(display_dates[0][6:8])}日 ~ " f"{display_dates[-1][:4]}年{int(display_dates[-1][4:6])}月{int(display_dates[-1][6:8])}日") print(f"{'=' * 72}") print(f"{'日期':<12} {'星期':<6} {'天气':<14} {'温度':<12} {'降水概率':<10}") print(f"{'-' * 72}") for date_str in display_dates: d = seen[date_str] month = date_str[4:6] day = date_str[6:8] wk = d.get('wk', '') w1 = d.get('w1', '') w2 = d.get('w2', '') max_t = d.get('max', '--') min_t = d.get('min', '--') hgl = d.get('hgl', '--') cla = d.get('cla', '') # 有w1时使用文字描述,没有时根据降水概率推断 if w1: weather = w1 if w2: weather += f'转{w2}' else: # d40数据没有w1,根据hgl降水概率推断天气 weather = infer_weather_from_hgl(hgl) print(f"{int(month)}月{int(day):02d}日 周{wk} {weather:<12} {min_t}~{max_t}°C {hgl}") print(f"{'=' * 72}") print(f" 说明:") print(f" - 前15天有完整天气现象描述") print(f" - 16天后为长期预报,根据降水概率推断天气状况") print(f" - 数据来源:中国天气网 (weather.com.cn)") print(f"{'=' * 72}") def display_30day_forecast(city_name, city_id): """获取并展示30天天气预报""" display_forecast(city_name, city_id, days=30) def display_40day_forecast(city_name, city_id): """获取并展示40天天气预报""" display_forecast(city_name, city_id, days=40)7w4.net小葱技能站,你的AI助手技能库。
Referer 和 User-Agent 请求头var fc40 = 前缀history 类型数据(应过滤)rain1 > 0 或 hgl > 50% 时,提醒用户可能下雨°Cget_forecast_data() 函数按日期范围查询这个天气预报 Skill 做得不错,功能很全面,能查实时天气、短中期预报和40天长期预报,数据来源可靠。文档写得很详细,代码可以直接用,常见问题也有说明。不足是缺少实际查询示例,而且40天预报后半段只有温度没有天气描述这点不够显眼,容易让用户困惑。总体质量良好,核心功能扎实,但还可以更完善一些。