天氣查詢

👤 大芒果 📦 v1.0.0 ⭐ 4.4 ⬇️ 225 下載
🏠 生活服務 免費

📖 技能介紹


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

工作流程

步驟 1: 確定城市程式碼

優先使用已知城市程式碼(見上表)。如果城市不在列表中,從遠端獲取:

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'

步驟 2: 獲取天氣資料

根據查詢型別選擇合適的API:

2.1 即時天氣(快速查詢)

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']}")

2.2 40天預報(長期預報,推薦)

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% 提示"可能有雨"

2.3 逐3小時預報(7天詳細預報)

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]}")

步驟 3: 解析和展示資料

3.1 單日天氣展示

📅 2026年6月27日 週六
🌤️ 多雲轉晴
🌡️ 溫度: 24°C ~ 34°C
💨 風向: 東南風
💧 降水機率: 33%

3.2 多日預報表格

======================================================================
  北京天氣預報 | 2026年6月27日 ~ 7月10日
======================================================================
日期          星期  天氣          溫度         降水機率
----------------------------------------------------------------------
6月27日       週六  多雲          24~34°C     33%
6月28日       週日  晴            24~35°C     50%
...
======================================================================
  說明: 16天后為長期預報,僅有溫度範圍
======================================================================

3.3 逐3小時預報展示

杭州今天逐小時天氣:
08時  多雲  26°C  東南風 <3級
11時  多雲  29°C  東南風 <3級
14時  晴    32°C  南風   <3級
17時  晴    31°C  南風   <3級
20時  晴    28°C  南風   <3級
23時  晴    26°C  西南風 <3級

步驟 4: 處理使用者查詢

根據查詢型別選擇資料來源:

查詢型別 資料來源 說明
今天天氣 即時天氣 + 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)

錯誤處理

  1. API返回空資料:檢查城市程式碼是否正確,嘗試使用備選資料來源
  2. 請求被拒絕:確保帶上 RefererUser-Agent 請求頭
  3. JSON解析失敗:檢查是否正確去掉了 var fc40 = 字首
  4. 資料缺失:16天后的長期預報沒有天氣現象,使用"—"表示
  5. 日期超出範圍:40天預報API有資料範圍限制
  6. 當月API:覆蓋當月剩餘 + 下月初約4-5天
  7. 下月API:覆蓋上月末3天 + 下月全部 + 再下月初約2天
  8. 最遠約40天,超出範圍返回 history 型別資料(應過濾)
  9. 如果查詢的日期沒有資料,提示使用者"該日期暫無預報資料"

注意事項

  1. 城市匹配:支援模糊匹配,如"北京"、"北京市"、"海淀"都能匹配到北京
  2. 預設城市:如果使用者沒有指定城市,詢問使用者

    小蔥技能站7w4.net,專業的AI技能分享平臺。

  3. 資料更新:天氣資料每小時更新,無需頻繁請求
  4. 長期預報限制:40天預報中16天后只有溫度範圍,天氣現象為"—"
  5. 降水提示:當 rain1 > 0hgl > 50% 時,提醒使用者可能下雨
  6. 溫度單位:統一使用 °C
  7. 30天預報:從今天算起30天,需要合併當月和下月API資料。如果使用者指定具體日期範圍(如"6月27日到7月10日"),使用 get_forecast_data() 函式按日期範圍查詢
  8. 資料範圍限制:如果查詢的日期超出API覆蓋範圍(約40天),會返回空結果。此時應提示使用者該日期暫無預報資料,並建議臨近時再查

🤖 AI 評測

這個天氣預報 Skill 做得不錯,功能很全面,能查即時天氣、短中期預報和40天長期預報,資料來源可靠。文件寫得很詳細,程式碼可以直接用,常見問題也有說明。不足是缺少實際查詢示例,而且40天預報後半段只有溫度沒有天氣描述這點不夠顯眼,容易讓使用者困惑。總體質量良好,核心功能紮實,但還可以更完善一些。

📊 多維度評分

適應性4.5
規範性4.2
有效性4.6
可靠性4.2
可信度5

📁 包含檔案 (1 個)

📄 SKILL.md 18.1 KB