[12306专题]第四篇12306的余票查询

1.获取指定车次的余票信息

  • 查询余票是整个自动化流程中非常重要的环节
  • 查询余票的地址是https://kyfw.12306.cn/otn/leftTicket/init
  • 为了减轻12306系统的负担,忙时查票需要先登陆帐户.
首先请求12306的查询余票的界面https://kyfw.12306.cn/otn/leftTicket/init,请求之前,先按F12,切换到network选项卡,查看请求的会话连接.
  • 其实在请求这个界面的时候,发出了一个交互https://ad.12306.cn/sdk/webservice/rest/appService/getAdAppInfo.json?placementNo=0004&clientType=2&billMaterialsId=ac3398e4e6b7417789051de0894b4516,获得的响应中,带有一组cookie.
  • 参数格式:Cookie:fp_ver=4.5.1; RAIL_EXPIRATION=1505841771820; RAIL_DEVICEID=lRJS4LUFlzx7hr4FQRpeJJpsadV-NKiN74dplfA37atNLMDEbaQ5C6J7rs1hRym9o8t_LtIR_0jv0kqT7Os6tHJkRPeq_rm5Xk9445hd7HyNMxnS-2CHmLBB9Mjid_JCYsuaYzPK-oM8CWUUNG5Ebdrg3bvyX9ez
  • 这组参数对后期提交订单,有很关键的作用,这里先铺个垫.

按照正常查询余票的方法,选择好出发地,目的地,日期后,(以9.19,长沙-南宁为例子)查询,会发出2个请求,其中包含了余票信息的请求才是我们需要的.
https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2017-09-19&leftTicketDTO.from_station=CSQ&leftTicketDTO.to_station=NNZ&purpose_codes=ADULT
  • 这个返还的响应包含了余票信息的json,以及做一部操作需要用到的cookie

在请求的报文中,有一个6个键值对的json格式的报文.

json格式如下:
  • 展开data的话,下面还带有3个键值对,分别是map,flag,result
  • map注释记录了查询的始发和到达,以及真正停靠的实际车站名称
  • 而result,则是一个以列表为值的数组,每一条记录,记载了主要的的车次信息.
{
    "validateMessagesShowId":"_validatorMessage",
    "status":true,
    "httpstatus":200,
    "data":{
        "result":Array[17],
        "flag":"1",
        "map":{
            "NFZ":"南宁东",
            "CWQ":"长沙南",
            "CSQ":"长沙",
            "NNZ":"南宁"
        }
    },
    "messages":[
    ],
    "validateMessages":{
    }
}
继续展开result,发现其实,每一个列表里面的额成员,都是以|进行分割,一共有35个,说明可以展示35个字段信息
"result":[
            "|23:00-06:00系统维护时间|38000K16270A|K1627|ZZF|NNZ|CSQ|NNZ|04:42|19:08|14:26|IS_TIME_NOT_BUY|GxZzagLz9ugMJpCswv%2FPEZaZNl%2BDiFVaouh6Nb0zNTm%2FbesL5RbzwxxrtC0%3D|20170918|3|F1|10|20|0|0||||13|||有||有|有|||||10401030|1413",
         ......此处省略很多条记录........
            "|23:00-06:00系统维护时间|62000K177901|K1779|CSQ|NNZ|CSQ|NNZ|21:04|11:38|14:34|IS_TIME_NOT_BUY|JEsmMbhy8nZeDW56jALAIE%2FHkKy7o99ZLSD9SAbbFxDdTNOaxKqg3QTO63Y%3D|20170919|3|Q7|01|10|0|0||||9|||有||有|有|||||10401030|1413"
        ],
进一步对记录进行解析,会得到如下信息:
  • 比较重要的是0对应的参数,应该是这趟车的一组钥匙.
  • 1代表可以预定,3车次,8,9,10代表出发时间,到达时间,历时,12代表一组校验参数,13则是日期
  • 座次方面,23代表软卧,25特等座,26无座,29硬座
  • 座位有三种状态:余票的数字,有,无
0 rfPu9cd9%2FCO4fTYKlDg0pHTTRVqg6QsSgCaFh3LqjsT8iktXc3FDpW71HgrlaFjD10wL%2BijItcmG%0AIKYBLbbOC6y%2FI7ULR39mb8S1p9gWnYgDsIesosW2kfKac0sEiin8jXnC3BY1Vqu0jgaQ5Nikje6s%0Azl4YtA%2FIfbyC2RWn3eEYU%2BP4BfHZWmSypXkyOOvYKV2OcJZ1yNeysVi4ZcDJTStYniRBlwpP%2BjJ8%0AjBXQgTztdr0Y
1 预订
2 5l000G150500
3 G1505 (车次)
4 NKH   
5 NFZ
6 CWQ
7 NFZ
8 14:06  (出发时间)
9 19:51  (到达时间)
10 05:45 (历时)
11 Y
12 QLDC3b1%2FMGi7CRnV4Aoh5SW%2Bu92aGXTN%2BMXppgw5dC2G1uXn
13 20170919
14 3
15 H2
16 18
17 25
18 0
19 0
20 
21 
22 
23 (软卧)
24 
25 (特等座)
26 (无座)
27 
28 
29 (硬座)
30  (二等座)
31  (一等座)
32 
33 
34 O0M090
35 OM9

2.模拟查询余票

  • 得到上述信息之后,就可以反过来,去查询需要的车票信息了
  • 回到上面的查票请求https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2017-09-19&leftTicketDTO.from_station=CSQ&leftTicketDTO.to_station=NNZ&purpose_codes=ADULT,可以看到有4组参数
    leftTicketDTO.train_date=2017-09-19 #查询日期
    leftTicketDTO.from_station=CSQ      #出发站的编码
    leftTicketDTO.to_station=NNZ        #到达站的编码
    purpose_codes=ADULT                 #乘坐的是大人还是学生
    接下来,要处理的问题,就变成了,如何构造这些参数
    • 日期,乘车人很容易实现
    • 出发站和到达站才是难点
  • 如何获取出发和到达站的对应关系呢?实际上,这些信息,在我们请求https://kyfw.12306.cn/otn/leftTicket/init这个页面的时候,已经加载进来了,没错,正是https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9025请求到的数据.

事实上,它是一个以@ 开头的文件,中间用|进行分割的长字符串.
@bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京东|BOP|beijingdong|bjd|1@bji|北京|BJP|beijing|bj|2...
#分割之后是这个样子的:
@bjb|北京北|VAP|beijingbei|bjb|0
@bjd|北京东|BOP|beijingdong|bjd|1
@bji|北京|BJP|beijing|bj|2
#包含了6个元素,其中第二个,第三个分别对应站名中文,站名编码
因为,这个字符串过长,所以处理的方法是,将它保存到一个文件station_names.py中,做成

station_names ='@bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京东|BOP|beijingdong|bjd|1@bji|北京|BJP|beijing|bj|2

赋值给变量station_names的形式,通过导入这个变量,引用它,否则显示的字符太多,会拖垮编辑器.
  • 实际的处理中,是把这个字符串变成一个city的字典{‘中文站名’:’站名编码’},通过city[‘长沙’]的方式获取对应的值CSQ,具体实现方式如下
from station_names import station_names #把那串字符保存到了文件的station_names导入进来
for i in station_names.split('@'):      #字符串以@进行分割,取出的i就是每一个(bjb|北京北|VAP|beijingbei|bjb|0)
    if not i:                           #i是空的话,跳过
        continue
    tmp_list=i.split('|')               #i不为空的话,以|进行切割,出来的tmp_list是[bjb,北京北,VAP,beijingbei,bjb,0]
    city[tmp_list[1]]=tmp_list[2]       #把列表的第2,3个元素装到字典,形成city={'北京北':'VAP','北京东':'BOP',...}这种格式
from_station=city['长沙']               #city['长沙']其实就是取字典{'长沙':'CSQ'}中长沙对应的值CSQ
to_station=city['南宁']
  • 参数就大功告成了.

3.监控需要的车次余票信息

通常我们要做的事情,不是去监控有的车票,而是去监控没有的车票,如果有票了,提醒我们去下单购买,所以需要弄清楚的事情是:
  • 需要什么车次,有没有票
  • 需要什么座次的票(一等座,卧铺,还是硬座)
  • 代码部分实现的逻辑如下
#coding:utf8
from __future__ import unicode_literals #解决DOS环境下中文乱码的问题
import urllib,urllib2,ssl,cookielib,json,damatuWeb,time
from station_names import station_names #导入文件station_names.py中的变量station_namesc=cookielib.LWPCookieJar()
cookie=urllib2.HTTPCookieProcessor(c)
opener=urllib2.build_opener(cookie)
urllib2.install_opener(opener)
ssl._create_default_https_context=ssl._create_unverified_contextcity={}                                     #构建空字典
station_names=station_names.decode('utf-8')  #字符转utf-8
for i in station_names.split('@'):      #上面讲过了
    if not i:
        continue
    tmp_list=i.split('|')
    city[tmp_list[1]]=tmp_list[2]
from_station=city[raw_input("请输入出发站名:").decode('utf-8')]   #输入出发的站名
to_station=city[raw_input("请输入到达站名:").decode('utf-8')]
train_date=raw_input("请选择日期(2017-09-20):")
def getTrain():                                                 #封装成函数
    url='https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=%s&leftTicketDTO.from_station=%s&leftTicketDTO.to_station=%s&purpose_codes=ADULT'%(train_date,from_station,to_station)
    headers={
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36',
        'Referer':'https://kyfw.12306.cn/otn/leftTicket/init',
        'cookie':'JSESSIONID=AA5E16A1DF7B051EAB5055BA2B95537D; route=c5c62a339e7744272a54643b3be5bf64; BIGipServerotn=435159562.24610.0000; BIGipServerpool_passport=317522442.50215.0000; fp_ver=4.5.1; RAIL_EXPIRATION=1505841771820; RAIL_DEVICEID=lRJS4LUFlzx7hr4FQRpeJJpsadV-NKiN74dplfA37atNLMDEbaQ5C6J7rs1hRym9o8t_LtIR_0jv0kqT7Os6tHJkRPeq_rm5Xk9445hd7HyNMxnS-2CHmLBB9Mjid_JCYsuaYzPK-oM8CWUUNG5Ebdrg3bvyX9ez; _jc_save_fromStation=%u957F%u6C99%2CCSQ; _jc_save_toStation=%u5357%u5B81%2CNNZ; _jc_save_fromDate=2017-09-19; _jc_save_toDate=2017-09-17; _jc_save_wfdc_flag=dc',
        }  #需要注意的是,查询余票需要带上cookie,否则12306会提示网络忙
    req=urllib2.Request(url,headers=headers)
    h=opener.open(req).read().decode('utf-8')
    h=json.loads(h)
    return h['data']['result']          #返回的是一个列表html=getTrain()
for i in html:                          #遍历列表,取出每一个i,对应一个车次信息,里面有35个字段
    tmp_list=i.split('|')               #将信息分割成为列表.
    if tmp_list[30]=='有' and tmp_list[30] !='':   #30对应二等座,如果有二等座,就打印出来.
        print tmp_list[13],tmp_list[3],tmp_list[8],tmp_list[9],tmp_list[10],'有票',tmp_list[0]
达到了预期的效果…

总结

理解12306的余票查询信息,是实现自动化抢票的前提.这节重点剖析了12306查询余票后返回的信息的机构,请求这些参数从哪里获取,如果模拟一个请求去查询所需要的票信息.每个记录信息对应的字段又是什么含义.熟悉掌握后,才能再下一步自动下单,做好准备工作,所以请重点学习.
赞(0)
未经允许不得转载:http://www.yueguangzu.net逗逗网 » [12306专题]第四篇12306的余票查询
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址