从curl到工程封装:台风实时路径API实践
适用场景与接口能力边界
台风实时路径 API 提供西北太平洋和南海区域的台风监测数据,包括活跃台风列表、单台风完整移动路径(实况点+预报点)以及风云卫星云图。适用于防灾预警系统、气象可视化大屏、航运路线规划、户外出行决策等场景。
接口能力边界:单次请求可获取活跃台风列表(action=list)、单台风详细路径(action=detail,需配合台风ID)、卫星云图(action=images)或综合数据(action=all,一次返回活跃台风+最新云图)。QPS 限制为 10 次/秒,无需认证也可调用,但携带 API Key(通过Authorization头)可提高额度。接口采用 POST 方法,请求体为 JSON。
前置准备:鉴权与请求格式
鉴权方式
根据素材,请求头可携带Authorization: Bearer <你的API Key>(可选),但 curl 示例使用的是X-API-Key头。文档显示两种方式均可。为安全,推荐使用Authorization头。本文示例统一使用X-API-Key(与示例一致)。
请求体参数
| 参数名 | 类型 | 必需 | 说明 |
|---|---|---|---|
| action | string | 否 | 操作类型:list / detail / images / all |
| id | string | 否 | 台风ID(action=detail时必填) |
| status | string | 否 | list时使用:active(仅活跃)/ all(含停编) |
| limit | number | 否 | images时使用:云图数量1-50 |
若不传任何参数,默认返回活跃台风列表。
curl 快速验证
首先用 curl 获取活跃台风列表:
curl -sS \ -X POST \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"action":"list","status":"active"}' \ "https://v1.apizero.cn/api/typhoon"替换YOUR_API_KEY为实际密钥。返回 JSON 中data字段包含台风列表数组,每个元素有id、name_cn、name_en、tc_num、is_active等字段。
获取单个台风详细路径(以 id=3257931 为例):
curl -sS \ -X POST \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id":"3257931","action":"detail"}' \ "https://v1.apizero.cn/api/typhoon"返回中data.current为最新实况,data.points为路径点列表。
返回字段解读
成功响应结构:
{ "code": 0, "msg": "成功", "request_id": "abc123", "data": { ... } }data字段内容随 action 变化。以detail为例,主要字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 台风唯一ID |
| tc_num | string | 台风编号,如2609 |
| name_cn | string | 中文名 |
| name_en | string | 英文名 |
| is_active | boolean | 是否活跃 |
| current | object | 最新实况(见下) |
| point_count | number | 路径点总数 |
| points | array | 路径点列表(实况+预报) |
current对象字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| time_cst | string | 时间(北京时间) |
| longitude | number | 经度 |
| latitude | number | 纬度 |
| grade | string | 台风等级(如热带风暴) |
| pressure | number | 中心气压(hPa) |
| wind_speed | number | 最大风速(m/s) |
| wind_dir | string | 移动方向 |
| type | string | 类型:analysis(实况)或 forecast(预报) |
points数组每个元素结构与current类似,但不含wind_dir(方向只在当前点提供)。注意points按时间顺序排列,包含实况点和官方预报点。
错误响应:code非 0,msg描述错误原因。
常见错误与处理
| 错误场景 | 错误码 | 处理建议 |
|---|---|---|
| API Key 无效或缺失 | 401 | 检查密钥 |
| 请求参数格式错误 | 400 | 校验 JSON 合法性 |
| 台风 ID 不存在 | 404 | 确保从 list 获取的 ID 有效 |
| 频率超限 | 429 | 降低请求频率,增加退避 |
从 curl 到工程封装
生产环境中不能每次都手写 curl,需要将调用逻辑封装成可复用的代码。下面以 Python 为例,展示如何从零搭建一个稳健的调用客户端。
基础封装:一个请求函数
import requests import json from typing import Optional, Dict, Any BASE_URL = "https://v1.apizero.cn/api/typhoon" class TyphoonAPI: def __init__(self, api_key: str): self.api_key = api_key self.headers = { "X-API-Key": api_key, "Content-Type": "application/json" } def _request(self, payload: dict) -> Dict[str, Any]: """发起 POST 请求,返回解析后的字典""" resp = requests.post(BASE_URL, headers=self.headers, json=payload) resp.raise_for_status() # 非2xx抛出异常 return resp.json()业务方法封装
def get_active_list(self, status: str = "active") -> list: """获取活跃台风列表""" payload = {"action": "list", "status": status} data = self._request(payload) if data.get("code") != 0: raise Exception(f"API错误: {data.get('msg')}") return data.get("data", []) def get_detail(self, typhoon_id: str) -> dict: """获取单台风详细路径""" payload = {"action": "detail", "id": typhoon_id} data = self._request(payload) if data.get("code") != 0: raise Exception(f"API错误: {data.get('msg')}") return data.get("data", {}) def get_images(self, limit: int = 10) -> list: """获取卫星云图列表""" payload = {"action": "images", "limit": limit} data = self._request(payload) if data.get("code") != 0: raise Exception(f"API错误: {data.get('msg')}") return data.get("data", []) def get_all(self) -> dict: """综合数据:活跃台风+最新云图""" payload = {"action": "all"} data = self._request(payload) if data.get("code") != 0: raise Exception(f"API错误: {data.get('msg')}") return data.get("data", {})错误重试与退避
网络波动或临时限流时,需要自动重试。使用tenacity库或手动实现指数退避:
import time from functools import wraps def retry(max_retries=3, backoff=2): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): last_exc = None for attempt in range(max_retries): try: return func(*args, **kwargs) except (requests.exceptions.RequestException, Exception) as e: last_exc = e if attempt < max_retries - 1: wait = backoff ** attempt time.sleep(wait) raise last_exc return wrapper return decorator # 在 _request 上应用重试 class TyphoonAPI: @retry(max_retries=3, backoff=2) def _request(self, payload): resp = requests.post(BASE_URL, headers=self.headers, json=payload, timeout=10) resp.raise_for_status() return resp.json()速率限制保护
为避免触发 429,可以添加令牌桶或简单延迟:
class TyphoonAPI: def __init__(self, api_key, qps_limit=8): self.api_key = api_key self.headers = {...} self.min_interval = 1.0 / qps_limit self._last_call = 0.0 def _throttle(self): now = time.time() elapsed = now - self._last_call if elapsed < self.min_interval: time.sleep(self.min_interval - elapsed) self._last_call = time.time() def _request(self, payload): self._throttle() ... # 后续请求代码使用示例
api = TyphoonAPI(api_key="your_api_key_here") # 获取活跃台风列表 active = api.get_active_list() for typhoon in active: print(f"{typhoon['name_cn']} ({typhoon['tc_num']})") # 获取第一个台风的详细路径 if active: first_id = active[0]['id'] detail = api.get_detail(first_id) print(f"当前气压: {detail['current']['pressure']} hPa")异步封装(可选)
对于高并发场景,可使用aiohttp实现异步调用:
import aiohttp import asyncio class AsyncTyphoonAPI: def __init__(self, api_key): self.api_key = api_key self.headers = { "X-API-Key": api_key, "Content-Type": "application/json" } async def _request(self, payload): async with aiohttp.ClientSession(headers=self.headers) as session: async with session.post(BASE_URL, json=payload) as resp: resp.raise_for_status() return await resp.json() async def get_detail(self, typhoon_id): payload = {"action": "detail", "id": typhoon_id} return await self._request(payload)工程化注意事项
- API Key 管理:不要硬编码,使用环境变量或配置中心,如
os.getenv("TYPHOON_API_KEY")。 - 日志与监控:记录每次请求的 URL、参数、耗时和状态码,便于排查问题。
- 数据缓存:台风路径变化不频繁(通常每小时更新),可缓存结果(如 Redis)并设置 TTL=1h,减少 API 调用次数。
- 版本兼容:接口可能升级,保留请求/响应字段的扩展性,避免因新增字段导致解析报错。
- 时区处理:返回时间字段
time_cst为北京时间(UTC+8),进行跨时区显示时需转换。 - 边界条件:当
point_count为 0 时(如刚生成 ID 尚无路径),需处理空列表。
参考文档
- API 官方文档
- 原始 Markdown 文档
