最近在做一个航空数据分析的小项目需要处理大量航班起降数据其中就涉及到从香港国际机场HKG起飞的航班轨迹分析。在数据清洗和可视化过程中我发现网上关于如何系统性地获取、解析和展示这类特定机场航班数据的完整教程比较零散。本文将围绕“香港国际机场起飞的飞机”这一数据主题手把手带你搭建一个从数据获取到动态可视化的全流程分析系统。无论你是对航空数据感兴趣的数据爱好者还是需要处理类似地理空间数据的开发者都能从本文中找到可直接复用的代码和清晰的实现思路。1. 背景与核心概念在开始动手之前我们首先要明确几个核心概念和本项目的目标。香港国际机场 (HKG)作为全球最繁忙的货运机场和重要的客运枢纽其航班数据具有高密度、高复杂性的特点。分析从HKG起飞的航班可以洞察航线网络、航班频次、机型分布等有价值的信息。航班数据来源通常实时的航班数据需要通过专业的API接口获取例如ADS-B (Automatic Dependent Surveillance–Broadcast)数据。这是一种飞机通过卫星导航确定其位置并定期广播的协议许多开源项目和商业公司都提供相关的数据流或历史数据查询服务。对于学习和原型开发我们可以使用一些免费的或提供免费层级的API。本项目的目标我们将构建一个简单的数据管道实现以下功能数据获取从公开的航空数据API中筛选出指定时间段内从香港国际机场HKG起飞的航班数据。数据解析与清洗处理返回的JSON格式数据提取关键字段如航班号、机型、起降机场、经纬度轨迹等。数据存储将清洗后的数据存储到本地文件如CSV或轻量级数据库中便于后续分析。数据可视化利用Python的matplotlib、plotly或folium库在地图上动态绘制出这些航班的飞行轨迹。技术栈预览本项目将主要使用Python语言涉及requests库调用APIpandas库处理数据以及folium库进行地图可视化。整个流程清晰代码可复用性强。2. 环境准备与版本说明在开始编码前请确保你的开发环境已就绪。以下是本文示例所使用的核心环境不同版本间API和语法可能略有差异请根据实际情况调整。操作系统Windows 10/11, macOS 或 Linux (Ubuntu 22.04) 均可。编程语言Python 3.8 或以上版本。建议使用3.9以获得更好的兼容性。开发工具任意你熟悉的代码编辑器或IDE如 VS Code, PyCharm, Jupyter Notebook。Python包管理使用pip进行安装。创建项目目录并初始化环境建议创建一个独立的项目文件夹并在其中管理依赖。# 创建项目文件夹 mkdir hkg_flight_analysis cd hkg_flight_analysis # 创建虚拟环境 (推荐) python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 安装必要的Python库 pip install requests pandas folium matplotlib关键库版本说明requests (2.28)用于发送HTTP请求到航空数据API。pandas (1.5)用于数据清洗、分析和存储。folium (0.14)用于生成交互式Leaflet地图非常适合绘制地理轨迹。matplotlib (3.7)用于生成静态的轨迹图或统计图表。关于API密钥本文示例将使用一个提供免费层级的航空数据API——OpenSky Network。你需要在其官网免费注册一个账户以获取API凭证。请注意免费接口通常有调用频率限制如每分钟/每天次数限制仅供学习和测试使用。3. 核心组件与原理拆解3.1 OpenSky Network API 简介OpenSky Network 是一个由社区驱动的开源项目提供全球范围的实时和历史ADS-B航班数据。其REST API允许我们查询特定时间、特定机场的航班信息。核心接口我们主要使用其flights/all接口的变体来获取在指定时间区间内所有可观测的航班。然后我们需要在结果中筛选出estDepartureAirport为 “HKG” 的记录。API限制认证部分接口需要认证免费注册即可。速率限制未认证用户限制较严认证用户权限更高。务必遵守其使用条款避免高频请求。数据延迟实时数据通常有1-2分钟的延迟历史数据需要特定权限。3.2 航班数据结构从API获取的航班数据通常是JSON格式一个航班对象可能包含以下关键字段icao24: 飞机的唯一ICAO 24位地址十六进制。callsign: 航班呼号如 “CPA123”可能被清理过。estDepartureAirport: 预估的起飞机场ICAO代码如 “HKG”。estArrivalAirport: 预估的降落机场ICAO代码。firstSeen,lastSeen: 航班首次和末次被观测到的Unix时间戳。estDepartureAirportHorizDistance,estArrivalAirportHorizDistance: 与预估机场的水平距离。注意estDepartureAirport和estArrivalAirport是系统的估算并非100%准确的飞行计划但对于宏观分析足够有效。3.3 可视化方案选择静态图 (matplotlibbasemap/cartopy): 适合生成报告用图但交互性差。basemap已停止维护推荐使用cartopy但安装稍复杂。交互式网页地图 (folium)基于Leaflet.js可生成HTML文件直接在浏览器中交互缩放、拖拽、点击查看信息。本项目将采用此方案因其简单直观且结果易于分享。高级交互可视化 (plotly)功能强大可创建复杂的仪表盘但学习曲线稍陡。4. 完整实战案例获取与可视化HKG起飞航班接下来我们分步骤实现整个流程。4.1 获取OpenSky API凭证并编写配置首先去 OpenSky Network 官网注册账户并在账户设置中找到你的“用户名”和“密码”这将被用作HTTP Basic Auth的凭证。在项目根目录创建一个config.py文件用于安全地存储你的凭证切勿将此文件提交到公共代码仓库。# config.py OPENSKY_USERNAME “your_username_here” # 替换为你的用户名 OPENSKY_PASSWORD “your_password_here” # 替换为你的密码4.2 编写数据获取模块创建一个data_fetcher.py文件。# data_fetcher.py import requests from datetime import datetime, timedelta import time import config # 导入配置文件 def fetch_flights_from_hkg(start_time, end_time): 从OpenSky API获取指定时间范围内从HKG起飞的航班。 参数: start_time (int): 开始时间的Unix时间戳。 end_time (int): 结束时间的Unix时间戳。 返回: list: 航班字典列表如果出错返回None。 # OpenSky的flights/all接口需要认证 url “https://opensky-network.org/api/flights/all” params { ‘begin’: start_time, ‘end’: end_time, # 注意此接口本身不支持直接按机场过滤我们需要获取所有航班后本地筛选 } # 使用HTTP Basic认证 auth (config.OPENSKY_USERNAME, config.OPENSKY_PASSWORD) try: print(f“正在请求数据时间范围: {datetime.fromtimestamp(start_time)} 至 {datetime.fromtimestamp(end_time)}“) response requests.get(url, paramsparams, authauth, timeout30) response.raise_for_status() # 如果状态码不是200抛出HTTPError all_flights response.json() # 筛选出预估起飞机场为HKG的航班 hkg_departures [flight for flight in all_flights if flight.get(‘estDepartureAirport’) ‘HKG’] print(f“共获取到 {len(all_flights)} 个航班其中从HKG起飞的有 {len(hkg_departures)} 个。”) return hkg_departures except requests.exceptions.RequestException as e: print(f“请求API时发生错误: {e}“) return None except ValueError as e: print(f“解析JSON响应时发生错误: {e}“) return None def get_unix_timestamp(hours_ago1): 获取当前时间和N小时前的时间戳。 用于快速测试。 end_dt datetime.utcnow() start_dt end_dt - timedelta(hourshours_ago) # OpenSky API 需要秒级时间戳 return int(start_dt.timestamp()), int(end_dt.timestamp()) if __name__ “__main__”: # 示例获取过去1小时的数据 start, end get_unix_timestamp(hours_ago1) flights fetch_flights_from_hkg(start, end) if flights: # 打印前几个航班信息 for i, f in enumerate(flights[:3]): print(f“航班 {i1}: {f.get(‘callsign’)} 从 HKG 飞往 {f.get(‘estArrivalAirport’)}“)关键点说明flights/all接口返回的是指定时间段内所有被观测到的航班数据量可能很大。我们通过flight.get(‘estDepartureAirport’) ‘HKG’在本地进行筛选。免费API有调用限制请勿在循环中高频调用。获取历史数据可能需要更复杂的接口或权限。时间戳必须是整数秒。datetime.utcnow()使用UTC时间与API时区一致。4.3 数据清洗与存储创建一个data_processor.py文件用于处理原始数据并保存。# data_processor.py import pandas as pd from datetime import datetime import json def process_and_save_flights(flights_data, output_file‘hkg_departures.csv’): 处理航班数据并保存为CSV文件。 参数: flights_data (list): fetch_flights_from_hkg 返回的原始数据列表。 output_file (str): 输出CSV文件名。 if not flights_data: print(“无有效数据可处理。”) return None # 将数据转换为Pandas DataFrame df pd.DataFrame(flights_data) # 选择我们感兴趣的列并重命名为更易读的名称 columns_of_interest { ‘icao24’: ‘Aircraft_ICAO24’, ‘callsign’: ‘Callsign’, ‘estDepartureAirport’: ‘Departure_Airport’, ‘estArrivalAirport’: ‘Arrival_Airport’, ‘firstSeen’: ‘First_Seen_Unix’, ‘lastSeen’: ‘Last_Seen_Unix’, } df df[list(columns_of_interest.keys())].rename(columnscolumns_of_interest) # 将Unix时间戳转换为可读的日期时间格式 df[‘First_Seen’] pd.to_datetime(df[‘First_Seen_Unix’], unit‘s’) df[‘Last_Seen’] pd.to_datetime(df[‘Last_Seen_Unix’], unit‘s’) # 计算航班大致持续时间秒 df[‘Duration_Seconds’] df[‘Last_Seen_Unix’] - df[‘First_Seen_Unix’] # 按起飞时间排序 df df.sort_values(by‘First_Seen’) # 重置索引 df df.reset_index(dropTrue) # 保存到CSV df.to_csv(output_file, indexFalse, encoding‘utf-8-sig’) print(f“数据已保存至 {output_file}共 {len(df)} 条记录。”) # 也保存一份JSON副本供其他用途 json_file output_file.replace(‘.csv’, ‘.json’) df.to_json(json_file, orient‘records’, indent2, date_format‘iso’) print(f“JSON 数据已保存至 {json_file}。”) return df def load_flights_from_csv(csv_file‘hkg_departures.csv’): 从CSV文件加载已处理的航班数据。 try: df pd.read_csv(csv_file, parse_dates[‘First_Seen’, ‘Last_Seen’]) print(f“从 {csv_file} 成功加载 {len(df)} 条记录。”) return df except FileNotFoundError: print(f“文件 {csv_file} 未找到。”) return None if __name__ “__main__”: # 此部分仅为模块测试实际运行由主程序调用 # 假设我们已经有了数据 # from data_fetcher import fetch_flights_from_hkg, get_unix_timestamp # start, end get_unix_timestamp(hours_ago2) # raw_data fetch_flights_from_hkg(start, end) # df process_and_save_flights(raw_data) pass4.4 航班轨迹可视化这是最有趣的部分。我们将使用folium绘制地图。但请注意OpenSky的flights/all接口不包含详细的经纬度轨迹点。要获取轨迹需要调用另一个接口tracks它需要飞机的ICAO24地址和特定时间。为了简化示例我们假设我们已经通过其他方式或后续查询获得了部分航班的轨迹数据或者我们仅用起降机场的位置来绘制连线。创建一个visualizer.py文件。# visualizer.py import folium from folium.features import DivIcon import pandas as pd import random # 一个简单的机场ICAO代码到坐标的映射字典 (示例实际需要更全的数据) AIRPORT_COORDS { ‘HKG’: (22.3080, 113.9185), # 香港国际机场 ‘PVG’: (31.1434, 121.8052), # 上海浦东 ‘PEK’: (40.0799, 116.6031), # 北京首都 ‘NRT’: (35.7647, 140.3863), # 东京成田 ‘SIN’: (1.3644, 103.9915), # 新加坡樟宜 ‘BKK’: (13.6811, 100.7475), # 曼谷素万那普 ‘LAX’: (33.9416, -118.4085), # 洛杉矶 ‘JFK’: (40.6413, -73.7781), # 纽约肯尼迪 ‘DXB’: (25.2532, 55.3657), # 迪拜 ‘LHR’: (51.4700, -0.4543), # 伦敦希思罗 } def create_flight_map(flights_df, output_html‘hkg_flights_map.html’): 根据航班DataFrame创建交互式地图。 由于缺乏详细轨迹用起降机场连线代替。 if flights_df is None or flights_df.empty: print(“没有航班数据用于生成地图。”) return # 1. 创建以HKG为中心的地图 hkg_coords AIRPORT_COORDS.get(‘HKG’, (22.3, 113.9)) flight_map folium.Map(locationhkg_coords, zoom_start5, tiles‘OpenStreetMap’) # 2. 标记香港国际机场 folium.Marker( hkg_coords, popup‘b香港国际机场 (HKG)/b’, tooltip‘出发地’, iconfolium.Icon(color‘red’, icon‘plane’, prefix‘fa’) ).add_to(flight_map) # 3. 为每条航班绘制从HKG到目的地的连线 # 为了清晰我们可能只绘制前N条或者按目的地去重后绘制 # 这里我们按目的地分组每个目的地只画一条线并用数字标注航班数量 destination_counts flights_df[‘Arrival_Airport’].value_counts() for dest_airport, count in destination_counts.items(): dest_coords AIRPORT_COORDS.get(dest_airport) if dest_coords: # 绘制航线 folium.PolyLine( locations[hkg_coords, dest_coords], color‘blue’, weight1.5 min(count / 5, 3), # 线条粗细根据航班数量调整 opacity0.7, popupf“bHKG → {dest_airport}/bbr航班数: {count}“, tooltipf“飞往 {dest_airport} ({count} 班次)” ).add_to(flight_map) # 在目的地机场标记点旁添加航班数量标签 folium.map.Marker( dest_coords, iconDivIcon( icon_size(150,36), icon_anchor(0,0), htmlf‘div style“font-size: 10pt; color: black; background-color: white; border-radius: 3px; padding: 2px;”{dest_airport} ({count})/div‘, ) ).add_to(flight_map) else: print(f“警告未找到机场 {dest_airport} 的坐标已跳过。”) # 4. 添加图例或标题 (folium本身没有内置图例可以添加HTML控件) title_html ‘’’ div style“position: fixed; top: 10px; left: 50px; width: 300px; height: 80px; background-color: white; border:2px solid grey; z-index:9999; font-size:14px; padding: 10px; border-radius: 5px;” b香港国际机场 (HKG) 起飞航班示意/bbr 红线香港国际机场br 蓝线飞行航线 (粗细代表航班频次)br 标签目的地机场 (航班数) /div ‘’’ flight_map.get_root().html.add_child(folium.Element(title_html)) # 5. 保存地图为HTML文件 flight_map.save(output_html) print(f“交互式地图已生成: {output_html}“) print(f“请用浏览器打开此文件查看。共绘制了 {len(destination_counts)} 个不同的目的地。”) if __name__ “__main__”: # 测试加载之前保存的数据并生成地图 from data_processor import load_flights_from_csv df load_flights_from_csv(‘hkg_departures.csv’) if df is not None: create_flight_map(df)4.5 整合主程序并运行最后创建一个main.py文件作为程序入口串联整个流程。# main.py from data_fetcher import fetch_flights_from_hkg, get_unix_timestamp from data_processor import process_and_save_flights from visualizer import create_flight_map import time def main(): print(“ 香港国际机场起飞航班分析系统 “) # 步骤1获取数据 (例如获取过去2小时的数据) print(“\n[步骤1] 正在从OpenSky Network获取航班数据...”) start_ts, end_ts get_unix_timestamp(hours_ago2) # 可调整时间范围 raw_flights fetch_flights_from_hkg(start_ts, end_ts) if not raw_flights: print(“数据获取失败程序退出。”) return # 步骤2处理并保存数据 print(“\n[步骤2] 正在处理并保存航班数据...”) processed_df process_and_save_flights(raw_flights) if processed_df is None: print(“数据处理失败程序退出。”) return # 简单打印一些统计信息 print(f“\n数据概览:”) print(f“ 时间范围: {processed_df[‘First_Seen’].min()} 至 {processed_df[‘First_Seen’].max()}“) print(f“ 航班总数: {len(processed_df)}“) print(f“ 目的地机场数: {processed_df[‘Arrival_Airport’].nunique()}“) print(f“ 最繁忙的目的地: {processed_df[‘Arrival_Airport’].value_counts().head(3).to_dict()}“) # 步骤3生成可视化地图 print(“\n[步骤3] 正在生成航班航线可视化地图...”) create_flight_map(processed_df, output_html‘hkg_departures_map.html’) print(“\n 分析完成 ) print(“请查看生成的 ‘hkg_departures.csv’ 数据和 ‘hkg_departures_map.html’ 地图文件。”) if __name__ “__main__”: main()4.6 运行与结果在项目根目录下运行主程序python main.py如果一切顺利你将在控制台看到数据获取、处理和保存的日志最后提示分析完成。当前目录下会生成hkg_departures.csv结构化的航班数据表格。hkg_departures.json相同数据的JSON格式。hkg_departures_map.html交互式地图文件。用浏览器打开hkg_departures_map.html你将看到一张以香港为中心的地图红色标记是HKG蓝色线条是飞往各个目的地的航线线条粗细和目的地标签上的数字代表了该航线的航班数量。5. 常见问题与排查思路在运行本项目时你可能会遇到以下问题问题现象可能原因解决思路运行python main.py时报ModuleNotFoundError未安装所需库或未在正确的虚拟环境中运行。1. 确认已激活虚拟环境 (venv\Scripts\activate或source venv/bin/activate)。2. 运行pip install -r requirements.txt或重新执行pip install requests pandas folium。API请求返回401 Unauthorized错误config.py中的用户名或密码错误或未设置。1. 检查config.py文件中的OPENSKY_USERNAME和OPENSKY_PASSWORD是否已正确替换为你的OpenSky账户凭证。2. 确认凭证无误可以尝试在命令行用curl或浏览器测试认证。API请求返回429 Too Many Requests或长时间无响应触发了OpenSky API的速率限制。免费账户有严格的调用限制。1.最重要立即停止频繁请求。等待一段时间如几分钟到一小时再试。2. 在代码中增加延时例如在循环调用API时使用time.sleep(60)。3. 考虑减少请求的时间范围如从过去6小时改为过去1小时。获取到的航班数据为空 (hkg_departures列表为空)1. 指定的时间段内确实没有从HKG起飞的航班被OpenSky网络观测到。2. 时间戳格式错误或时间范围太短。3. API服务临时故障。1. 检查start_time和end_time是否正确。使用datetime.fromtimestamp()打印出来看看。2. 尝试扩大时间范围如过去4小时。3. 访问OpenSky网站或状态页查看服务是否正常。生成的地图没有航线只有HKG一个点1.AIRPORT_COORDS字典中缺少目的地机场的坐标。2. 航班数据中的Arrival_Airport字段为空或不是有效的ICAO代码。1. 打印destination_counts查看有哪些目的地机场。2. 将缺失的机场坐标添加到AIRPORT_COORDS字典中。可以使用公开的机场数据库或手动查询补充。3. 检查原始数据确认estArrivalAirport字段有值。地图能显示但线条全部重叠看不清目的地机场过多且很多机场地理位置接近如中国大陆多个机场。1. 在visualizer.py的create_flight_map函数中可以过滤掉航班数量较少的目的地例如只绘制前10个最繁忙的目的地。2. 考虑使用其他可视化形式如饼图显示目的地分布或柱状图显示航班数量。错误KeyError: ‘estDepartureAirport’API返回的某些航班数据字典中可能没有estDepartureAirport键。在筛选时使用flight.get(‘estDepartureAirport’)而不是flight[‘estDepartureAirport’]。get方法在键不存在时返回None可以避免报错。本文代码已使用.get()方法。6. 最佳实践与工程建议将一个小脚本提升为一个更健壮、可维护的数据分析项目可以考虑以下实践配置管理永远不要将API密钥、密码等敏感信息硬编码在代码中或提交到版本控制系统如Git。使用config.py并从环境变量读取是基础做法。更安全的方式是使用python-dotenv库从.env文件加载。# .env 文件 OPENSKY_USERNAMEyour_username OPENSKY_PASSWORDyour_password# 在代码中读取 from dotenv import load_dotenv import os load_dotenv() username os.getenv(‘OPENSKY_USERNAME’)错误处理与日志目前的try…except是基础。生产环境中应使用更完善的日志系统如logging模块记录信息、警告和错误便于排查。对于可能失败的API请求考虑加入重试机制如tenacity库并设置指数退避策略。数据持久化对于持续收集的数据CSV文件可能变得笨重。考虑使用SQLite轻量级或PostgreSQL、MySQL等数据库。设计合理的表结构例如将航班信息、机场信息分开存储并建立关联。性能优化如果分析长时间段的数据API可能需要分多次请求。设计一个循环每次请求一个较短的时间窗口如1小时并妥善处理时间边界。将数据处理清洗、转换和可视化分离。可以先将清洗后的数据存入数据库可视化时再从数据库读取避免每次重新处理原始数据。可视化增强真实轨迹要绘制真实轨迹需要调用OpenSky的/tracks接口需要认证传入飞机的icao24地址和时间戳。这会产生大量额外的API调用务必谨慎并遵守限制。动态地图使用folium的TimestampedGeoJson可以制作时间轴动画展示航班随时间的变化但这需要完整的轨迹点数据。仪表板对于复杂分析可以考虑使用plotly Dash或Streamlit构建一个包含图表、表格和地图的完整Web仪表板。遵守数据使用政策严格遵守OpenSky Network等数据提供方的使用条款。免费数据通常仅限非商业、研究或个人使用。在公开分享你的分析结果尤其是可视化地图时最好注明数据来源。扩展方向多机场分析修改代码支持分析任意机场的起降航班。机型分析OpenSky的/metadata/aircraft/icao24/接口可以根据icao24查询机型信息丰富你的数据分析维度。历史数据分析研究OpenSky的历史数据接口分析特定日期或事件的航班模式变化。通过这个项目你不仅学会了如何处理航空数据更掌握了一套从API获取、清洗、存储到可视化的通用数据管道搭建方法。这套方法可以迁移到分析其他类型的时空数据上例如船舶AIS数据、气象站数据、物联网传感器网络数据等。