原因: 网络信号不好, 数据库异常,客户端没有及时刷新或逻辑错误。
本套教程共分3章:
本套内容主要用于分析12306购票流程,意在编写一套自动购票小程序。12306接口 api 经常变动,但是流程分析是固定的。因此,本套教程主要记录12306 相关购票流程分析过程,以作记录。
查询到余票后,点击预定(假设用户已经登录),那么就会走下单流程:
可以看到, submitOrderRequest 是 POST 请求,其请求地址为:
既然是 POST 请求,那么我们就来看下它的请求体内容:
图中对参数的解释已经很清楚了,这里唯一要注意的是:我们在查询时获取得到的 secretStr 是已经被 urlencode 后的内容,因此,当我们在发送请求时,首先要把这个 secretStr 先解码回来,这样做的原因是大部分网络请求框架对于 form data 数据都会自动进行 urlencode (比如 requests 对于 dict 数据),因此,这里需要先解码回原来的数据,后面网络请求框架编码后才能得到正确的值。
最后,来看下服务器给我们返回的内容:
这里我们只需看下 status 的内容即可,如果 status 为 true ,说明我们的请求时成功的;如果 status 为 false ,说明我们请求失败了,失败原因可以在 messages 中获取。
我们主要获取的就是 globalRepeatSubmitToken 和 ticketInfoForPassengerForm 这两个变量内容,其中 globalRepeatSubmitToken 就是一个字符串,可以很容易由正则得到它的内容;而 ticketInfoForPassengerForm 里面我们需要的内容较多,用正则获取会繁琐很多,仔细看一下,其实它就是一个 json 数据,所以我们可以先用正则获取 json 字符串内容,再将其转成 json 就可以了,但是这里需要注意一下, ticketInfoForPassengerForm 的格式不是严格符合 json 格式,因此这里获取到字符串后,需要把里面的单引号( ' )转换成双引号( " ),这样才能正确的用 json 来解析(小细节,注意一下)。
参数信息也比较简单:
参数内容见上图。
那么我们下面我们来看下服务器给我返回的数据:
可以看到服务给我呢返回的是一个 json 数据。这里同样要先看下 status 的状态,为 true ,请求成功,为 false ,请求失败,失败内容从 messages 中获取。
如果我们的请求成功了( status:true ),那么我们就可以打开 data 字段,就可以看到如下图所示内容:
可以看到,在 data 字段里面的 normal_passengers 字段就存储了我们账号中所有乘客的信息。因此,我们这里最主要的就是提取出这些乘客的信息,为后续提交订单做准备(乘客信息)。
然后,看下请求体:
这里的 cancel_flag 和 bed_level_order_num 应该是固定值,但是这两个键在 ticketInfoForPassengerForm 里面也能查找到,只是为 null ,这里应该是12306预留的一些键值,建议在程序中首先从 ticketInfoForPassengerForm 获取,当其为 null 时,才采用上面的默认值(鬼知道12306什么时候就用上这些预留的键讷!!!)
passengerTicketStr 这个参数的组合方式为: 1(seatType),0,1(车票类型:ticket_type_codes),张三(passenger_name),1(证件类型:passenger_id_type_code),320xxxxxx(passenger_id_no),151xxxx(mobile_no),N
如果有多个乘客,那么各个乘客之间用一个 _ 分隔: seatType,0,ticket_type_codes,xxxx,mobile_no,N_seatType,0,ticket_type_codes,xxxx,mobile_no,N
oldPassengerStr 这个参数的组合方式为: 张三(passenger_name),1(证件类型:passenger_id_type_code),320xxxxxx(passenger_id_no),1_
如果有多个乘客,那么直接拼接到后面就可以了: name,1,identity,1_name2,1,identity2,1_
最后看下返回结果:
各键值含义请看上图。
这里有一个点可以注意的是:我们上面看到的是成功的返回信息,笔者在测试时,抓包抓到检测失败(即 status:true , submitStatus:false )的信息里面 data 字段里面会有一个 errMsg 键值,其携带了失败的具体信息,所以在程序中如果 submitStatus 失败了,那么可以输出 errMsg 显示原因。
注: 笔者觉得这里可以大胆假设一下,形如上面格式的 json 数据(包含 status , message 用来判别请求是否成功,以及另一个键内部拥有一个标志的键值),那么,在请求成功,动作失败时,一定会伴随有一个失败信息的键值返回,即 errMsg 。
这里 Firefox 获取的参数 train_date 的格式是错误的,因此这里放上 Chrome 上截取到的最新参数信息(内容可能与上面的信息不同,但是参数获取原来一致):
最后来看下返回体内容吧:
可以看到返回结果也是一个 json ,返回结果的参数内容见上图,至于其它参数是什么意思,笔者暂时也未能分析出来!哈哈。
注: 此处,根据票的类型,向不同的网址发送确认信息:
还是按照我们上面的分析过程来:
首先,这是一个 POST 请求,请求网址为:
然后,来看下请求体:
参数详情见上图。
这里要讲一下: choose_seats ,这里的值可以为空,表示随机座位;也可以按照上面所示的输入座位号,具体的座位号应该是按下图所示方式进行排序:
最后,看下返回结果:
这里看到其返回的 json 格式跟我们上面分析 checkOrderInfo 格式是一致的,所以这里大胆猜测一下,当 submitStatus 返回 false 时,会同时返回 errMsg 字段。
那么,我们来看下其发送的参数内容:
参数详情请查看上图。
最后,来看下服务器返回的结果:
这里的 *** 作步骤是,我们需要死循环轮询订单结果,直到当 waitTime 为负数并且返回的订单号( orderId )不为 null ,那么就表明我们下单成功了。如果 waitTime 为负数,但是 orderId 为 null ,那么就需要从 data[msg] 提取出失败信息,12306官方请求轮询时间为3秒。(笔者测试发现,程序中请求时, waitTime 的值经常为-100,因此,这里用 waitTime 进行判断可能存在误区,必须用 orderId 进行判断,当 orderId 为空,并且 data[msg] 不为空,则失败,退出程序)
注 : 上述接口中的 waitTime 其实就是订单排队时间,单位:秒。
所以我们可以通过 waitTime 数值,大概估计出排队时间,既可以给出友好的提示,又可以根据这个预估时间动态调整再次查询请求,减少请求次数。
然后,看下请求体:
参数含义见上图。
最后,看下返回结果:
参数含义见上图。
以上,其实就已经算是完成了下单流程。
但是,如果还想获取订单详细信息,则请看第9步。
其参数为:
最重要的是其返回值:
所以,对于该接口返回内容,首先要判断 status 的值为 true ,表明请求成功,如果 status 为 false ,那么猜测错误原因在 messages 字段中。然后下单的车票内容在 data["orderDBList"][0]["tickets"][index] 中,这是一个数组,数组的长度就是下单的票数(就像上面下单数为2张,所以长度为2),每个数组内包含了车票的详细信息,我们可以从中取出所需信息进行展示。
至此,我们终于完成12306下单整个流程。
到这里,我们对12306的 登录流程 , 余票查询流程 和 下单流程 都已经详细的解析完毕了,读者根据我们这3篇的内容就可以编写出一套自动购票小程序了 ^-^ 。
最后,附上笔者自己写的一套自动抢票小软件: EasyTrain
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)