298 lines
9.5 KiB
Markdown
298 lines
9.5 KiB
Markdown
# MSN 天气 API 探索报告
|
||
|
||
**生成时间**: 2026-05-03
|
||
**探索目标**: 确认 MSN 天气是否有免费可用的 API 接口
|
||
|
||
---
|
||
|
||
## 一、核心结论
|
||
|
||
**MSN 没有官方公开的免费 REST API**,但存在**微软官方内部 API**(通过浏览器抓包获得),在国内访问速度快,可直接调用。
|
||
|
||
- ❌ **不是**官方对外公开的 API(无文档、无 SLA)
|
||
- ✅ **是**微软官方的后台接口(`assets.msn.cn` / `api.msn.cn` 均为微软域名)
|
||
- ⚠️ 属于**非公开内部 API**,随时可能变更或失效
|
||
|
||
---
|
||
|
||
## 二、可用接口汇总
|
||
|
||
| 接口 | URL | 功能 | 稳定性 |
|
||
|------|-----|------|--------|
|
||
| **当前天气** | `https://assets.msn.cn/service/weather/current` | 获取实时天气 | ✅ 稳定可用 |
|
||
| **每日预报** | `https://assets.msn.cn/service/weather/dailyforecast` | 未来10天预报 | ✅ 稳定可用 |
|
||
| **天气趋势** | `https://assets.msn.cn/service/weather/weathertrends` | 历史+趋势+日历 | ✅ 可用(参数复杂) |
|
||
| api.msn.cn 当前 | `https://api.msn.cn/weather/current` | 用城市名获取 | ✅ 可用(但城市名不准) |
|
||
| api.msn.cn 预报 | `https://api.msn.cn/weather/forecast` | 预报 | ❌ 500错误 |
|
||
|
||
**推荐**:只用 `assets.msn.cn` 的两个接口即可满足大部分需求。
|
||
|
||
---
|
||
|
||
## 三、关键参数说明
|
||
|
||
### 必须参数
|
||
- **`apiKey`**: `j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo`
|
||
- 固定值,修改任意字符即返回 401 Unauthorized
|
||
- 从微软 MSN 天气前端代码中提取
|
||
- **`lat`** / **`lon`**: 经纬度(WGS84 坐标系)
|
||
- **`locale`**: 语言区域,如 `zh-cn`、`en-us`
|
||
|
||
### 可选参数
|
||
- **`units`**: 温度单位,`C`(摄氏)或 `F`(华氏)
|
||
- **`days`**: 预报天数(dailyforecast 接口,最大10天)
|
||
|
||
### 不需要的参数
|
||
- `user`: 测试发现不带也能正常工作
|
||
- `cm`、`ocid`、`fdhead` 等: weathertrends 专用,dailyforecast 不需要
|
||
|
||
### 必须请求头
|
||
```http
|
||
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
|
||
Referer: https://www.msn.com/zh-cn/weather
|
||
```
|
||
不带这些请求头会返回 `401 Authorization Required`。
|
||
|
||
---
|
||
|
||
## 四、调用示例
|
||
|
||
### PowerShell 示例
|
||
|
||
```powershell
|
||
$apiKey = "j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo"
|
||
$headers = @{
|
||
"User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||
"Referer" = "https://www.msn.com/zh-cn/weather"
|
||
}
|
||
|
||
# 获取当前天气(北京)
|
||
$uri_current = "https://assets.msn.cn/service/weather/current?apiKey=$apiKey&lat=39.904172&lon=116.407417&units=C&locale=zh-cn"
|
||
$currentResp = Invoke-RestMethod -Uri $uri_current -Headers $headers
|
||
$current = $currentResp.value[0].responses[0].weather[0].current
|
||
|
||
Write-Host "温度: $($current.temp)C"
|
||
Write-Host "天气: $($current.cap)"
|
||
Write-Host "体感: $($current.feels)C"
|
||
Write-Host "湿度: $($current.rh)%"
|
||
Write-Host "风速: $($current.windSpd)km/h"
|
||
Write-Host "气压: $($current.baro)hPa"
|
||
Write-Host "紫外线: $($current.uv) ($($current.uvDesc))"
|
||
Write-Host "AQI: $($current.aqi) ($($current.aqiSeverity))"
|
||
|
||
# 获取未来7天预报
|
||
$uri_forecast = "https://assets.msn.cn/service/weather/dailyforecast?apiKey=$apiKey&lat=39.904172&lon=116.407417&units=C&locale=zh-cn&days=7"
|
||
$forecastResp = Invoke-RestMethod -Uri $uri_forecast -Headers $headers
|
||
$days = $forecastResp.value[0].responses[0].weather[0].days
|
||
|
||
Write-Host "`n未来7天预报:"
|
||
foreach ($day in $days) {
|
||
$d = $day.daily
|
||
Write-Host " $($d.valid.ToString().Substring(0,10)): $($d.tempLo)-$($d.tempHi)C, 降水$($d.precip)%, 风速$($d.windMax)km/h"
|
||
}
|
||
```
|
||
|
||
### curl 示例
|
||
|
||
```bash
|
||
# 当前天气
|
||
curl -H "User-Agent: Mozilla/5.0" \
|
||
-H "Referer: https://www.msn.com/zh-cn/weather" \
|
||
"https://assets.msn.cn/service/weather/current?apiKey=j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo&lat=39.904172&lon=116.407417&units=C&locale=zh-cn"
|
||
|
||
# 7天预报
|
||
curl -H "User-Agent: Mozilla/5.0" \
|
||
-H "Referer: https://www.msn.com/zh-cn/weather" \
|
||
"https://assets.msn.cn/service/weather/dailyforecast?apiKey=j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo&lat=39.904172&lon=116.407417&units=C&locale=zh-cn&days=7"
|
||
```
|
||
|
||
---
|
||
|
||
## 五、返回数据结构
|
||
|
||
### current 接口响应
|
||
|
||
```json
|
||
{
|
||
"@odata.context": "api.msn.com/weather/$metadata#current",
|
||
"value": [{
|
||
"responses": [{
|
||
"weather": [{
|
||
"current": {
|
||
"temp": 20.0, // 当前温度
|
||
"cap": "晴", // 天气描述
|
||
"capAbbr": "晴", // 简短描述
|
||
"feels": 20.0, // 体感温度
|
||
"rh": 14.0, // 相对湿度 %
|
||
"baro": 1006.0, // 气压 hPa
|
||
"windSpd": 25.0, // 风速 km/h
|
||
"windDir": 360, // 风向(度)
|
||
"windGust": 43.0, // 阵风速度
|
||
"uv": 5.0, // 紫外线指数
|
||
"uvDesc": "中等", // 紫外线描述
|
||
"vis": 30.0, // 能见度 km
|
||
"dewPt": -8.0, // 露点温度
|
||
"aqi": 22.0, // AQI指数
|
||
"aqiSeverity": "优", // AQI等级
|
||
"cloudCover": 15.0, // 云量 %
|
||
"created": "2026-05-03T14:29:06+08:00"
|
||
}
|
||
}],
|
||
"source": {
|
||
"location": "北京, 北京市, 中国",
|
||
"coordinates": {"lat": 39.904172, "lon": 116.407417}
|
||
}
|
||
}]
|
||
}]
|
||
}
|
||
```
|
||
|
||
### dailyforecast 接口响应
|
||
|
||
```json
|
||
{
|
||
"@odata.context": "api.msn.com/weather/$metadata#dailyforecast",
|
||
"value": [{
|
||
"responses": [{
|
||
"weather": [{
|
||
"days": [
|
||
{
|
||
"daily": {
|
||
"valid": "2026-05-03T00:00:00", // 日期
|
||
"tempLo": 8, // 最低温
|
||
"tempHi": 21, // 最高温
|
||
"precip": 5.0, // 降水概率 %
|
||
"windMax": 10.0, // 最大风速
|
||
"windMaxDir": 286, // 风向
|
||
"rhHi": 35.85, // 最高湿度
|
||
"rhLo": 14.0, // 最低湿度
|
||
"icon": 1, // 图标代码
|
||
"symbol": "d000", // 天气符号
|
||
"uv": 5.0, // 紫外线指数
|
||
"uvDesc": "中等"
|
||
}
|
||
}
|
||
// ... 更多天
|
||
]
|
||
}]
|
||
}]
|
||
}]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、多城市验证结果
|
||
|
||
| 城市 | 经纬度 | 状态 | 示例数据 |
|
||
|------|--------|------|----------|
|
||
| 北京 | 39.904172, 116.407417 | ✅ 正常 | 20°C, 晴, 湿度14% |
|
||
| 上海 | 31.2304, 121.4737 | ✅ 正常 | 21°C, 多云, 湿度54% |
|
||
| 广州 | 23.1291, 113.2644 | ✅ 正常 | 24°C, 多云, 湿度79% |
|
||
| 成都 | 30.5728, 104.0668 | ✅ 正常 | 23°C, 局部多云, 湿度42% |
|
||
|
||
---
|
||
|
||
## 七、限制与注意事项
|
||
|
||
### 1. apiKey 固定
|
||
- 当前 key 硬编码在微软前端代码中
|
||
- 修改任意字符即失效(返回 401)
|
||
- **长期有效性未知**,微软可能随时更换
|
||
|
||
### 2. 非公开接口
|
||
- 无官方文档
|
||
- 无 SLA(服务等级协议)保证
|
||
- 数据结构可能随时变更
|
||
|
||
### 3. 需要特定请求头
|
||
必须携带 `User-Agent` 和 `Referer`,否则返回 401。
|
||
|
||
### 4. 限流未知
|
||
未测试请求频率限制,建议生产环境加入适当的请求间隔。
|
||
|
||
### 5. 城市名接口不可靠
|
||
`api.msn.cn` 使用城市名参数可能返回错误城市(测试"北京"返回了也门首都萨那)。
|
||
|
||
### 6. 法律合规
|
||
- 这是非公开接口,用于个人项目/内部工具问题不大
|
||
- **不建议用于商业产品**(随时可能失效,且无使用授权)
|
||
|
||
---
|
||
|
||
## 八、推荐方案
|
||
|
||
### 最简调用方案
|
||
|
||
```powershell
|
||
function Get-MSNWeather {
|
||
param(
|
||
[double]$Lat,
|
||
[double]$Lon,
|
||
[string]$Locale = "zh-cn"
|
||
)
|
||
|
||
$apiKey = "j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo"
|
||
$headers = @{
|
||
"User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||
"Referer" = "https://www.msn.com/zh-cn/weather"
|
||
}
|
||
|
||
# 当前天气
|
||
$currentUri = "https://assets.msn.cn/service/weather/current?apiKey=$apiKey&lat=$Lat&lon=$Lon&units=C&locale=$Locale"
|
||
$current = Invoke-RestMethod -Uri $currentUri -Headers $headers
|
||
|
||
# 7天预报
|
||
$forecastUri = "https://assets.msn.cn/service/weather/dailyforecast?apiKey=$apiKey&lat=$Lat&lon=$Lon&units=C&locale=$Locale&days=7"
|
||
$forecast = Invoke-RestMethod -Uri $forecastUri -Headers $headers
|
||
|
||
return @{
|
||
Current = $current.value[0].responses[0].weather[0].current
|
||
Forecast = $forecast.value[0].responses[0].weather[0].days
|
||
}
|
||
}
|
||
|
||
# 使用示例
|
||
$weather = Get-MSNWeather -Lat 39.904172 -Lon 116.407417
|
||
$weather.Current.temp # 当前温度
|
||
```
|
||
|
||
---
|
||
|
||
## 九、补充:天气图标对照
|
||
|
||
接口返回的 `icon` 或 `symbol` 字段对应天气图标:
|
||
- `0`: 晴天
|
||
- `1`: 大部晴朗
|
||
- `2`: 局部多云
|
||
- `3`: 多云
|
||
- `4`: 阴天
|
||
- `5-6`: 有雾
|
||
- `7-8`: 阴沉
|
||
- `9-12`: 阵雨
|
||
- `13-18`: 雷雨
|
||
- `19-22`: 雨夹雪
|
||
- `23-26`: 小雪
|
||
- `27-30`: 中到大雪
|
||
|
||
图标完整 URL:
|
||
```
|
||
http://img-s-msn-com.akamaized.net/tenant/amp/entityid/AAehR3S.img
|
||
```
|
||
(其中 `AAehR3S` 是从 `urlIcon` 字段获取)
|
||
|
||
---
|
||
|
||
## 十、总结
|
||
|
||
| 项目 | 结论 |
|
||
|------|------|
|
||
| 是否有免费 API | ✅ 有(非公开内部接口) |
|
||
| 国内速度 | ✅ 快(msn.cn 国内节点) |
|
||
| 稳定性 | ⚠️ 未知(非官方,随时可能变) |
|
||
| 数据完整性 | ✅ 完整(当前+预报+AQI+紫外线等) |
|
||
| 推荐用途 | 个人项目、内部工具、原型开发 |
|
||
| 不推荐用途 | 商业产品、长期运行服务 |
|
||
|
||
**建议**:如果用于生产环境,推荐同时准备备用方案(如和风天气、OpenWeatherMap 等)。
|