Agent应用指南:获取12306官网全量站点及其编码信息

获取 12306官网:拿到全路最完整的车站数据表

写在前面:3382 站、11 字段:把 12306 车站数据一次讲透

设想你手上有某城市的地铁刷卡数据、或某平台的城际 OD 数据,想叠加"火车站位置"做空间分析。你打开 12306 想找一份全国车站清单,结果搜出来是零散的售票页、起售时间公告------车站信息从来没有一个统一的下载入口

它分散在三个 JS 文件里,完整度天差地别。结论先放这:

  • 真正"最全"的,是购票域(kyfw)下的 station_name.js3382 个车站、11 个字段
  • 它带城市归属、带三字码,是打通 12306 余票 / 时刻 / OD 接口的"通用主键";
  • 不含经纬度,需要坐标得另走接口或第三方源。

一、12306 藏着三张"车站表",完整度天差地别

同一官网,不同域名、不同用途,车站信息被拆成了三份。用途不同,别拿错。

文件 域名 车站数 字段数 城市归属 主要用途
qss.js www 2 803 2 起售(放票)时间表
station_name.js www(官网域) 3 284 6 官网车站联想输入
station_name.js kyfw(购票域) 3 382 11 全路最全车站主数据

一句话记牢:要车站主数据,认准 kyfw.12306.cn 下的 station_name.js

二、最全的那张表,URL 在这里

直接给地址(浏览器或脚本可直连,无需登录):

复制代码
https://kyfw.12306.cn/otn/resources/js/framework/station_name.js

它定义了一个全局变量 station_names,形如:

javascript 复制代码
var station_names = '@bjb|北京北|VAP|beijingbei|bjb|0|0357|北京|.......@vap|北京北......';

技术细节:

  • 整张表以 @ 分隔每条记录 ,条内以 | 分隔字段
  • 首页 https://www.12306.cn/index/ 本身并不直接引用它,但购票域各页面加载时都会拉它做车站联想输入------所以它永远是最新的;
  • 对比 www 域版本 https://www.12306.cn/index/script/core/common/station_name.js,两者不是同一个文件(域名、路径、内容都不同)。kyfw 版多出了城市代码、城市名、境外站国别等字段,且车站数多近 100 个。

三、字段解剖:一条记录藏着 11 个字段

这是本文的重点之一。很多人拿到 station_names 只取了站名,浪费了后面 9 个字段。我们拿"北京北"这条记录拆开看:

序号 字段 含义 示例(北京北)
0 telecode 站名电报码 bjb
1 station_name 车站名称 北京北
2 spell 三字码(查票用这个 VAP
3 pinyin 拼音全拼 beijingbei
4 initial 拼音首字母 bjb
5 seq 序号 0
6 city_code 城市代码 0357
7 city_name 城市名 北京
8 国别码 仅境外站填充 lao
9 国别名 仅境外站填充 老挝
10 英文名 仅境外站填充 vientiane

几个关键点:

  • 字段 2(三字码)是金钥匙 。北京 = BJP、上海 = SHH、广州南 = GZN......余票、时刻、OD 类接口都靠它寻站,不是靠站名也不是靠电报码。
  • 字段 6/7 直接送你城市归属。后面按城市群聚合、按省统计都用得上,不用再单独做"站→市"映射。
  • 字段 8--10 只有境外站有值(中老铁路的万象、磨丁、琅勃拉邦),是国别信息。

四、动手获取:完整可运行脚本

station_name.js 的本质是一个 var station_names='...' 字符串:记录之间用 @ 分隔,字段之间用 | 分隔。下面把「下载 → 解析 → 落盘 CSV」整合成一个保存后可直接运行 的脚本 grab_stations.py

python 复制代码
import csv
import re
import requests

SOURCE_URL = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js"
SRC_FILE = "station_name_kyfw.js"
OUT_FILE = "station_name_full.csv"

# 11 个字段,顺序与 12306 源文件一致
FIELDS = [
    "telecode",      # 0 电报码(部分内部建表接口使用)
    "station_name",  # 1 站名
    "spell",         # 2 三字码(leftTicket / OD 接口寻站用)
    "pinyin",        # 3 全拼
    "initial",       # 4 拼音首字母
    "seq",           # 5 序号
    "city_code",     # 6 城市代码
    "city_name",     # 7 城市名
    "country_code",  # 8 国别代码(仅边境 / 国际站有值)
    "country_name",  # 9 国别名
    "en_name",       # 10 英文名
]


def fetch_source() -> str:
    """优先自动下载;失败则使用本地已保存的 JS 文件。"""
    headers = {
        "User-Agent": (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/120.0.0.0 Safari/537.36"
        ),
        "Referer": "https://kyfw.12306.cn/otn/leftTicket/init",
    }
    try:
        resp = requests.get(SOURCE_URL, headers=headers, timeout=20)
        resp.raise_for_status()
        print(f"已自动下载:{SOURCE_URL}({len(resp.text):,} 字符)")
        return resp.text
    except Exception as exc:
        print(f"自动下载失败:{exc}")
        print(f"尝试读取本地文件:{SRC_FILE}")
        with open(SRC_FILE, encoding="utf-8") as f:
            return f.read()


def parse(text: str) -> list:
    """从 station_name.js 文本解析出二维记录表。"""
    m = re.search(r"var\s+station_names\s*=\s*'(.*)'", text, re.S)
    if not m:
        raise ValueError("未找到 var station_names = '...'")
    body = m.group(1)
    rows = [row.split("|") for row in body.split("@") if row.strip()]
    # 字段不足 11 的脏数据按 None 补齐,保证列对齐
    for r in rows:
        if len(r) < len(FIELDS):
            r.extend([None] * (len(FIELDS) - len(r)))
    return rows


def main() -> None:
    text = fetch_source()
    rows = parse(text)
    with open(OUT_FILE, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.writer(f)
        writer.writerow(FIELDS)
        writer.writerows(rows)
    print(f"已解析 {len(rows)} 个车站、{len(FIELDS)} 个字段 → {OUT_FILE}")


if __name__ == "__main__":
    main()

运行效果:

text 复制代码
已自动下载:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js
已解析 3382 个车站、11 个字段 → station_name_full.csv

得到的 station_name_full.csv 就是全路车站主数据底座,后续所有接口查询、建表、可视化都可以拿它做字典。

六、全国枢纽锚点:用最少锚点覆盖所有省份的站点查询

拿到 3 382 站主数据后,一个现实问题是:批量拉取全国站点数据不可能逐站两两查询,而且 leftTicket 只返回直达车次 ,省内小站配到远端枢纽往往查出来是空。解决办法是圈定一组"枢纽锚点",用"枢纽 ↔ 省内站点"配对查询------只要一个省里至少有一个枢纽与该省站点有直达/经停,就能用这组枢纽把全省覆盖掉。

6.1 全国直达连通强度一张图

从"对外直达车次总量"看,上海、郑州、北京西 处于第一梯队(3 000+),武汉、西安、广州次之。覆盖完整性上,5 个枢纽可以一票直达全国 32 个有 12306 客运站的省级行政区;西安唯一缺口是海南------琼州海峡靠铁路轮渡接驳,西安没有图定直发车。

6.2 六大枢纽辐射 OD 流向图(弧线 + Top6 排行)

这一组直接复刻 OD 地理流向图的读图逻辑:每张卡片左半边是同一张中国版图,弧线从枢纽(大圆点)指向目的地省(小圆点) ,弧线越粗、节点越大,表示该方向的直达车次越多;灰色细线是省界,仅作空间参照。右半边是对应的 Top6 目的地 榜单。一眼能读出的"主场"非常清楚:

  • 北京西(华北):天津 562、河北 432 独一档,弧线向华北、东北扇形展开;
  • 上海(华东):浙江 708、江苏 702 双核,绝大多数车流都锁死在长三角;
  • 广州(华中/华南):湖南 432、湖北 288 领衔,往华中纵深辐射;
  • 武汉(华中十字):湖南 368、河南 304、广东 304 三足鼎立,是全国最"均衡"的十字路口;
  • 郑州(华北十字):河北 338、陕西 336、湖北 290 均匀铺开,南北向与东西向并重;
  • 西安(西北门户):河南 334、甘肃 258、四川 204 靠前,对西北有压倒优势,但整体能级偏低、且唯一缺口海南。

弧线的方向告诉你"车流去哪里",线粗告诉你"去得多不多",右侧榜单告诉你"具体是哪些省、多少班"------三者对照,比填色地图更像一张"铁路导航图"。

6.3 枢纽能级与全国覆盖完整性

从"对外直达车次总量"看,上海、郑州、北京西 处于第一梯队(3 000+),武汉、西安、广州次之。覆盖完整性上,5 个枢纽可以一票直达全国 32 个有 12306 客运站的省级行政区;西安唯一缺口是海南------琼州海峡靠铁路轮渡接驳,西安没有图定直发车。

6.4 OD 实测:6 大枢纽 × 6 个难点省份的直达车次

把西藏、新疆、青海、宁夏、海南、黑龙江这 6 个远端省份拉出来看:

  • 西安对西北的压倒性优势:青海 96 班、宁夏 108 班、新疆 26 班;
  • 北京西对东北的统治力:黑龙江 94 班;
  • 海南是全国最难直达的省份:除广州 10 班外,其余枢纽都只有 2--4 班,西安直接为 0。

七、这份主数据能拿来干什么

拿到 3382 站 × 11 字段的主数据,真正价值在"关联"。它是打通 12306 各接口的通用主键:

典型链路:

  1. 站名 / 三字码 / 城市归属 → 作为主键,去 JOIN 余票、时刻、OD 接口返回的数据;
  2. 城市名 / 城市代码 → 映射到 19 个城市群(京津冀、长三角、珠三角......),做城市群级聚合;
  3. 空间化 → 需要坐标时,用三字码去时刻 / 余票接口反查,或接第三方地理编码,形成OD 流向图。

文章仅用于分享个人学习成果与个人存档之用,分享知识,如有侵权,请联系作者进行删除。所有信息均基于作者的个人理解和经验,不代表任何官方立场或权威解读。

相关推荐
2401_868534781 小时前
MATLAB:车牌识别
python·django
卷无止境1 小时前
用 FastAPI 撑起大文件的上传下载:从流式处理到断点续传的完整实践
后端·python·fastapi
张龙6872 小时前
别再裸调大模型了:用 60 行 Python 给 LLM 调用加上「重试 + 超时 + 降级」
python
菜冻鱼2 小时前
Python-sklearn-评估指标
开发语言·人工智能·python·机器学习·numpy·pandas·sklearn
guyiICtestsocket2 小时前
国内支持定制的手机LPDDR芯片测试座工厂多种结构
人工智能·python·智能手机
for_ever_love__2 小时前
python基础语法学习: 变量, 输入输出, 运算符
网络·python·学习
努力搬砖的咸鱼2 小时前
AI Agent测试全景图:它到底改变了什么
人工智能·python·ai·集成测试·pytest·agent·ai编程
十三画者3 小时前
【文献分享】SIMBA:单细胞嵌入与特征共学习
人工智能·信息可视化·数据挖掘·数据分析·数据可视化