name: china-weather description: 中國天氣查詢工具。Use when user wants to check weather for Chinese cities. Supports current weather, forecast, air quality, life index with rich data and beautiful formatting. Free APIs with fallback strategy. 天氣查詢、天氣預報、空氣質量。 version: 1.0.3 license: MIT-0 metadata: {"openclaw": {"emoji": "🌤️", "requires": {"bins": ["curl", "python3"], "env": []}}}
中國天氣查詢工具,支援即時天氣、天氣預報、空氣質量、生活指數,提供豐富的資料和精美的排版輸出。
7w4.net小蔥技能。
| API | 認證方式 | 免費額度 | 配置要求 |
|---|---|---|---|
| wttr.in | 無需認證 | 無限制 | ❌ 無需任何配置 |
| 和風天氣 | API Host + Key | 1000次/天 | ✅ 需要API_HOST + API_KEY |
| OpenWeatherMap | API Key | 1000次/天 | ✅ 需要API_KEY + 繫結信用卡 |
| 心知天氣 | API Key | 無限制 | ✅ 需要API_KEY |
推薦:優先使用wttr.in(無需任何配置,免費無限制)
# 優先順序:無需配置 → 需要配置
API_CHAIN = [
{"name": "wttr", "requires_key": False}, # 無需任何配置
{"name": "qweather", "requires_key": True}, # 需要API Host + Key
{"name": "openweathermap", "requires_key": True} # 需要Key + 信用卡
]
和風天氣從2026年起不再支援公共API地址,必須使用專屬API Host:
# 環境變數配置
export QWEATHER_API_HOST="你的API_HOST" # 如:abc1234xyz.def.qweatherapi.com
export QWEATHER_API_KEY="你的API_KEY"
# 正確的API呼叫
curl "https://${QWEATHER_API_HOST}/v7/weather/now?location=101010100&key=${QWEATHER_API_KEY}"
注意:響應使用gzip壓縮,需要新增--compressed引數或在程式碼中處理
API_CHAIN = [
{"name": "qweather", "priority": 1, "fallback": True},
{"name": "seniverse", "priority": 2, "fallback": True},
{"name": "openweathermap", "priority": 3, "fallback": True},
{"name": "wttr", "priority": 4, "fallback": False}
]
def get_weather_with_fallback(city):
"""Try APIs in priority order, fallback on failure"""
for api in API_CHAIN:
try:
result = call_api(api["name"], city)
if result:
return result
except Exception as e:
if api["fallback"]:
continue
else:
raise
return None
pip install requests
python3 << 'PYEOF'
import os
import requests
import json
from datetime import datetime
class WeatherService:
def __init__(self):
self.apis = {
'qweather': QWeatherAPI(),
'seniverse': SeniverseAPI(),
'openweathermap': OpenWeatherMapAPI(),
'wttr': WttrAPI()
}
def get_weather(self, city, days=7):
"""Get weather with fallback strategy"""
for name, api in self.apis.items():
try:
result = api.get_weather(city, days)
if result:
result['source'] = name
return result
except Exception as e:
print(f"⚠️ {name} failed: {e}")
continue
return None
def format_weather(self, data, lang='zh'):
"""Format weather data beautifully"""
if lang == 'zh':
return self._format_chinese(data)
else:
return self._format_english(data)
def _format_chinese(self, data):
"""Chinese format output"""
output = []
output.append(f"┌{'─'*50}┐")
output.append(f"│ 🌤️ {data['city']}天氣預報")
output.append(f"└{'─'*50}┘")
output.append("")
# 當前天氣
output.append(f"📍 當前天氣")
output.append(f"├─ 🌡️ 溫度: {data['current']['temp']}°C (體感 {data['current']['feels_like']}°C)")
output.append(f"├─ 🌤️ 天氣: {data['current']['weather']}")
output.append(f"├─ 💧 溼度: {data['current']['humidity']}%")
output.append(f"├─ 🌬️ 風向: {data['current']['wind_dir']} {data['current']['wind_speed']}km/h")
output.append(f"└─ 👁️ 能見度: {data['current']['visibility']}km")
output.append("")
# 空氣質量
if 'aqi' in data:
output.append(f"🌫️ 空氣質量")
aqi = data['aqi']
aqi_level = self._get_aqi_level(aqi['value'])
output.append(f"├─ AQI: {aqi['value']} ({aqi_level})")
output.append(f"├─ PM2.5: {aqi.get('pm25', 'N/A')}μg/m³")
output.append(f"├─ PM10: {aqi.get('pm10', 'N/A')}μg/m³")
output.append(f"└─ 首要汙染物: {aqi.get('primary', 'N/A')}")
output.append("")
# 未來預報
output.append(f"📅 未來預報")
for day in data.get('forecast', [])[:7]:
output.append(f"├─ {day['date']}: {day['weather']} {day['temp_min']}~{day['temp_max']}°C")
output.append("")
# 生活指數
if 'indices' in data:
output.append(f"🧥 生活指數")
for idx in data['indices'][:4]:
output.append(f"├─ {idx['name']}: {idx['level']}")
return '\n'.join(output)
def _format_english(self, data):
"""English format output"""
output = []
output.append(f"┌{'─'*50}┐")
output.append(f"│ 🌤️ {data['city']} Weather Forecast")
output.append(f"└{'─'*50}┘")
output.append("")
# Current weather
output.append(f"📍 Current Weather")
output.append(f"├─ 🌡️ Temperature: {data['current']['temp']}°C (Feels like {data['current']['feels_like']}°C)")
output.append(f"├─ 🌤️ Weather: {data['current']['weather']}")
output.append(f"├─ 💧 Humidity: {data['current']['humidity']}%")
output.append(f"├─ 🌬️ Wind: {data['current']['wind_dir']} {data['current']['wind_speed']}km/h")
output.append(f"└─ 👁️ Visibility: {data['current']['visibility']}km")
output.append("")
# Air quality
if 'aqi' in data:
output.append(f"🌫️ Air Quality")
aqi = data['aqi']
aqi_level = self._get_aqi_level(aqi['value'])
output.append(f"├─ AQI: {aqi['value']} ({aqi_level})")
output.append(f"├─ PM2.5: {aqi.get('pm25', 'N/A')}μg/m³")
output.append(f"├─ PM10: {aqi.get('pm10', 'N/A')}μg/m³")
output.append(f"└─ Primary: {aqi.get('primary', 'N/A')}")
output.append("")
# Forecast
output.append(f"📅 Forecast")
for day in data.get('forecast', [])[:7]:
output.append(f"├─ {day['date']}: {day['weather']} {day['temp_min']}~{day['temp_max']}°C")
output.append("")
return '\n'.join(output)
def _get_aqi_level(self, aqi):
"""Get AQI level description"""
if aqi <= 50:
return "優 🟢"
elif aqi <= 100:
return "良 🟡"
elif aqi <= 150:
return "輕度汙染 🟠"
elif aqi <= 200:
return "中度汙染 🔴"
elif aqi <= 300:
return "重度汙染 🟣"
else:
return "嚴重汙染 🟤"
class WttrAPI:
"""wttr.in - Free, no API key required"""
def get_weather(self, city, days=7):
url = f"https://wttr.in/{city}?format=j1"
response = requests.get(url, timeout=10)
data = response.json()
current = data['current_condition'][0]
result = {
'city': city,
'current': {
'temp': current['temp_C'],
'feels_like': current['FeelsLikeC'],
'weather': current['lang_zh'][0]['value'] if 'lang_zh' in current else current['weatherDesc'][0]['value'],
'humidity': current['humidity'],
'wind_dir': current['winddir16Point'],
'wind_speed': current['windspeedKmph'],
'visibility': current['visibility']
},
'forecast': []
}
for day in data['weather'][:days]:
result['forecast'].append({
'date': day['date'],
'weather': day['hourly'][4]['weatherDesc'][0]['value'],
'temp_min': day['mintempC'],
'temp_max': day['maxtempC']
})
return result
class QWeatherAPI:
"""和風天氣 - 需要API Host + Key"""
def __init__(self):
self.api_key = os.environ.get('QWEATHER_API_KEY', '')
self.api_host = os.environ.get('QWEATHER_API_HOST', '')
# 預設使用GeoAPI端點
self.geo_url = f"https://{self.api_host}/geo/v2" if self.api_host else ''
self.weather_url = f"https://{self.api_host}/v7" if self.api_host else ''
def get_weather(self, city, days=7):
if not self.api_key or not self.api_host:
return None
# Get city ID
city_id = self._get_city_id(city)
if not city_id:
return None
# Get current weather (with gzip support)
url = f"{self.weather_url}/weather/now?location={city_id}&key={self.api_key}"
response = requests.get(url, timeout=10, headers={'Accept-Encoding': 'gzip'})
response.encoding = 'utf-8'
try:
current_data = response.json()
except:
return None
if current_data.get('code') != '200':
return None
now = current_data['now']
result = {
'city': city,
'current': {
'temp': now['temp'],
'feels_like': now['feelsLike'],
'weather': now['text'],
'humidity': now['humidity'],
'wind_dir': now['windDir'],
'wind_speed': now['windSpeed'],
'visibility': now['vis']
},
'forecast': self._get_forecast(city_id, days)
}
# Get AQI
aqi = self._get_aqi(city_id)
if aqi:
result['aqi'] = aqi
return result
def _get_city_id(self, city):
"""Get city ID from city name"""
url = f"{self.geo_url}/city/lookup?location={city}&key={self.api_key}"
response = requests.get(url, timeout=10, headers={'Accept-Encoding': 'gzip'})
try:
data = response.json()
if data.get('code') == '200' and data.get('location'):
return data['location'][0]['id']
except:
pass
return None
def _get_forecast(self, city_id, days):
"""Get weather forecast"""
url = f"{self.base_url}/weather/7d?location={city_id}&key={self.api_key}"
response = requests.get(url, timeout=10)
data = response.json()
forecast = []
for day in data.get('daily', [])[:days]:
forecast.append({
'date': day['fxDate'],
'weather': day['textDay'],
'temp_min': day['tempMin'],
'temp_max': day['tempMax']
})
return forecast
def _get_aqi(self, city_id):
"""Get air quality index"""
url = f"{self.base_url}/air/now?location={city_id}&key={self.api_key}"
response = requests.get(url, timeout=10)
data = response.json()
if data.get('code') == '200':
air = data['now']
return {
'value': int(air['aqi']),
'pm25': air.get('pm2p5'),
'pm10': air.get('pm10'),
'primary': air.get('primary')
}
return None
# Example usage
service = WeatherService()
# Query weather
data = service.get_weather('Beijing')
if data:
# Format output
output = service.format_weather(data, lang='zh')
print(output)
else:
print("❌ 無法獲取天氣資料")
PYEOF
# 註冊: https://dev.qweather.com
# 免費額度: 1000次/天
export QWEATHER_API_KEY="your_key_here"
# 註冊: https://openweathermap.org/api
# 免費額度: 1000次/天
export OPENWEATHER_API_KEY="your_key_here"
# 註冊: https://www.seniverse.com
# 免費額度: 無限制(基礎資料)
export SENIVERSE_API_KEY="your_key_here"
# 直接使用,無需註冊
curl "wttr.in/Beijing?format=j1"
| 欄位 | 說明 | 單位 |
|---|---|---|
| temp | 溫度 | °C |
| feels_like | 體感溫度 | °C |
| weather | 天氣狀況 | 文字 |
| humidity | 溼度 | % |
| wind_dir | 風向 | 方位 |
| wind_speed | 風速 | km/h |
| visibility | 能見度 | km |
| pressure | 氣壓 | hPa |
| 欄位 | 說明 | 單位 |
|---|---|---|
| aqi | 空氣質量指數 | 0-500 |
| pm25 | PM2.5濃度 | μg/m³ |
| pm10 | PM10濃度 | μg/m³ |
| so2 | 二氧化硫 | μg/m³ |
| no2 | 二氧化氮 | μg/m³ |
| co | 一氧化碳 | mg/m³ |
| o3 | 臭氧 | μg/m³ |
| 指數 | 說明 | 等級 |
|---|---|---|
| 穿衣指數 | 建議穿著 | 寒冷/涼爽/舒適/炎熱 |
| 紫外線指數 | UV強度 | 弱/中等/強/很強 |
| 運動指數 | 適合運動程度 | 適宜/較適宜/不宜 |
| 洗車指數 | 適合洗車程度 | 適宜/較適宜/不宜 |
| 感冒指數 | 感冒風險 | 低/中/高 |
這是一款功能豐富的中文天氣查詢工具,支援即時天氣、預報、空氣質量和生活指數查詢。最大優點是操作簡單,無需配置即可查詢天氣,如果主服務不可用還會自動切換備用介面。介面排版清晰美觀,中英文都能用。但部分高階功能(如空氣質量、生活指數)需要註冊第三方賬號才能完全使用,整體文件和實際程式碼存在一些小不一致,對普通使用者來說可能造成困惑。總的來說基礎功能實用可靠,適合日常天氣查詢使用。