+从零实现一款12306刷票软件1.1

郑重申明一下:这里介绍的技术仅供用于学习,不可用于恶意攻击12306服务器,请勿滥用本文介绍的技术。对12306服务器造成的任何损失,后果自负。

当然,由于12306服务器用户量巨大,为了防止黄牛和其他一些非法攻击者,12306的很多url和在购票过程中各个步骤的协议细节经常发生变化。所以,本文中介绍的一些具体的url,可能在你看到本文时已经失效。但是这并没有关系,只要你掌握了本文中介绍的分析方法,您就可以灵活地修改您的代码,以适应最新的12306服务器的要求。

举个例子,如12306的查票接口目前的url是:

https://kyfw.12306.cn/otn/leftTicket/query

可能过几天就变成了

https://kyfw.12306.cn/otn/leftTicket/queryX

再过几天又可能变成

https://kyfw.12306.cn/otn/leftTicket/queryY

然后一个星期后又可能变成

https://kyfw.12306.cn/otn/leftTicket/queryZ

这些笔者都见过。所以,重在原理的学习,掌握了原理,不管12306的相关url变成什么样,都可以以不变应万变。哎,12306在与黄牛的斗争中越走越远啊T_T

本文将使用以下工具来分析12306购票的过程,然后使用C++语言,模拟相关的过程,最终购票。

  1. Chrome浏览器(其他的浏览器也可以,都有类似的界面,如Chrome,装了httpwatch的IE浏览器等)
  2. 一个可以登录12306网址并且可以购票的12306账号
  3. Visual Studio(版本随意,我这里用的是VS 2013)

一、查票与站点信息接口

之所以先分析这个接口,是因为查票不需要用户登录的,相对来说最简单。我们在Chrome浏览器中打开12306余票查询页面,网址是:https://kyfw.12306.cn/otn/leftTicket/init,如下图所示:

然后在页面中右键菜单中选择【检查】菜单,打开后,选择【网络】选项卡。如下图所示:

打开后页面变成二分窗口了,左侧是正常的网页页面,右侧是浏览器自带的控制台,当我们在左侧页面中进行操作后,右侧会显示我们浏览器发送的各种http请求和应答。我们这里随便查一个票吧,如查2018年5月20日从上海到北京的票,点击12306网页中【查询】按钮后,我们发现右侧是这样的:

通过图中列表的type值是xhr,我们可以得出这是一个ajax请求(ajax是一种浏览器原生支持的异步技术,具体细节请读者自行搜索)。我们选择这个请求,你能看到这个请求的细节——请求和响应结果:

在reponse中,我们可以看到我们的这个http的去除http头的响应结果(包体,可能是解压缩或者解码后的):

这是一个json格式,我们找个json格式化工具,把这个json格式化后贴出来给大家看一下,其实您后面会发现12306的http请求结果中与购票相关的数据基本上都是json格式。这里的json如下:

{  
    "validateMessagesShowId": "_validatorMessage",  
    "status": true,  
    "httpstatus": 200,  
    "data": {  
        "result": ["null|23:00-06:00系统维护时间|5l0000G10270|G102|AOH|VNP|AOH|VNP|06:26|12:29|06:03|IS_TIME_NOT_BUY|RLVVIt093U2EZuy2NE+VQyRloXyqTzFp6YyNk6J52QcHEA01|20180520|3|HZ|01|11|1|0|||||||||||1|有|13||O090M0|O9M|0",(内容太长,这里省略) "],  
        "flag": "1",  
        "map": {  
            "AOH": "上海虹桥",  
            "BJP": "北京",  
            "VNP": "北京南",  
            "SHH": "上海"  
        }  
    },  
    "messages": [],  
    "validateMessages": {}  
}

其中含有的余票信息在result节点中,这是一个数组。每个节点以|分割,我们可以格式化后显示在自己的界面上:

我这里做的界面比较简陋,读者如果有兴趣可以做更精美的界面。我们列下这个请求发送的http数据包和应答包:

请求包:

 1GET /otn/leftTicket/query?leftTicketDTO.train_date=2018-05-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=BJP&purpose_codes=ADULT HTTP/1.1  
 2Host: kyfw.12306.cn  
 3Connection: keep-alive  
 4Cache-Control: no-cache  
 5Accept: */*  
 6X-Requested-With: XMLHttpRequest  
 7If-Modified-Since: 0  
 8User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36  
 9Referer: https://kyfw.12306.cn/otn/leftTicket/init  
10Accept-Encoding: gzip, deflate, br  
11Accept-Language: zh-CN,zh;q=0.9,en;q=0.8  
12Cookie: RAIL_EXPIRATION=1526978933395; RAIL_DEVICEID=WKxIYg-q1zjIPVu7VjulZ9PqEGvW2gUB9LvoM1Vx8fa7l3SUwnO_BVSatbTq506c6VYNOaxAiRaUcGFTMjCz9cPayEIc9vJ0pHaXdSqDlujJP8YrIoXbpAAs60l99z8bEtnHgAJzxLzKiv2nka5nmLY_BMNur8b8; _jc_save_fromStation=%u4E0A%u6D77%2CSHH; _jc_save_toStation=%u5317%u4EAC%2CBJP; _jc_save_fromDate=2018-05-20; _jc_save_toDate=2018-05-19; _jc_save_wfdc_flag=dc 

应答包:

 1HTTP/1.1 200 OK
 2Date: Sat, 19 May 2018 15:23:58 GMT
 3Content-Type: application/json;charset=UTF-8
 4Transfer-Encoding: chunked
 5ct: C1_217_85_8
 6Content-Encoding: gzip
 7Age: 1
 8X-Via: 1.1 houdianxin183:6 (Cdn Cache Server V2.0)
 9Connection: keep-alive
10X-Dscp-Value: 0
11X-Cdn-Src-Port: 33963
12Cache-Control: no-cache, no-store

通过上一篇文章《从零实现一个http服务器》我们知道这是一个http GET请求,其中在url后面是请求附带的参数:

leftTicketDTO.train_date: 2018-05-20  
leftTicketDTO.from_station: SHH  
leftTicketDTO.to_station: BJP  
purpose_codes: ADULT  

这四个参数分别是购票日期出发站到达站票类型(这里是成人票(即普通票)),正好对应我们界面上的查询信息:

但是,读者可能会问,这里的出发站和到达站分别是SHH和BJP,这些站点代码,我如何获得呢?因为只有知道这些站点编码我才能自己购买指定出发站和到达站的火车票啊。如果您是一位细心的人,您肯定会想到,我们查票的时候再进入查票页面,这些站点信息就已经有了,那么可能是在这个查票页面加载时,从服务器请求的站点信息,所以我们刷新下查票页面,发现果然是这样:

进入查票页面之前,浏览器从https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9053下载一个叫station.name.js文件,这是一个javascript脚本,里面只有一行代码,就是定义了一个station_names的js变量,之所以url地址后面加一个station_version=1.9053,你可以理解成版本号,但是主要是通过一个随机值1.9053,让浏览器不要使用缓存中的station_name.js,而是每次都从服务器重新加载下这个文件,这样的话如果站点信息有更新,也可以避免因为缓存问题,导致本地的缓存与服务器上的站点信息不一致。由于站点信息比较多,我们截个图把:

看上图,我们可以看出来,每个站点信息都是通过@符号分割,然后通过|分割每一个站点的各种信息。这样的话,根据上文的格式假如我们要查询2018年5月30日从长春到南京的火车普通票,就可以通过网址:

https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2018-05-30&leftTicketDTO.from_station=CCT&leftTicketDTO.to_station=NJH&purpose_codes=ADULT

原文发布于微信公众号 - 高性能服务器开发(easyserverdev)

原文发表时间:2018-05-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏软件测试经验与教训

看图说话:持久式XSS(跨站)漏洞示例

读书与实践是获取知识的主要渠道,学习的权力只掌握在每个人自己手中,让学习成为一种生活的习惯,这比任何名牌大学的校徽重要得多!

2142
来自专栏后端技术探索

Web 开发人员需知的 Web 缓存知识

今天踩着前辈们的肩膀,再次把这篇文章翻译整理下。一来让自己对web缓存的理解更深刻些,二来让大家注意力稍稍转移下,不要整天HTML5, 面试题啊叨啊叨的~~

1132
来自专栏知识分享

7-开发板接入小五物联实现远程控制(Air202模块+单片机)

https://www.cnblogs.com/yangfengwu/p/9337033.html

1082
来自专栏cs

wechat作弊

菜鸟一个最多跳60多分,看到 微信小游戏跳一跳外挂教程(安卓版)的教程,自己尝试了一下,ubuntu17环境,python2,python3公存在,开始失败了,...

3649
来自专栏非著名程序员

【小技巧】AS 手动实现无线真机调试

其实无线真机调试很简单,走一遍流程就能学会并且记住,无需插件,无需 Root,使用插件反而会将这个流程复杂化。先放上纯流程版,方便通篇阅读之后的快速查阅,接下来...

2447
来自专栏Jerry的SAP技术分享

在SAP云平台的CloudFoundry环境下消费ABAP On-Premise OData服务

我的前一篇文章 使用Java+SAP云平台+SAP Cloud Connector调用ABAP On-Premise系统里的函数介绍了在SAP云平台的Neo环境...

1916
来自专栏IMWeb前端团队

再见2015 再见cmd

本文作者:IMWeb yisbug 原文出处:IMWeb社区 未经同意,禁止转载 2015年已经快要过去了,你是否还在使用有着十几年历史的cmd命令行...

2539
来自专栏云计算教程系列

CentOS 如何配置NTP加入NTP池项目

准确的计时对于几乎所有服务或软件都至关重要。在分布式平台上运行的电子邮件,记录器,事件系统和调度程序,用户身份验证机制和服务都需要准确的时间戳来按时间顺序记录事...

1610
来自专栏腾讯Bugly的专栏

H5 和移动端 WebView 缓存机制解析与实战

作者:叶建升 个人主页:http://www.linkedin.com/in/jiansheng-ye-b3319778/ 导语 web缓存是web开发逃不开的...

4874
来自专栏Java3y

纳税服务系统总结

纳税服务系统总结 纳税服务系统是我第一个做得比较大的项目(不同于javaWeb小项目),该项目系统来源于传智Java32期,十天的视频课程(想要视频的同学关注我...

2989

扫码关注云+社区

领取腾讯云代金券