12306抢票V1.0

申明:本文仅供学习交流,若读者对12306服务器造成任何损失,后果自负!

  随着国泉来京务工3月有余,计划火车出行回家欢度一下国庆佳节,可在买票上犯了难,感叹这大北京还真是去哪都不容易啊,本着学习的目的,研究了一下12306抢票,以下为国泉实现的抢票过程。

一、改造计划

1、车次和乘车人改为选择式,从服务请求下来信息后在界面打钩选择,这样相对更方便。

2、实现脱机操作,优化的主要目的!开发一个BS版本,将S端部署至云服务器,浏览器只需要选择乘车人和车次进行下单,服务器日夜兼程的进行刷票,成功后发送短信告知。

 

二、 设计思路

1、http抓包分析协议,基于win32窗口及控件,gdi+绘制验证码图片,c++11线程及锁,curl做http客户端,jsoncpp解析服务返回的json,ini文件保存服务url方便修改。

2、双线程,刷票为一个线程,点击刷票后,此线程一直去查票,每循环一次,给主线程发消息刷新界面(若想提高刷票效率,可考虑多线程多路访问12306不同CDN节点)。

3、用户先登录,一直保持登录状态在去刷票,有余票立即抢单,而非有余票后登录在下单,刷票中若检测到被踢下线,显示登录界面,如此循环。

4、验证码每登录成功一次,将图片及选择结果缓存下来,这也会是国泉以后做BS版本实现自动登录的关键,需要大数据支撑。

三、效果预览

四、细节分析

蓝色为项目里用到的协议,下面一个一个分析:

1、https://kyfw.12306.cn/otn/login/init

初始化,目的是获取服务器给的cookie信息,以后的操作都需要带着这些标识,获取方法网上都是解析返回结果从httpHeader解析出来,然后加到请求头里,一开始国泉也跟着这样做,但是后来发现不用这么费劲,因为牛鼻curl支持cookie操作,见图二。

2、https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.2729611590628027

获取图片验证码,url最后的一串数字需要客户端每次随机生成一个16位的浮点数,国泉的做法是double d = rand() * 100.0000 / RAND_MAX

成功返回的body内容即是一张jpg图片,我们只需要将图片绘制出来,当鼠标点击的时候,我们需要提取到鼠标相对于图片左上角的偏移坐标,就是选择结果,同时可以绘制一个小图片。

3、https://kyfw.12306.cn/passport/captcha/captcha-check

图片验证,将鼠标选择的xy坐标用逗号间隔开,拼成以上的字符串传入body进行验证,在测试过程中国泉共出现过以下4种结果,第一种就是上一步说的cookie问题,这个困惑了国泉好一阵!

 

4、https://kyfw.12306.cn/passport/web/login

账户密码验证,格式是固定的,传入账户密码,返回带uamtk表示成功,需要将他解析出来并保存

5、https://kyfw.12306.cn/passport/web/auth/uamtk

uamtk验证,分析协议内容发现需要将上一步返回的uamtk追加到cookie里,国泉的做法是在这一步curl操作额外增加

curl_easy_setopt(curl, CURLOPT_COOKIE, "uamtk=4t8HHxPat82ghbQunJn6tkH0DDxoAZtpNP3qoA36l2l0");

若成功会携带newapptk,同样解析出来并保存。

6、https://kyfw.12306.cn/otn/uamauthclient

newapptk验证,将上一步的newapptk内容按格式作为body发送过去,成功后返回apptk,同样解析并保存下来。

7、https://kyfw.12306.cn/otn/login/userLogin

登录,将上一步提取的apptk追加到cookie里发送过去

curl_easy_setopt(curl, CURLOPT_COOKIE, "tk=YU0JXfSejqzzmSdvvRppefWnhkKmHq5gKycl_Q64l2l0");

这一步协议的返回值为302。下面的协议都需要带着这个tk码

8、https://kyfw.12306.cn:443/otn/index/initMy12306

登录,返回值200即成功,至此经过3轮验证,终于登录成功,可喜可贺,可喜可贺。


9、https://kyfw.12306.cn/otn/leftTicket/queryO?leftTicketDTO.train_date=2018-10-18&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=CQW&purpose_codes=ADULT

余票查询,即便未登录,我们也是可以查询余票的。这里需要注意的是url里接口名queryO官方会随着版本迭代做修改,国泉在测试的时候用的queryA,写这篇文章的时候成了queryO,在调用A接口时会返回302重定向。

后面的乘车日期,出发和到达车站代码可通过服务https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9053获取,格式是车站以@符号艾特分隔,车站信息以|符号竖杠分隔,名称和代码分别在索引1和2的位置,我们需要缓存到本地文件,在程序启动时读取到内存,也可每次启动程序时去请求一次。若status为true,flag=1即解析result内容,结果是一个json数组,车次内容以|符号竖杠分隔,这里需要把内容解析并缓存下来,其中比较重要的是索引为0~15的值,后面会用到,25~后面的就是余票信息,其中比较重要的是下标为0的那个很长的一串字符secretStr,用来标记每一次查询结果,下单时必须对应起来。

10、https://kyfw.12306.cn/otn/login/checkUser

登录检查,body内容是固定的,若没有被踢下线,返回flag为true

11、https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest

申请订单,这里面的body内容比较多用&与号分隔,格式为:余票返回的secretStr、出发日、返程日、其次两个固定内容,后面是出发地名称、目的地名称、最后一个内容固定,成功status为true。

12、https://kyfw.12306.cn/otn/confirmPassenger/initDc

获取订单编号,请求内容固定,返回的body是一个html页,需要解析内容里的两个32位md5,其中两个的关键字分别是globalRepeatSubmitToken和key_check_isChange。

13、https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo

检查订单,body内容比较多展开后就是图2的内容

REPEAT_SUBMIT_TOKEN是上一步解析出的globalRepeatSubmitToken

oldPassengerStr需要单独进行urlEncode编码,格式为姓名,身份证类型,身份证,类型_ 见图3

passengerTicketStr需要单独进行urlEncode编码,格式为坐席类型,0,1,姓名,身份证类型,身份证号码,联系电话,N,多个以下划线隔开,见图3,坐席类型可参考图4,国泉在做的时候只加了常用的5种坐席。

其中的乘客信息,有两种方式,国泉是登录成功后读取的https://kyfw.12306.cn/otn/passengers/query服务获取的联系人,也可以在下单的时候选择联系人。

其他的内容都是固定的,成功status为true,ifShowPassCode为N

14、https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount

 获取排队人数,确定是否可以下单,body内容展开见图2

REPEAT_SUBMIT_TOKEN和一步一样。

seatType为坐席类型码,见上图。

train_date为格林时间,需要将本地时间转为图2的格式在进行urlEncode编码。

然后其他的数据全部来自query接口结果我们需要购买车票的那列车次数据,对应着赋值即可。

返回内容status为true标识成功,这一步国泉在判断的时候没管data里的排队信息,直接跳过去下一步下单。

  

15、https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue

下单,这里只需要将12步里解析出的key_check_isChange用上,其他信息在上面都用过了,这里就不在解释

当submitStatus=True标识成功。

恭喜你,买到票了,是不是很简单,晚上加个鸡腿!

猜你喜欢

转载自www.cnblogs.com/GuoQuanLiu/p/9767413.html