背景
高铁管家app里面有个小功能,叫做“跟着高铁去旅行”,在“行程”tab。我记得以前这个功能有个专门的tab,好像叫做“高铁游”。
可能这个功能比较小众吧。这个功能是什么呢?比如说我在上海,现在想找个上海坐高铁2小时以内能到的城市去玩,就可以在这里找找推荐。以前在这里可以找到1、2、3小时以内的旅游目的地,不过现在看起来做了重新的划分,提供的是2、4、6、8、10个小时的目的地。
这个功能虽然小众,但我觉得还挺不错的。尤其像江浙沪地区,旅游城市还比较密集,如果想周末或者3天小长假出去玩的话,可以到这里找找推荐。
可是,这个功能可能确实受众比较窄吧,所以收集的城市站点其实也比较少,基本都是一些热门的城市。可是实际上,也许还有其他的站点(可能是城市,也可能是小县城)边上也有好玩的地方。
于是问题来了,是否有方法,可以快速地找到某一个城市(或高铁站)出发,N小时之内可以到达的高铁站点呢?找到适合的站点之后,就可以再去对应地搜索这个城市或县城是否有值得玩的地方。
探索过程
坐飞机比较麻烦,坐火车、高铁就方便多了。所以,肯定得先看看12306是否有这样的功能,或者接口?先在12306官网上找了下,自然并没有提供。那么,就得看接口了。
在百度搜,可以找到不少关于12306接口的说明。我首先参考的是这一篇: https://blog.csdn.net/sinat_35861727/article/details/87435177。虽然这篇文章解决场景的出发点和我的不一样,但是还是可以参考的。
其中提到了3个重要的接口:
https://kyfw.12306.cn/otn/resources/js/framework/station_name.js
获取所有站点的信息
https://kyfw.12306.cn/otn/resources/js/query/train_list.js
获取排班后的车次信息
https://kyfw.12306.cn/otn/czxx/queryByTrainNo
获取车次运行的各个站点信息
先试用下这几个接口。第一个接口看起来没问题,但是第二个接口,访问之后发现返回的数据非常陈旧了,并不能获得现在最新的车次信息。也就是说,这些接口可以用,但是之后12306车次更新了,可能我也不能用。
再用这个接口的url去搜一下,果然也有不少人提到这个接口不可用了。在另一篇 https://blog.csdn.net/weixin_43316129/article/details/111193533 文章里面,有提到一个新的接口,即,在12306通过F12直接去查到的 https://kyfw.12306.cn/otn/queryTrainInfo/query?leftTicketDTO 这个接口。
我试了下,这个接口目前还在正常使用。只是稍微有点问题,即,通过api访问的时候,需要带上一些Cookie信息(直接网页访问后拿到就行)。这个Cookie信息目前不是很确定时效性以及校验策略。不过试了下,确实可用性还不错,获取的Cookie起码好几天之后还是能用的。
所以如果是查询余票信息之类的场景,大家可以直接使用这个接口来获取信息。
可是,这个接口对我需要的场景来说,还不太够。因为这个接口需要明确传入出发地和到达地(实际是用的高铁站点的电报码,可参考前面的接口1获得的内容)。那么,我想获取所有站到站信息的话,就需要通过接口1拿到的3千多个站点信息,一一尝试作为出发地和到达地才能遍历出所有结果?也就是说,需要访问接口3000*3000,即900万次。
方案可行,但又不是那么可行。
继续挖掘
上面的博客其实也提供了一种思路,就是到12306站点上,去直接看他们在用的功能,到底是怎么访问接口的。我也可以照着这个思路再去探索下。
于是发现 https://kyfw.12306.cn/otn/queryTrainInfo/init 这个页面,有个“时刻表”功能,这里面可以挖掘到2个好用的接口。一个是在搜索框输入的时候,有个自动匹配的接口,可以告诉我们到底有哪些车次。比如输入G1,就会出现G1、G10、G11等下拉联想,这其实是一个搜索接口 https://search.12306.cn/search/v1/train/search 。
当我们选择了一个车次进行查询,又能获得这个车次的所有沿途站点信息。这里实际上是用了 https://kyfw.12306.cn/otn/queryTrainInfo/query 这个接口。
最终方案
因此,最终方案逐渐清晰起来。
一、获取站点信息:
借助的接口是:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js
获得到的信息格式类似:@bjb|北京北|VAP|beijingbei|bjb|0|0357|北京||
比如站点名称、城市名称、拼音、电报码(数据中的VAP这个字段)等,都是比较有用的。
不过其中也有一些小的问题,例如有的站点名称中间有空格,我猜测这些都用不上,所以统一把空格都去掉了。
去掉空格,把所有的数据存到数据库,又发现出现了站点名称完全一样的情况。
其他都一样,就电报码不一样。我猜测可能是有历史数据?我直接在12306的首页去搜站点-站点,发现用的是其中CUW这个码,于是我就直接把WAI这个标记为无效了。
所有数据跑下来,大概有20个重复的情况。于是在代码中直接做了个“黑名单”的机制,用于标记无效记录。
二、遍历车次信息
借助的接口是 https://search.12306.cn/search/v1/train/search 。
这里就需要做一些遍历操作了。我的搜索目标是高铁、动车、城际列车,所以我就用G、D、C来作为开头,再拼上1-99这些数字,来分别查询。这里可以获得的信息形如:
{ "date": "20230511", "from_station": "深圳北", "station_train_code": "G1192", "to_station": "郑州东", "total_num": "11", "train_no": "6i000G119201" }
其中from_station和to_station是中文名格式的,为了方便以后使用,可以在前面的表中,去找到对应的站点id,用id来做关联。
三、查询某车次的沿途站点信息
借助接口:https://kyfw.12306.cn/otn/queryTrainInfo/query。
这里其实就需要用到类似“6c000G118406”这样的内部车次号信息了,这个信息在前一个接口中可以获得。
这次能拿到的信息形如:
{ "arrive_day_str": "当日到达", "arrive_time": "09:27", "station_train_code": "G1184", "station_name": "韶关", "arrive_day_diff": "0", "OT": [], "start_time": "09:29", "wz_num": "--", "station_no": "02", "running_time": "00:51" },
也就是这个车次所有沿途的站点,到达时间、出发时间、运行时长等等,都可以获得。
在处理的时候,需要注意始发站的时间记录。此外,跨天运行的信息,也需要注意。
这些车次沿途站点信息,可以记录在另一个表里,大约有几万条数据吧。
四、遍历站对站的车次信息
经过前面的步骤三,其实我们就可以继续解析站到站的信息了。
比如说,我们车次X,经过S1、S2、S3、S(n)这n个站点。那么也就意味着,从S1到S2、S3...S(n),以及从S2到S3、S4...S(n),一直从S(n-1)到S(n),都分别有一个X车次能够到达。
其中,Sa到Sb站点的车次信息可以记录如下:
Sa-Sb有一条X站点,出发时间=Sa的发车时间,到站时间=Sb的到站时间,运行时间=以上两个时间之差。
同样,需要注意处理跨天、起始站的特殊情况。
这些信息可以再用一张数据表记录下来,这就获得了几十万条站到站的车次信息。
五、其他情况
1. 在运行的过程中,会发现,每天的车次信息其实还不一致,有可能有的车次只在特定日期运行。
这个要如何处理,可以看大家实际需求。对我来说,其实哪一天都无所谓。只要我知道两个站点之间有车次就行,具体真的某一天是否有,我还会再查。
2. 数据更新
这些查询做成后台任务,可以每天执行一次,更新后一天或N天的数据。对我来说,因为日期信息对我没什么用,所以我就默认更新后一天的数据,逐步在数据库里面更新就行。
3. 我需要的最短车次信息
这个也不做详细展开了,其实基本就是个查找最小值的过程。可以现算,但最好的方式也是用一个缓存表专门记录下来。
最后的成品
地址就不放啦,放些截图:
搜索界面:
搜索结果:
到对应站点的所有车次:
其中一趟车次的具体信息:
发布于 2023-05-17 15:44:40 回复
还是放一个链接吧:)