全国地铁数据采集、处理与应用实战:从GCJ-02坐标到网络分析
1. 项目缘起:一份地铁数据能做什么?
最近在做一个城市通勤效率分析的小项目,需要用到全国主要城市的地铁站点坐标和线路信息。我本以为这是个简单的需求,上网一搜“地铁数据”,结果发现情况远比想象中复杂。要么是数据陈旧,停留在2020年甚至更早;要么是格式混乱,一个城市一个样,有的用CSV,有的用JSON,经纬度坐标系还不统一;更头疼的是,很多所谓“开源”的数据集,实际上是从某些地图服务商的API里爬取的,不仅存在法律风险,数据的完整性和准确性也存疑。折腾了好几天,项目进度卡在数据准备阶段,这让我下定决心,自己动手整理一份靠谱的、更新及时的全国地铁数据。
这份“全国各大城市地铁站数据(更新至2023-05)”就是这次折腾的成果。它不仅仅是一个简单的站点列表,而是包含了城市、线路名称、站点名称、经纬度坐标(GCJ-02坐标系)、站点序号等关键字段的结构化数据。对于数据分析师、城市规划爱好者、交通研究者,或者是想开发一款通勤类App的开发者来说,这样一份干净、统一、可追溯的数据,能省去大量数据清洗和校验的时间,让你直接进入核心的分析或开发环节。接下来,我就详细拆解这份数据的来龙去脉、处理过程中的关键决策,以及如何在实际项目中应用它。
2. 数据采集:源头、方法与合法性边界
数据质量的核心在于源头。为了保证数据的权威性和准确性,我放弃了从商业地图API或第三方聚合网站抓取的想法,而是将目光投向了各城市地铁运营公司的官方发布渠道。
2.1 官方数据源的识别与抓取策略
我主要依赖两类官方数据源:一是各城市轨道交通集团官方网站的“线路查询”或“车站信息”页面;二是政府数据开放平台,例如“北京市政务数据资源网”、“上海市公共数据开放平台”等。这些平台通常会以标准格式(如JSON、XML)提供包括站点坐标、线路走向在内的静态数据,更新相对及时,且具有官方背书。
采集工具上,我选择了Python的requests库配合BeautifulSoup进行网页解析。对于结构清晰的HTML页面,编写定向的解析脚本即可。例如,某个城市地铁官网的站点列表页,其HTML结构通常是规整的<ul>和<li>标签嵌套,很容易定位到站点名称。关键挑战在于获取经纬度坐标。很多官网并不直接显示坐标,但会嵌入一个在线地图(如高德、百度地图)来展示站点位置。
这里就用到了一个技巧:通过浏览器的开发者工具(F12),监控网络请求。当地图加载或点击站点时,浏览器会向地图服务商的接口发送请求,这个请求的URL参数或返回的JSON数据中,往往就包含了精确的经纬度坐标。我的脚本就是模拟这一过程,通过解析这些网络请求来提取坐标。必须强调的是,这个过程仅用于获取公开显示的数据,且严格控制请求频率,避免对目标服务器造成压力,这是数据采集者的基本素养。
2.2 坐标系的选择与统一:为什么是GCJ-02?
从不同渠道获取的坐标,可能基于不同的坐标系,最常见的有WGS-84(GPS标准)、GCJ-02(中国官方加密坐标系)、BD-09(百度坐标系)。如果混用,会导致地图上的点位漂移几百米甚至上千米。
我最终选择将所有坐标统一转换为GCJ-02坐标系。这是基于以下考虑:
- 应用场景适配:在国内主流的地图应用开发中,高德地图、腾讯地图的底层坐标均为GCJ-02。使用这个坐标系,数据可以无缝对接到这些平台进行可视化或路径规划。
- 数据源倾向:许多官方数据源或基于其的地图服务,输出的坐标本身就是GCJ-02。统一至此,可以减少不必要的转换次数,降低误差累积。
- 转换可行性:从WGS-84到GCJ-02的加密算法是公开的(虽然逆向解密不鼓励),有成熟的开源库(如Python的
coordtransform)可以完成批量转换。而从GCJ-02到BD-09的转换也是确定的。
注意:坐标转换会引入微小误差,但对于地铁站点定位(通常精度在10-50米级别)而言,这个误差在可接受范围内。关键在于整个数据集保持坐标系一致。
2.3 数据清洗与结构化:从原始信息到可用字段
原始抓取下来的数据是杂乱无章的文本。清洗和结构化是赋予数据价值的关键一步。我使用pandas库在Python中完成了主要工作。
核心清洗步骤包括:
- 去重与纠错:合并因不同名称(如“火车站”与“XX站”)指向同一站点的记录。对照官方线路图,人工校验站点顺序和名称的正确性,特别是对于新开通的线路和站点。
- 字段标准化:建立统一的数据结构。最终确定的字段如下:
city_name:城市中文名(如“北京市”)。line_name:地铁线路名称(如“1号线”、“大兴机场线”)。station_name:站点名称(如“西单”、“虹桥2号航站楼”)。longitude:经度(GCJ-02坐标系)。latitude:纬度(GCJ-02坐标系)。sequence:该站点在线路上的序号(从1开始计数,用于判断方向和相邻关系)。
- 异常值处理:检查经纬度坐标是否存在明显异常(如超出城市边界、落在水域中央)。对于个别无法通过脚本获取或明显错误的数据,采用交叉验证法:对比多个官方或权威地图服务上的位置,取最集中的点位,必要时进行手工修正。
这个过程耗时最长,需要极大的耐心和细心。一个城市的线路图可能要反复核对好几遍。但正是这份笨功夫,保证了最终数据集的可靠性。
3. 数据集详解:内容、边界与文件格式
截至2023年5月,这份数据集覆盖了国内所有已开通地铁运营的40余座城市,包括北京、上海、广州、深圳等一线城市,以及成都、杭州、武汉、南京等新一线城市,也涵盖了徐州、绍兴、洛阳等二三线城市的地铁系统。
3.1 数据内容与统计概览
数据集以城市为单位组织。每个城市一个独立的CSV文件,文件名即为城市名(如beijing.csv)。打开任意一个文件,你会看到类似下面的数据片段(以北京地铁部分数据为例):
| city_name | line_name | station_name | longitude | latitude | sequence |
|---|---|---|---|---|---|
| 北京市 | 1号线 | 苹果园 | 116.177528 | 39.926179 | 1 |
| 北京市 | 1号线 | 古城 | 116.184149 | 39.914574 | 2 |
| 北京市 | 1号线 | 八角游乐园 | 116.209805 | 39.907245 | 3 |
| ... | ... | ... | ... | ... | ... |
| 北京市 | 大兴机场线 | 草桥 | 116.352089 | 39.851789 | 1 |
| 北京市 | 大兴机场线 | 大兴新城 | 116.410812 | 39.685371 | 2 |
| 北京市 | 大兴机场线 | 大兴机场 | 116.410229 | 39.511517 | 3 |
数据统计:全部数据合并后,总计包含超过5000个地铁站点,涉及超过200条地铁线路。数据量足以支撑宏观的城市群分析,也能满足对单一城市的深度挖掘。
3.2 数据边界与局限性说明
没有任何数据集是完美的,明确边界能帮助你更好地使用它。
- 时间边界:“更新至2023-05”意味着数据集包含了截至2023年5月已开通运营的线路和站点。2023年5月之后新开通的线路(例如北京地铁17号线北段、上海地铁18号线二期等)不在本次数据范围内。
- 空间边界:仅包含地铁系统(含地铁、轻轨、有轨电车等城市轨道交通制式)。市域铁路、城际铁路如未纳入地铁运营体系(如上海的“金山铁路”),则未包含在内。
- 属性边界:数据集聚焦于空间位置和拓扑关系(站点、线路、顺序)。不包含以下信息:
- 站点出入口位置、换乘通道距离。
- 首末班车时间、发车间隔。
- 站点周边POI(商业、住宅)。
- 线路建设年份、造价等非空间属性。
提示:这些附加信息可以通过其他开放数据源(如POI数据、公交时刻表)与本数据集进行关联分析,从而衍生出更丰富的应用。
3.3 文件格式与使用建议
选择CSV格式是出于最大程度的兼容性。几乎任何数据分析工具(Excel, Python pandas, R, SQL数据库)都能轻松读取CSV文件。
使用建议:
- 编码:文件使用UTF-8编码保存,避免中文乱码问题。
- 读取:在Python中,使用
pandas.read_csv(‘city_name.csv’)即可加载。 - 合并:如果需要全国数据,可以遍历所有城市文件,用
pandas.concat()进行合并。 - 可视化:将
longitude和latitude字段直接作为x, y坐标,即可在地图库(如Python的folium,kepler.gl,或JavaScript的Leaflet)中绘制出地铁网络图。
4. 实战应用:从数据到洞察的四个场景
有了干净的数据,我们就可以玩出很多花样了。下面分享几个我实际尝试过的应用场景,并附上关键代码思路。
4.1 场景一:城市地铁网络拓扑结构分析
地铁网络本质是一个图(Graph),站点是节点(Node),相邻站点间的连接是边(Edge)。利用networkx这样的图分析库,我们可以计算许多有价值的指标。
核心分析指标与实现:
- 度中心性(Degree Centrality):一个站点的连接数(即经过的线路数)。换乘站的度中心性最高。这能帮你快速识别城市的核心枢纽。
import networkx as nx import pandas as pd # 假设df是某个城市的数据 G = nx.Graph() # 添加节点(站点) for station in df[‘station_name’].unique(): G.add_node(station) # 添加边(同一线路上相邻站点) for line in df[‘line_name’].unique(): line_df = df[df[‘line_name’] == line].sort_values(‘sequence’) for i in range(len(line_df)-1): G.add_edge(line_df.iloc[i][‘station_name’], line_df.iloc[i+1][‘station_name’]) # 计算度中心性 degree_centrality = nx.degree_centrality(G) # 找出度最高的前5个站点(通常是换乘站) top_hubs = sorted(degree_centrality.items(), key=lambda x: x[1], reverse=True)[:5] - 平均路径长度与聚类系数:衡量网络的“紧密”程度。平均路径长度短,说明从任意一站到另一站平均需要经过的站点数少,网络效率高。聚类系数高,说明站点之间容易形成小团体(比如某片区域站点密集连接)。对比不同城市这些指标,能看出其地铁网络规划风格的差异(是放射状、网格状还是环状混合)。
4.2 场景二:基于空间距离的站点服务范围模拟
一个站点的服务能力并非局限于其出入口,而是覆盖周边一定步行距离(如800米)的区域。我们可以用缓冲区分析来模拟这个服务范围。
使用GeoPandas进行空间分析:
import geopandas as gpd from shapely.geometry import Point import matplotlib.pyplot as plt # 将DataFrame转换为GeoDataFrame geometry = [Point(xy) for xy in zip(df[‘longitude’], df[‘latitude’])] gdf = gpd.GeoDataFrame(df, geometry=geometry, crs=‘EPSG:4326’) # WGS84 # 转换为投影坐标系(如UTM)以便进行米制距离计算 gdf_projected = gdf.to_crs(‘EPSG:32650’) # 以北京所在的UTM 50N为例 # 创建800米缓冲区 gdf_projected[‘buffer’] = gdf_projected.geometry.buffer(800) # 800米 # 可视化 fig, ax = plt.subplots(figsize=(10, 10)) gdf_projected.buffer.plot(ax=ax, alpha=0.3, color=‘blue’) # 服务范围 gdf_projected.plot(ax=ax, color=‘red’, markersize=5) # 站点通过叠加城市行政区划或人口热力图,可以进一步分析地铁服务对人口、就业的覆盖情况,识别轨道交通的“盲区”。
4.3 场景三:地铁房价值分析的简单模型
虽然房价受多重因素影响,但距地铁站的距离无疑是一个强相关因子。我们可以用这份数据快速计算房产(或小区)到最近地铁站的距离。
计算最近地铁站距离:
from scipy.spatial import cKDTree import numpy as np # 假设property_df是包含房产经纬度的DataFrame # 提取地铁站坐标数组 station_coords = np.array(list(zip(df[‘longitude’], df[‘latitude’]))) # 构建地铁站坐标的KD-Tree(快速最近邻查询) tree = cKDTree(station_coords) # 房产坐标 property_coords = np.array(list(zip(property_df[‘lng’], property_df[‘lat’]))) # 查询每个房产到最近地铁站的距离(返回距离和索引) distances, indices = tree.query(property_coords, k=1) # k=1找最近的一个 # distances即为直线距离(度),可近似转换为米(1度约111公里) property_df[‘distance_to_nearest_subway_km’] = distances * 111将这个距离字段加入房价回归模型,你会发现它通常是一个显著的负向预测因子。当然,更精细的模型还需要考虑步行路径距离、换乘便利性等。
4.4 场景四:城市间地铁网络发展对比可视化
将多个城市的数据放在同一张地图上,可以直观对比不同城市地铁网络的密度、形态和规模。
使用Folium制作交互式对比地图:
import folium # 创建底图,中心点设在中国中部 m = folium.Map(location=[35, 105], zoom_start=4) # 为不同城市定义不同颜色 city_colors = {‘北京市’: ‘red’, ‘上海市’: ‘blue’, ‘广州市’: ‘green’, ‘深圳市’: ‘purple’} for city, color in city_colors.items(): city_df = df[df[‘city_name’] == city] # 为每条线路添加PolyLine for line in city_df[‘line_name’].unique(): line_df = city_df[city_df[‘line_name’] == line].sort_values(‘sequence’) locations = line_df[[‘latitude’, ‘longitude’]].values.tolist() folium.PolyLine(locations, color=color, weight=2.5, opacity=0.7, popup=f‘{city} - {line}’).add_to(m) # 保存为HTML文件 m.save(‘subway_network_comparison.html’)生成的HTML文件可以在浏览器中打开,你可以缩放、点击查看线路信息,非常直观地看到北上广深地铁网络在形态上的差异(北京环形放射、上海密集网格、广州十字骨架等)。
5. 数据维护、扩展与协作建议
静态数据会过时,而城市地铁每天都在生长。如何让这份数据保持活力?
5.1 数据更新机制
我建立了一个半自动化的更新流程:
- 监控源:订阅各城市地铁官方公众号、新闻,关注“开通试运营”等关键词。同时定期(如每季度)扫描一次政府数据开放平台。
- 增量更新:当发现新线路开通信息后,手动执行一次针对该城市的采集脚本。脚本被设计为“增量模式”,即只抓取和合并新数据,不会覆盖已验证的旧数据。
- 版本管理:使用Git进行版本控制。每次更新提交一个新的版本标签(如
v2023-11),并在README.md中清晰记录更新日志(更新日期、新增城市、新增线路)。这样,使用者可以根据需要获取特定时间点的数据快照。
5.2 数据质量校验清单
在每次更新或使用前,建议运行一个简单的校验脚本,检查以下常见问题:
- 坐标漂移:随机抽样部分站点,将其坐标在高德/腾讯地图开放平台(提供坐标转换和展示API)上显示,肉眼核对位置是否准确。
- 拓扑错误:检查每条线路的站点
sequence是否连续,是否有重复或缺失。 - 命名一致性:检查同一站点在不同线路中名称是否一致(特别是换乘站)。
- 字段完整性:检查是否有任何一条记录的必填字段(城市、线路、站点、坐标)为空。
5.3 向社区开放与协作
我将这份数据集托管在了GitHub上,并采用了CC BY 4.0(知识共享署名)协议。这意味着任何人都可以自由地使用、分享甚至基于它进行创作,只需注明原始来源。
我期待的协作方式:
- Issue反馈:如果你在使用中发现任何错误(站点缺失、坐标不准、线路错误),欢迎在GitHub仓库提交Issue,附上官方来源链接,我会尽快核实并修复。
- Pull Request:如果你自己补充了某个城市的新线路数据,或者增加了新的字段(如车站编号、站台形式),非常鼓励你发起Pull Request。这是让数据集共同进化的最佳途径。
- 应用案例分享:如果你用这份数据做出了有趣的分析或应用,也欢迎在讨论区分享。你的用例可能会启发其他人,甚至推动数据集增加新的维度。
整理数据的过程,就像是在绘制一幅动态的城市脉络图。最初只是为了解决自己的问题,但看到这份数据能被用于学术研究、商业分析甚至公益项目时,觉得所有的繁琐工作都是值得的。数据本身是冰冷的坐标点,但当它与人的活动、城市的生长结合在一起时,就产生了温度和价值。希望这份持续更新的地铁数据,能成为你探索城市、构建应用的一块可靠基石。如果在使用中遇到任何问题,或者有好的想法,随时可以交流。