从这篇文章,读者可以了解道机场气象报文的背景,知道其中数据大概长什么样子,如何利用python语言解析出机场气象报文里面的信息形成格式化数据。
背景
上个月的我国第六号台风“烟花”光临时,虹桥国际机场实行停飞管制,从本质上来说,民航业还是一个靠天吃饭的行业,气象条件对民航至关重要,国家也投入了大量人力和物力在机场气象数据的采集,存储,处理,发放和使用等全流程来保障民航领域健康发展,机场气象有其特殊性和历史渊源,目前我国空管单位均以气象报文的形式发送机场气象报文。
机场气象报文又称气象通告,是机场气象台对机场地面及上部空间天气进行定时观测的资料以报文的形式进行报告和发布的,一般有例行观测报(METAR),特殊观测报文(SPECI)和天气预报(TAF)三种,本文主要讲解例行观测报(METAR)。
依照 MH/T 4016.1-2004 标准中所作的规定,例行观测应每小时观测一次,也可每半小时观测一次, 观测时次份有以下三种
1,24小时观测:每日16:00(UTC时间)至次日16:00每半小时或一小时观测一次
2,13小时观测:每日00:00(UTC)至12:00每半小时或一小时观测一次
3,不定时观测:在有飞行活动期间,每半小时或一小时观测一次
气象报文资料里面一般包含下面几个关键数据字段
- 报文种类标识MEART
- 机场四字码,如ZSSS
- 观测时间,如101500Z
- 自动报告标识,AUTO
- 地面风向和风速,如05004MPS指代风向050,风速04mps 此项含有特殊标识VRB,代表风向不定
- 主导能见度
- 跑道视程
- 现在天气情况
- 云或垂直能见度
- 气温和露点温度
- 修正海平面气压
- 趋势
- 补充情报
示例
下面是一个最常见的报文主体内容
METAR ZGGG 010800Z 05004MPS 280V100 7000 -TSRA FEW033CB 31/24 Q1013 TEMPO 2000 TSRA SCT026CB BKN030
解析出来各字段的含义:
-
定时气象观测报文(METAR)
-
广州白云机场(ZGGG)
-
观测时间UTC时间1日8:00,北京时间1日16:00(010800Z)
-
风向050度风速4m/s(05004MPS)
-
风向范围280°~100°(280V100)
-
能见度7000米(7000)
-
小雷雨天气(-TSRA)
-
少云,云底高度3300英尺云层类型积雨云(FEW033CB)
-
温度31摄氏度(31/24)
-
露点24摄氏度(31/24)
-
修正海平面气压1013(Q1013)
-
气象变化趋势短时(TEMPO)
-
趋势天气为:
-
短时能见度2000米(2000)
-
短时雷雨天气(TSRA)
-
短时有2600英尺较少积雨云(SCT026CB)
-
3000英尺多云(BKN030).
思路
从上面示例看到报文主体大致包括两部分内容,一部分是跟气象采集站有关的基础信息,一部分是观测气象信息,针对采集站的信息比较固定可以直接撸下来,针对气象内容信息有时候有些字段会空缺,可以采取try except来避免误解析,可以利用全文索引和正则匹配把气象主体内容进行切割把解析出来的字段放到对应的字段下面即可,依循这个思路,下面给出了解析python代码。
代码
def get_weather(airport4, start_time, end_time): #根据机场四字码和时间段获取气象数据
url = "http://www.baidu.com/flightService/getAirportMetarContent/"+airport4+"/"+start_time+"/"+end_time
print(url)
logging.info (url)
response = requests.get(url=url, headers = header, timeout =30)
response.encoding = "GBK"
result = json.loads(response.text)[0]
#print(result)
status = result["resultcode"]
if status=='0': #如果请求成功,有数据返回
weather_content = result["resultdata"][-1] #筛选最后一个
content = weather_content["content"] #气象主体
#print(content)
try:
airport4 = re.findall (r' [A-Z]{4}', content)[0].replace (' ', '') # 机场四字码
except:
airport4 = ''
try:
ob_time = re.findall (r'\d+Z', content)[0].replace ("Z", "") # 观测时间
except:
ob_time = ''
try:
if 'MPS' in content:
wind = re.findall (r'V{0,1}R{0,1}B{0,1}\d+[A-G]{0,1}\d+MPS', content)[0] # 风
wind_speed = re.findall (r'\d{2}MPS', wind)[0].replace ('MPS', '') # 风速
wind_towards = wind[0:3] # 风向
else:
wind = re.findall (r'V{0,1}R{0,1}B{0,1}\d+[A-G]{0,1}\d+KT', content)[0] # 风
wind_speed = re.findall (r'\d{2}KT', wind)[0].replace ('KT', '') # 风速
wind_towards = wind[0:3] # 风向
except:
wind = ''
wind_speed = ''
wind_towards = ''
try:
visib = re.findall (r'CAVOK| \d{4} ', content)[0].replace (' ', '') # 能见度
except:
visib = ''
try:
rvr = ' '.join (s1 for s1 in re.findall (r'R\d{2}.?\/.+? ', content)) # 跑道能见度
except:
rvr = ''
try:
nowweather = re.findall(r'-?\+?[A-Z]{6}|-?\+?[A-Z]{2,4}', content)[0].replace(' ', '') #现在天气
if nowweather == 'META':
nowweather = ''
else:
nowweather = nowweather
except:
nowweather = ''
try:
content1 = re.findall (r'METAR.*?Q\d{4}', content)[0]
cloud = ' '.join (s1 for s1 in re.findall (r'VV\d{3}|[A-Z]{3}\d{3}[A-Z]{2,3}|NSC|NCD|SKC|[A-Z]{3}\d{3}', content1)) # 云层
except:
cloud = ''
#气温
try:
temper = re.findall (r'M{0,1}\d./M{0,1}\d{2}.', content)[-1].split ("/")[0].replace (' ', '')
if temper[0] == 'M':
if temper[0:3] == 'M00':
temper = '0'
elif temper[0:2] == 'M0':
temper = temper[0] + temper[-1]
temper = temper.replace ('M','-')
else:
temper = temper.replace ('M', '-')
elif temper[0] == '0':
temper = temper[1]
except:
temper = ''
#露点
try:
dew = re.findall (r'M{0,1}\d./M{0,1}\d{2}.', content)[-1].split ("/")[1].replace (' ', '')
if dew[0] == 'M':
if dew == 'M00':
dew = '0'
if dew[0:2] == 'M0':
dew = dew[0] + dew[-1]
dew = dew.replace ('M', '-') # 露点
else:
dew = dew.replace ('M', '-')
elif dew[0] == '0':
dew = dew[1]
except:
dew = ''
try:
qnh = re.search (r'Q\d{4}', content).group ().replace ("Q", "") # 修正海压
except:
qnh = ''
try:
#print (re.findall (r'RERA.*?=|BECMG.*?=|TEMPO.*?=|NOSIG=', content))
if re.findall(r'RERA.*?=|BECMG.*?=|TEMPO.*?=|NOSIG=', content) != []: #剔除没有未来趋势的情况
trend = re.findall(r'RERA.*?=|BECMG.*?=|TEMPO.*?=|NOSIG=', content)[0].replace('=', '') #未来趋势
else:
trend = ''
except:
trend = ''
weather = [ob_time, wind_speed, wind_towards, visib, rvr, nowweather, cloud, temper, dew, qnh, trend, content]
# print(weather)
else: #如果请求成功,无数据返回
print('error:请求报错,resultcode的值不为0')
logging.info ('error:请求报错,resultcode的值不为0')
weather = []
return weather
结果预览
致谢
感谢汤晨杰先生将代码完善。
参考文献
2,民航气象报文讲解
3,机场气象报文解析
4,国际气象报文讲解