Skip to content

Repository files navigation

Rail Rhythm 中国铁路时刻表查询工具

一个简易的12306网站时刻表爬取、处理和离线查询工具

作为一名编程新手的项目,其内容正在不断充实之中,欢迎提出Issue,同时也非常期待您的Star。

这个项目的函数包装几乎不存在,我已经忍不了半年前写出来的史山了

这篇文档比较详细,快速上手建议阅读简明指南

联网导入 数据管理
按车次查询 按站点查询 按起止站查询
跳转和回溯 补充说明


1 联网导入

自动接入12306,以列车字头为单元爬取时刻表。默认读取查询当天的车次,如果要更改查询日期,请参照第2节。 命令格式为Import加车次头。纯数字用P代替。可以多个车次头连续,如Import GDC

自2025年2月21日更新之后,联网导入采用了多线程操作,请留意请求被拒绝的次数和导入之后的车次总量情况。你也可以在原有命令之后加上一个数字12指定模式,如Import A 1Import GDC 2,用降低请求速度的方式降低反爬可能性。

需要说明的是,有部分车次的时刻表无法通过时刻表查询得到,大约占全部的2-3%。这些车次大部分实际上确实会开行,但是直接从12306主页搜索框里却无法得到存储其时刻表的url,范围不定,原因未知,暂无修复思路

联网导入时,开启网络代理服务可能会导致异常的中断。

Sample input #1

Import PZTK
# 纯数字和Z、T、K字头
Import A  
# 一次导入G/D/C/K/T/Z/S/Y/纯数字

Sample output #1

Starting threads...
  0 / 531   0 success, 0 failed
 41 / 531   41 success, 0 failed
 88 / 531   88 success, 0 failed
...
527 / 531   527 success, 0 failed
531 / 531   531 success, 0 failed
------------------------------------------------------------
Train sum:	 15154 	( 10484 )
    G prefix	 5523 	( 3943 )
    D prefix	 3394 	( 2229 )
    C prefix	 3034 	( 2431 )
    Z prefix	 311 	( 201 )
    T prefix	 213 	( 150 )
    K prefix	 1941 	( 1305 )
    S prefix	 807 	( 783 )
    Y prefix	 14 	( 5 )
    Pure number	 329 	( 262 )
# 导入后共有15154条车次信息,去除车次切换和当日不开行后是10484条

这是正常导入。

下面则是被12306反爬的情况,数据总览中带有 ? 的行表示导入出现了错误(按照数量简单判断)。可能还会伴随着一些请求超时的控制台报错。遇到这种情况,说明此次爬取结果无效,建议不要保存。12306的反爬机制难以捉摸,有时仅仅爬取十几条 Y 字头车次的数据就会触发反爬,而有时连续爬取数千条 G、D、C 字头车次的数据却能顺利完成。

Sample output #2

Input instruction: import a
  0 / 531   0 success, 0 failed
 61 / 531   61 success, 0 failed
 75 / 531   75 success, 0 failed
 83 / 531   83 success, 0 failed
...
211 / 531   209 success, 2 failed: C98 G76 
248 / 531   242 success, 6 failed: T2 Z8 K12 Z7 C98 ...
267 / 531   243 success, 24 failed: C48 S89 G17 S52 D67 ...
...
------------------------------------------------------------
Train sum:	 9280 	( 6568 )
 ?  G prefix	 2460 	( 1801 )
 ?  D prefix	 2254 	( 1575 )
 ?  C prefix	 2752 	( 1834 )
 ?  Z prefix	 165 	( 99 )
 ?  T prefix	 39 	( 27 )
 ?  K prefix	 740 	( 435 )
 ?  S prefix	 691 	( 655 )
    Y prefix	 10 	( 4 )
 ?  Pure number	 169 	( 138 )

如果一直被反爬,可以考虑更改请求中的User-Agent设置。

你可以从源代码层面去修改,把headers中的User-Agent替换成自己浏览器的即可(我用的是我自己电脑上的Firefox),也可以输入命令agent,然后输入新的User-Agent

2 数据管理

2.1 保存和读取数据

从12306上导入数据很费时间,而且由于12306的反爬机制,短时间内请求次数过多可能导致得不到数据,因此建议每次运行程序时先读取,每次导入之后都保存。

保存数据的命令为Save,读取数据的命令为Load

在测试程序的时候你也可以读取我已经导入的数据。例如,仓库中train_data/no_list20250223.jsontrain_data/train_list20250223.json两个文件包括了2025年2月23日的全部车次数据(其他几个日期的数据可能并不完整)。

2.2 时间设置

本工具并不支持直接处理归属于不同日期的数据,但是可以采用下面给出的间接办法。

按照2025-01-01的格式输入一个日期,或者先输入命令TimeDate再输入日期,可以更改日期设置。

数据管理的底层逻辑是这样的:每次使用命令Load,就是把运行内存中的数据完全同步为当前时间设置指向的外部数据,而命令Save则会按当前时间设置把数据保存。

如果存在更改后日期的数据包,更改日期之后会自动读取。

2.3 数据总览

输入命令Sum,可以总览目前数据库中的车次。这个命令也会在Load之后自动调用,方便了解目前数据库中已经包含了哪些字头的车次数据,如果有需要再导入新的。

Sample input & output

Welcome to the Rail Rhythm railway timetable query tool
Current date setting: 2025-02-15
# 更改日期设置,自动读取
Input instruction: 2025-02-23
Date has been changed from 2025-02-15 to 2025-02-23
Load over
Train sum:	 15504 	( 11914 )
G prefix	 5548 	( 4280 )
D prefix	 3300 	( 2486 )
C prefix	 3034 	( 2431 )
Z prefix	 311 	( 201 )
T prefix	 213 	( 150 )
K prefix	 1941 	( 1305 )
S prefix	 807 	( 783 )
Y prefix	 14 	( 8 )
Pure number	 336 	( 270 )
# 可以看到所有类型车次数量都在正常范围,显然成功导入了
# 两列数字,分别是在重复和不重复计算车次变更下的计数

3 按车次查询

按车次查询,即输入车次,查询经过的车站及各站时刻。提供以下两种输入的格式。

  1. 输入Code,间隔一个空格,再加车次号
  2. 输入.后紧跟车次号

Sample input #1

Code C2025

Sample output #1

C2025   高速 	 北京南 - 天津
01 北京南  	 ----   18:47
02 亦庄   	 18:57  18:59   2'  
03 武清   	 19:15  19:20   5'  
04 天津   	 19:34  ---- 
# 每一行依次显示序号、站名、到点、开点、停站时间

Sample input #2

.Z33

Sample output #2

Z32 / Z33  直特 	 宁波 - 武昌
01 宁波   	 ----   21:03
02 余姚   	 21:40  21:44   4'  
03 绍兴   	 22:24  22:28   4'  
04 诸暨   	 23:21  23:24   3'  switch to Z33  
05 金华   	 00:13  00:20   7'  Day 2  
06 黄石   	 05:29  05:38   9'  switch to Z32  
07 鄂州   	 05:53  06:06  13'  
08 武昌   	 06:55  ----   
# 由于存在上下行切换,车次有两个。在最后还可以看到车次切换和日期变化

4 按站点查询

查询经停一个车站的所有车次信息,按开点排序(终到车按到点计)。同样也有两种输入的格式。

  1. Station加一个空格,后随站点中文名(不带“站”字)
  2. 直接输入站点中文名(带“站”字)

如果要对车次的方向、性质进行筛选,需要在上面的基础上,后加一个+符号,然后再输入关键字。关键字和筛选条件的对应关系如下。

  • st 始发
  • ed 终到
  • ps 中间经停
  • dn 下行,即车次尾号奇数
  • up 上行,即车次尾号偶数

如果要对车次的类型进行筛选,需要在最后加一个*,后加一个或多个字头(纯数字车次用P表示)。

Sample input #1

Station 绍兴北

Sample output #1

绍兴北 	 00:00 - 24:00 	 136 results
1    G7659   06:43   06:46    3'  杭州东 - 温州南 高速
2    G7711   06:48   06:50    2'  杭州东 - 温岭 高速
3    G1866   07:01   07:03    2'  宁波 - 郑州东 高速
...
134  D3124   21:59   22:06    7'  广州东 - 杭州东 动车
135  G4272   22:10   22:12    2'  宁海 - 新余北 高速
136  G7526   22:28   22:30    2'  温州南 - 杭州东 高速
绍兴北 	 136 results, 136 visible: ST ED PS UP DN 
# 每一行依次显示序号、车次、到点、开点、停站时间、始发终到站、车次类型

Sample input #2

拉萨站+st*Z
# 查找拉萨站始发的直特列车

Sample output #2

拉萨 	 00:00 - 24:00 	 22 results
1    Z8982   ----    09:25        拉萨 - 西宁 直特 ST 
2    Z8901   ----    10:50        拉萨 - 林芝 直特 ST 
3    Z166    ----    11:50        拉萨 - 上海 直特 ST 
4    Z266    ----    12:35        拉萨 - 广州 直特 ST 
5    Z22     ----    15:50        拉萨 - 北京西 直特 ST 
6    Z8992   ----    17:30        拉萨 - 西宁 直特 ST 
7    Z324    ----    19:00        拉萨 - 成都西 直特 ST 
拉萨 	 22 results, 7 visible: ST UP DN  
# 拉萨站共有22个车次,其中7个满足条件

5 按起止站查询

按照起止站查询,只需要把站名用-连接即可。如果起止站有多个选择,则用/分隔开。注意这里所有的输入都不应当有“站”字,也不应当有空格。

如果同一车次在选择范围内有不同的起止点搭配,那么查询结果会优先匹配顺序在前的站点。

Sample input #1

广州/广州白云-海口

Sample output #1

广州/广州白云 -> 海口   5 results
1   Z384   广州白云 	04:57	   海口   	16:30	< 11:33 >	 长春 - 海口 直特
2   K514   广州白云 	15:21	   海口   	05:50	< 14:29 >	 上海松江 - 海口 快速
3   Z504   广州白云 	18:26	   海口   	07:00	< 12:34 >	 北京西 - 三亚 直特
4   Z8006  广州   	20:38	   海口   	10:10	< 13:32 >	 深圳东 - 海口 直特
5   Z114   广州白云 	21:25	   海口   	08:50	< 11:25 >	 哈尔滨西 - 海口 直特
广州/广州白云 -> 海口   5 results, 5 visible
# 每一行依次显示序号、车次、出发站、出发时间、到达站、到达时间、用时、始发终到站、车次类型

如果要对结果进行筛选,和按站点查询类似,需要在原命令的基础上连上一个*,后加一个或多个字头(纯数字车次用P表示)。

车次是默认按照出发时间从早到晚排序的。如果要改变查询结果的排序,需要在原命令的基础上连上一个+,随后加上st edv,分别代表着按出发时间从早到晚、按到达时间从早到晚、按耗时从短到长排序。

Sample input #2

上海/上海虹桥-长春/长春西+v*GZ
# 表示查询了上海到长春的G和Z字头列车,并且按速度排序

Sample output #2

上海/上海虹桥 -> 长春/长春西   9 results
1   G1236  上海虹桥 	09:04	   长春   	19:49	< 10:45 >	 上海虹桥 - 吉林 高速
2   G1214  上海虹桥 	10:05	   长春西  	21:15	< 11:10 >	 上海虹桥 - 长春西 高速
3   G1204  上海虹桥 	09:33	   长春西  	20:45	< 11:12 >	 上海虹桥 - 哈尔滨西 高速
4   G1258  上海虹桥 	09:31	   长春西  	21:32	< 12:01 >	 上海虹桥 - 长春西 高速
5   Z516   上海   	15:32	   长春   	13:18	< 21:46 >	 上海 - 吉林 直特
6   Z172   上海   	19:34	   长春   	17:31	< 21:57 >	 上海 - 哈尔滨西 直特
上海/上海虹桥 -> 长春/长春西   9 results, 6 visible
# 可以看到,9个结果中只有6个车次满足筛选条件,其余3个K字头车次被隐藏了

如果想要按同城车站查找,可以用--连接两个城市的名称,程序将自动把城市名替换为同城车站的列表。

Sample input #3

成都--昆明+st*GD
# 查找成都到昆明的G和D字头列车,按出发时间排序

Sample output #3

成都/成都东/成都西/成都南/犀浦 -> 昆明/昆明南   22 results
1   D251   成都南  	07:06	   昆明   	15:19	< 8:13 >	 成都南 - 昆明 动车
2   G2837  成都东  	07:29	   昆明   	13:25	< 5:56 >	 成都东 - 大理 高速
3   D271   成都南  	08:21	   昆明   	15:59	< 7:38 >	 成都南 - 西双版纳 动车
...
18  G2821  成都东  	16:09	   昆明南  	22:39	< 6:30 >	 成都东 - 昆明南 高速
19  D261   成都南  	16:38	   昆明   	23:40	< 7:02 >	 成都南 - 昆明 动车
20  G2825  成都东  	17:27	   昆明南  	23:44	< 6:17 >	 成都东 - 昆明南 高速
成都/成都东/成都西/成都南/犀浦 -> 昆明/昆明南   22 results, 20 visible

你可以输入命令city_station,随后对默认的同城车站字典进行编辑,每一次的编辑永久有效。输入0表示一个城市的车站输入完毕,输入saveexit表示退出同城车站的编辑并保存编辑结果。

目前已经上传的同城车站数据涵盖全国地级以上城市。

6 跳转和回溯

6.1 索引跳转

前述查询方式的每一行中,最前面的一个数字即为索引,可以直接作为下一次的命令使用。这可以完成从起止站到按车次的跳转,以及车次和站点之间的跳转。

6.2 隐藏跳转

我们可能会好奇某一车次的时刻表中,某个区间还有哪些列车可以乘坐,隐藏跳转实现了这一功能。在索引跳转的基础上,如果用-连接两个站的索引,就相当于对这两个站点作了起止站查询。

6.3 回溯

输入<<《《可以恢复到上一个查询操作,输入>>》》可以回到恢复前的查询操作。

查询操作包括按车次、按站点、按起止站查询三种。

7 补充说明

有些用户可能会把本时刻表查询工具和其他工具的结果作对照,而结果往往不完全一致。可能有以下原因:

  • 数据不全:本工具通过12306主站爬取时刻表,一些特殊性质的铁路(例如宁波/绍兴城际、台州城际等)无法查询到。
    另外,最近还发现本工具无法爬取到
  • 数据混淆:没有遵循严格数据管理(参见第2节),导致不同日期车次重叠显示。其实提供的2025年1月22日数据已经犯了这个错误,先将就着用吧。
  • 数据未更新:如果有一边没有及时更新临时加开车次,那么就会遗漏,这尤其在春运期间常见。
  • 排序方式不同:在按站点查询车次时,本工具始终按照列车的开车时间进行排序,对于终到车次则按照到达时间排序,而其他工具可能采用不同的排序规则,例如路路通时刻表是按到达时间排序。

所有的输入都不必区分大小写。


最后,由于代码不太熟练,最初项目的框架考虑的也比较简单,变量都是全局的,所有函数也全部写在了main.py里,望不要嫌弃。


Love the rhythm of rails, love the veins of the world.

About

Rail Rhythm 中国铁路时刻表查询工具,一个简易的12306网站时刻表爬取、处理和离线查询工具

Resources

Stars

44 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages