python爬取台风路径
# 导入所需模块
import requests
import json
import re
import time
import pandas as pd
import datetime
# 设置请求头,模仿浏览器行为
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
# 定义函数来显示某一年的所有台风编号和名称
def show_tc_nums_and_names_by_year(year):
# 构建URL以获取JSON数据
url = f"http://typhoon.nmc.cn/weatherservice/typhoon/jsons/list_{year}?callback=typhoon_jsons_list_{year}"
# 发送GET请求并获取响应文本
html_obj = requests.get(url, headers=headers).text
# 处理字符串以提取有用的JSON部分
json_obj = html_obj[html_obj.index("(") + 1:html_obj.rindex(")")]
# 将JSON字符串转换为字典
json_dict = json.loads(json_obj)
# 解析JSON数据中的台风编号和名称
typhoon_list = json_dict.get("typhoonList", [])
if typhoon_list:
print(f"Year {year} has the following typhoons:") # 打印年份和台风信息提示
for typhoon in typhoon_list:
# 格式化台风编号
num = f"TC{typhoon[3]}"
# 获取或生成中文名称
name_cn = typhoon[2] if typhoon[2] != "null" else ""
# 获取英文名称
name_en = typhoon[1]
# 打印台风编号及名称
print(f"{num} ({name_en}, {name_cn})")
else:
print(f"No typhoon information found for year {year}") # 如果没有找到台风信息,则打印提示
# 定义函数预测给定日期加上小时增量后的日期
def date_pred(date, deltahour):
# 解析日期字符串为datetime对象
time = datetime.datetime.strptime(date, "%Y%m%d%H%M")
# 计算新日期
new_date = (time + datetime.timedelta(hours=deltahour)).strftime("%Y%m%d%H%M")
# 返回新日期字符串
return new_date
# 定义函数根据类型字符串返回台风强度描述
def get_type(date_type):
# 定义类型映射
item = {'TC': '热带气旋', 'TD': '热带低压', 'TS': '热带风暴', 'STS': '强热带风暴',
'TY': '台风', 'STY': '强台风', 'SuperTY': '超强台风', '': '',}
# 返回对应描述或默认值
return item.get(date_type, '')
# 定义函数获取特定台风的详细信息
def get_tc_info(item):
# 创建13位时间戳
t = int(round(time.time() * 1000))
# 构建URL并发送GET请求
url = 'http://typhoon.nmc.cn/weatherservice/typhoon/jsons/view_%s?t=%s&callback=typhoon_jsons_view_%s' % (item['id'], t, item['id'])
html_obj = requests.get(url, headers=headers, verify=False).text
# 提取JSON数据
data = json.loads(re.match(".*?({.*}).*", html_obj, re.S).group(1))['typhoon']
# 创建字典存储台风信息
info_dicts = {
'tc_num':item['tc_num'], # 台风编号
'name_cn':item['name_cn'], # 中文名称
'name_en':item['name_en'], # 英文名称
'dateUTC':[], # UTC日期
'dateCST':[], # CST日期
'vmax':[], # 最大风速 m/s
'grade':[], # 强度等级
'lat':[], # 纬度
'lon':[], # 经度
'mslp':[], # 中心气压 hPa
'attr':[] # 属性:预报或分析
}
# 遍历分析数据
for v in data[8]:
info_dicts['dateUTC'].append(v[1])
info_dicts['dateCST'].append(date_pred(v[1], 8)) # 将UTC时间转换为CST时间
info_dicts['vmax'].append(v[7])
info_dicts['grade'].append(get_type(v[3]))
info_dicts['lon'].append(v[4])
info_dicts['lat'].append(v[5])
info_dicts['mslp'].append(v[6])
info_dicts['attr'].append('analysis')
# 获取最新预报时间
dateUTC0 = info_dicts['dateUTC'][-1]
# 处理最新预报
if len(data[8][-1]) > 11 and data[8][-1][11] is not None and 'BABJ' in data[8][-1][11]:
BABJ_list = data[8][-1][11]['BABJ']
for i in range(len(BABJ_list)):
# 获取预报前置时间(小时)
pred_hour = int(BABJ_list[i][0])
# 预测UTC时间
dateUTC_pred = date_pred(dateUTC0, pred_hour)
info_dicts['dateUTC'].append(dateUTC_pred)
info_dicts['dateCST'].append(date_pred(dateUTC_pred, 8))
info_dicts['vmax'].append(BABJ_list[i][5])
info_dicts['grade'].append(get_type(BABJ_list[i][7]))
info_dicts['lon'].append(BABJ_list[i][2])
info_dicts['lat'].append(BABJ_list[i][3])
info_dicts['mslp'].append(BABJ_list[i][4])
info_dicts['attr'].append('forecast')
# 将字典转换为DataFrame
tc_info = pd.DataFrame(info_dicts)
# 返回DataFrame
return tc_info
# 定义函数根据年份和编号获取台风信息
def get_tc_info_by_year_and_num(year, num):
# 构建URL并发送GET请求
url = f"http://typhoon.nmc.cn/weatherservice/typhoon/jsons/list_{year}?callback=typhoon_jsons_list_{year}"
html_obj = requests.get(url, headers=headers, verify=False).text
# 打印响应文本(调试用途)
print(html_obj)
# 提取JSON数据
data = json.loads(re.match(".*?({.*}).*", html_obj, re.S).group(1))
# 检查是否存在台风列表
if data.get('typhoonList'):
for item in data['typhoonList']:
# 如果找到了匹配的台风编号
if item[4] == num:
# 创建包含台风ID、编号、中文名和英文名的字典
tc = {
'id': item[0],
'tc_num': str(item[4]),
'name_cn': item[2],
'name_en': item[1]
}
# 返回台风详细信息
return get_tc_info(tc)
# 如果没有找到匹配的台风,则返回None
return None
# 主程序入口
if __name__ == "__main__":
# 设置请求头(重复,应避免)
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
# 输入年份
year = input("请输入年份: ")
# 显示该年份的台风编号和名称
show_tc_nums_and_names_by_year(year)
# 输入台风TC编号(四位数字)
num = input("请输入台风TC编号(TC后面四位数字): ")
# 获取台风信息
data = get_tc_info_by_year_and_num(year, num)
# 如果数据存在,则打印并保存到CSV和Excel文件
if data is not None:
print(data)
data.to_csv(rf'{year}_TC{num}.csv', index=False)
print(f"台风{num}的数据已保存到文件{year}_TC{num}.csv")
data.to_excel(rf'{year}_TY{num}.xlsx', index=False, engine='openpyxl')
print(f"台风{num}的数据已保存到文件{year}_TC{num}.xlsx")
# 如果未找到数据,则打印提示
else:
print(f"在{year}年未找到台风{num}的数据。")
绘制路径图
import os
import pandas as pd
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像时负号'-'显示为方块的问题
# 设定默认字体大小为12
plt.rcParams['font.size'] = 12
import cartopy.crs as ccrs
import cartopy.feature as cfeat
from matplotlib.lines import Line2D
from matplotlib.collections import LineCollection
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
# 读取数据
df = pd.read_csv('D:/data/2024_TY2403.csv')
# 假设正确的列名是 'grade' 而不是 'grade_at_point',并且它表示每个点的强度级别
# 如果不是,请替换为正确的列名
# 定义颜色函数
def get_color(grade):
if grade == '热带低压' or grade == '热带扰动':
return '#FFFF00'
elif grade == '热带风暴':
return '#6495ED'
elif grade == '强热带风暴':
return '#3CB371'
elif grade == '台风':
return '#FFA500'
elif grade == '强台风':
return '#FF00FF'
elif grade == '超强台风':
return '#DC143C'
# 创建地图并绘制台风路径
def plot_typhoons(df, extent):
gdf = gpd.read_file('D:/data/中国_省.shp')
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
# 添加底图
ax.add_feature(cfeat.COASTLINE)
# 添加中国省界
gdf.boundary.plot(ax=ax, transform=ccrs.PlateCarree(), linewidth=0.5, edgecolor='black')
# 设置地图范围
ax.set_extent(extent, crs=ccrs.PlateCarree())
# 添加网格线
gl = ax.gridlines(draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
gl.xlabels_top = gl.ylabels_right = False
# 设置经纬度格式和刻度
ax.xaxis.set_major_formatter(LongitudeFormatter())
ax.yaxis.set_major_formatter(LatitudeFormatter())
# 绘制台风路径
for tc_num, group in df.groupby('tc_num'):
points = np.array(list(zip(group['lon'], group['lat'])))
if 'grade' in group.columns: # 确保 'grade' 列存在
colors = [get_color(grade) for grade in group['grade']] # 使用 'grade' 列
else:
# 如果没有 'grade' 列,可以使用默认颜色或其他逻辑
colors = ['#000000'] * len(points) # 例如,使用黑色
segments = np.array([points[i:i+2] for i in range(len(points)-1)])
lc = LineCollection(segments, colors=colors, linewidths=2)
ax.add_collection(lc)
# 添加图例(这里需要手动指定,因为使用了循环绘制)
legend_elements = [
Line2D([], [], color='#FFFF00', marker='o', label='热带低压/扰动', markersize=5),
Line2D([], [], color='#6495ED', marker='o', label='热带风暴', markersize=5),
Line2D([], [], color='#3CB371', marker='o', label='强热带风暴', markersize=5),
Line2D([], [], color='#FFA500', marker='o', label='台风', markersize=5),
Line2D([], [], color='#FF00FF', marker='o', label='强台风', markersize=5),
Line2D([], [], color='#DC143C', marker='o', label='超强台风', markersize=5),
]
ax.legend(handles=legend_elements, loc='upper left')
# 设置标题
plt.title('2403台风格美路径')
# 显示图形
plt.show()
# 调用函数,替换 extent 为所需的经纬度范围
plot_typhoons(df, extent=[100, 150, 0, 40]) # 根据需要设置extent