tnblog
首页
视频
资源
登录
有个性,不需要签名
排名
17
文章
40
粉丝
16
评论
21
Linq or Sql 取树末级节点
剑轩 : 这个方法是真的帅!
Python实例 2-12306抢票(二) 下单
18335584353 : 哥。我也是总返回url":"/leftTicket/init",&quot...
Visual Studio小技巧
剑轩 : [斜眼笑][斜眼笑]开会完了认真看了一遍
C#网络流读取(图片)
风清月 : 把一个图片放到字节数组里边[发呆](`・ω・´)
C#与Java二进制编码转换,补码
风清月 : 我勒个擦.....有时间这种问题很坑很难发现哇o(╥﹏╥)o
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

Python实例 2-12306抢票(二) 下单

11455人阅读 2019/1/20 18:51 总访问:286146 评论:3 收藏:1 手机
分类: Python

第二篇 刷票与下单


1.记住登陆

    上一篇写了登陆:http://www.tnblog.net/cz/article/details/162

    为了方便调试 不让每次登陆都去扫码一遍,做一个缓存登陆的。

    登陆就是会话保持,一般依赖于cookie,token之类的信息保存到cookie中,请求的时候带着cookie去服务器,服务器就知道是同一个人。而请求使用的request库的session会自动保存每次服务器返回的cookie信息,下次请求带上该cookie ,所以能在运行时一直保持你登陆过的状态,但是重新运行程序就不行了,所以我们可以把上次的cookie信息存到文件中,下次运行的时候把cookie读取放到session中去 就哦了!

登录前session的cookie信息

登陆成功的



把cookie信息存入文件,与读取出来:

  1. # 保存cookie
  2. def saveCookie():
  3.     _cookies = session.cookies.get_dict()
  4.     # 取到session的cookie信息 取出来是键值对把他转化成字符串类型保存下来
  5.     cookieStr = json.dumps(_cookies)
  6.     with open('./cookies.txt''w'as f:
  7.     f.write(cookieStr)
  8.     print('记录cookie成功')
  9. # 取出cookie
  10. def getCookie():
  11.     try:
  12.         with open('./cookies.txt''r'as f:
  13.         _cookie = json.load(f)
  14.         # session的cookie是一个RequestsCookieJar类型的,把键值对转换为给他
  15.         session.cookies = requests.utils.cookiejar_from_dict(_cookie)
  16.     except FileNotFoundError:
  17.         print('还未登陆过..')


保存成功之后 这样:



测试一下:

先登录

停止程序之后,直接读取cookie测试成功!!




好了开始进入刷票下单:

流程:

  1. https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-02-14&leftTicketDTO.from_station=CQW&leftTicketDTO.to_station=TVW&purpose_codes=ADULT # 查询
  2. https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest # 提交下单请求
  3. https://kyfw.12306.cn/otn/confirmPassenger/initDc # 获取一堆参数 后面的请求需要用到
  4. https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs # 获取购票人信息 (乘车人信息)
  5. https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo # 检查订单信息 (是否能购票,是否有未支付的订单)
  6. https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount # 获取余票,与排队人数
  7. https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue #选座,确定提交
  8. https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1547780170175&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=a8dc2ed1a814b3067cfc32550c17e0ed # 排队
  9. https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue # 确定下单成功回执


一、查询

  1. 地址:https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-02-14&leftTicketDTO.from_station=CQW&leftTicketDTO.to_station=TVW&purpose_codes=ADULT 
  2. 参数:{
  3.       leftTicketDTO.train_date:2019-02-14  #购票日期
  4.       leftTicketDTO.from_station:CQW #起点站
  5.       leftTicketDTO.to_station:TVW #终点站
  6.       purpose_codes:ADULT #票类型,ADULT普通票 
  7.     }
  8. 返回:{"data":{"flag":"1","map":{"CUW":"重庆北","CXW":"重庆西","TVW":"潼南"},"result":["|列车运行图调整,暂停发售|77000D51030J|D5103|CXW|ICW|CXW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W3|01|03|0|1|||||||||||||||||0|0|null","|列车运行图调整,暂停发售|77000D51170A|D5117|CUW|NIW|CUW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W2|01|03|0|1|||||||||||||||||0|0|null","IQEEzf7ymvboXyZSxb%2Bvno%2FvYNEZbmuqDINQp%2Bmnh3k4f0fECt06bHNQPKk4Hlpp8k%2FEY%2FHY1hMb%0Aua7x0jZi61wrCfaoT%2FPBfZMLd4iBVRZurosYHAz0E191H6A5Imnmwng39zx10jq6Bj5eBTTWbH4U%0Ag5a1RfO85IpYBCUAfwSajgMxulbU63F6m2rH%2Bn3QV2djAIrKzIDYwjfdFAuLAfzMa5Sb0cV3nEG7%0AnW43VHcCPF9qB%2BvHUSuHaQcbaHdYztBgPpKcWEXeSXZ0Hekt8%2BIORs8NXs%2FMiGi%2FLkbi5NdZYb%2BJ%0Ax36wYA%3D%3D|预订|710000K1420H|K143|NNZ|CDW|CXW|TVW|09:18|10:36|01:18|Y|t47JENLelAwrUH8jO7aObclgoEFHNBYIMu9USdXqyieI0PymUpkzmENrhno%3D|20190213|3|Z1|11|12|0|0||||2|||有||有|16|||||10401030|1413|0|0|null","MUyH%2FHTEfnPsZwsT%2B2JzqnO21xQ5sGp0nnfSH52Dxg82ZvrybT7nChBhBxb3TsvMjtJyUJx89uNQ%0AMCrH9ekzbik0zw54JI4FG7uavZBLFJwJFKM6jXIhI6akPir9cafU%2BO07Fq2oHvjihljCWeydEtMq%0AHltj9Cl6HqKGR9xxZS1UsSAWqyjFBxPFLPFkPjn1LwFH%2FnvW1li%2FMEDeRugFkjsdIzBNPnrhDB3I%0A%2Bi022Xdvfv8XW3YA0vrhyYKwZU%2FGAR52EgfaJAMKGomYC%2F6HFjCas%2BCl8big1Vkw%2FuIAObayl7Is%0AtwecSA%3D%3D|预订|710000K8720H|K873|ZJZ|ICW|CXW|TVW|09:24|10:52|01:28|Y|MtttfIDxq6HqNo9XM%2BstI4zCQgaSjr%2Fce9ZED1rem4EoS1%2FuzFDmxLqVGMs%3D|20190213|3|Z1|18|19|0|0||||无|||有||有|有|||||10401030|1413|0|0|null","|列车运行图调整,暂停发售|77000D514570|D5145|CUW|TVW|CUW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W2|01|03|0|1|||||||||||||||||1|0|null","|预订|4e0000D63208|D633|WHN|CDW|CUW|TVW|13:59|14:51|00:52|N|1y9bvC04rZ6kAvLar8uM3MsUNOn3MncbI4gVoC5mFGj1Ltnz|20190214|3|N3|13|15|0|0|||||||无||||无|无|||O0M0O0|OMO|0|0|null","|列车运行图调整,暂停发售|77000D513908|D5139|CUW|NUW|CUW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W2|01|03|0|1|||||||||||||||||0|0|null","|列车停运|71000D178202|D1783|NFZ|ICW|CXW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||Z1|17|18|0|1|||||||||||||||||0|0|null","|预订|6c000D180604|D1807|IZQ|ICW|CXW|TVW|16:01|16:48|00:47|N|0IsJRXfUvj4fKLm47STmvIB%2BzwWrdhrzCQqwSJd8lsG62utJ|20190214|3|Q9|13|14|0|0|||||||无||||无|||无|O0F0O0|OFO|0|0|null","43XpgRuIos5DmDfOdyu0HdaV7DDZSadtZ7uNBtK%2FIySnx0giFWZSvBddQ%2FPVAoKo%2FINIK7TD5jyE%0AGZv3ODius1%2BTL3IK6NIBVTdxysFwgmI3cKUME4TrOB2Pf1mw%2BFjSirjYyuvekN315V%2BbNGdVn62L%0Ai9%2FJxuXCZs8g0Akf1jhgE7VN6VNnxl8DGxwwz55RJ%2FGes5qp%2FvD2qa%2F%2FDf81Q4tSkXKCBlF5d2Oh%0ANjG49IAciQHLaX7gXTzzxY7HxJbmS7TBMNfxQCdtFa5%2Fm8NEAZem%2FLZqLU4%2BpDyZWyqoUO4%3D|预订|5l000D237340|D2373|NKH|ICW|CUW|TVW|16:56|17:47|00:51|Y|pp0%2Ft2%2BQaSwUfIh63Y%2FXtf%2FVMBxdG0SLx%2FK1MiAVidpk0ak%2B|20190214|3|H2|15|17|0|0|||||||无||||2|1|||O0M0O0|OMO|0|0|null","NwCfMpWhQYGrv9zKy%2FE2bJHhqOoVHXTgo5hTboBBIe%2FKzro2zxrnAiA4x1lswwh5upe5V31vZstu%0AK0eMiMwDCjgTeU4CwQtsJdOE%2FFN3yY6AN4D%2FS%2Fh1r1pC3QWwUfbXueeXWVrsXQuv7gxyz8wQjXDH%0Ah0%2Bfm2VUKochP2Te910gF9I5oWcUk4Gkih44Ey7u2hQmjW8QN%2BtYdmvCn5LZ7QW3GOZHWSzePrZJ%0ALr3Kyh2JqCZokd9MguNCNb99pGH5AV%2B97%2F3ek7cN1F872WL0HTRZsnS3wkhHwLowo4nWqmE%3D|预订|77000D514708|D5147|CUW|TVW|CUW|TVW|17:13|17:57|00:44|Y|CLy0iYHVYp2Mctmf3%2FRbBmuCCUGyeyU8JF%2Bjolnv%2FfplIrD2|20190214|3|W2|01|03|0|0|||||||有||||有|19|||O0M0O0|OMO|1|0|null","%2FCq%2FwdowS1S9VIqsBpnF2KRBh9cwXXdvrkE7Ni7Q2Yo%2F5hpvDoHAw%2FwYScidhgJuXAOY7%2B6E1Nln%0AfusLNDgv3zWGzFWW73htly%2Fo05MnO1hNMer5dhSDFMx3M2e2m4DYiSJ6wCdleKPb0%2FQc%2B46QGJbo%0A458zqWY%2F1CVFCWhCrd3w3RGHDQ2MqqzcaYkxvxSdcdpd3B1Xoy%2BBZjTNH6gEccvbvW%2BTmHZcP7S2%0ADLZg9NaARQD1cxGCc3VMnEE5kOxTDDuAL66PfFy%2B1hzHMz0kjLURhHrGie7rFzC%2Fzo64HxM%3D|预订|5l0000D35273|D353|AOH|ICW|CUW|TVW|18:11|18:56|00:45|Y|PEDp1LSroEHChj4TmdO54rOKNNaLWQ%2F3PWTrFnOf0Gmhulg9|20190214|3|H2|18|19|0|0|||||||无||||2|2|||O0M0O0|OMO|0|0|null","eZJHEjCjbrawzJcm8DtAxZjjT7uZZe%2Bw23ZW5o3WiK6sWUjGu4bAnn1AqRTGb0xoH4tUKbrjMQAz%0AyXb74lN6G3kDNrYCi66g%2BNKfci2BXK6Hd2fMEFi0PoAcBtOCXEFbXfA7x%2BSYaFxNh4P2nLtkQ46i%0AMY%2BTkporMHoy2I8Dq7MFekl%2FuPbLUlfEJlZ69NxPaYETZ3TPfW8aHKstvQ72CGjriLvdkpeK7sYj%0A9rRG5RB%2BzatWKpJYTJwC1pfiPXY7djOegP7Ifd2%2Frg6ZWfabP5faBnQxiyHsHWLX7Al8jHc%3D|预订......"]},"httpstatus":200,"messages":"","status":true}

 需要注意:

    1.查询地址的queryZ会随机变化,有可能是queryA之类的,先随便传一个,如果不对的话 他会返回正的给你,再带上正确的地址去查询就可以了

  1. {'c_name''CLeftTicketUrl''c_url''leftTicket/queryZ''status'False}

    2.起点站终点站的数据是个简码,可以去下载他的信息下来自己整理 地址:https://www.12306.cn/index/script/core/common/station_name_v10015.js  

是用@隔开的 这样↓自己解析成一个json文件 方便使用(自己写写)

  =>  

    3.把查询的信息配置到一个方法中,方便读取更改

  1. TicketDTO = {}  # 封装请求参数
  2. def initTicketDTO():
  3.     with open('./city.json', encoding='utf-8'as f:
  4.         CITY_DATA = json.load(f)
  5.     TicketDTO['train_date'] = '2019-02-14' #日期
  6.     TicketDTO['from_station_name'] = '重庆' #起点站
  7.     TicketDTO['to_station_name'] = '潼南' #终点站
  8.     TicketDTO['class'] = ['D5147'#想购买车次  支持多个
  9.     TicketDTO['holder'] = ['XX'#谁买票 目前只支持单人
  10.     
  11.     TicketDTO['from_station'] = CITY_DATA[TicketDTO['from_station_name']] #起点站转换的简码
  12.     TicketDTO['to_station'] = CITY_DATA[TicketDTO['to_station_name']] #终点站简码
  13.     
  14.     TicketDTO['passengerInfo'] = {} #购票人信息


查询车票代码:

  1. select_ticket_URL = 'leftTicket/queryA'  # 查票的地址是在queryA、queryZ啥的随机变化的
  2. def select_ticket():
  3.     initTicketDTO()
  4.     global select_ticket_URL  # 也可以从查询的初始化界面的leftTicket/init中获取,这就不写了 还是用这种方式
  5.     url = 'https://kyfw.12306.cn/otn/'+select_ticket_URL +\
  6.         '?leftTicketDTO.train_date='+TicketDTO['train_date'] + \
  7.         '&leftTicketDTO.from_station=' + TicketDTO['from_station'] +\
  8.         '&leftTicketDTO.to_station='+TicketDTO['to_station'] +\
  9.         '&purpose_codes=ADULT'  # ADULT:单程普通票
  10.     data = get(url)
  11.     json_result = json.loads(data)
  12.     if(not json_result['status']):
  13.         select_ticket_URL = json_result['c_url']
  14.         select_ticket()
  15.         return
  16.     city_info = json_result['data']['map']  # 城市信息
  17.     for item in json_result['data']['result']:
  18.         classes = item.split('|')  # 被竖线隔开的
  19.         canBuy = classes[11]  # 是否可购买
  20.         secretStr = urllib.request.unquote(classes[0])  # 下单信息
  21.         IsEnable = (classes[0] == "" and False or True)  # 有无预定信息
  22.         train_no = classes[2]  # 班次
  23.         TrainClass = classes[3]  # 班次
  24.         FirstSeat = classes[31]  # 一等座
  25.         SecondSeat = Convert(classes[30])   # 二等座
  26.         print(canBuy+"=> 班次:"+TrainClass+" 历程:" + classes[13]+" "+city_info[classes[6]]+"=>"+city_info[classes[7]] + " "+classes[8]+"-" + classes[9]+"["+classes[10] + "h] 一等座:"+str(FirstSeat)+" 二等座:"+str(SecondSeat))
  27.     if(TrainClass in TicketDTO['class']):
  28.         print(train_no+":二等座余票:"+str(SecondSeat))
  29.         if(IsEnable and canBuy == 'Y'):  # 有提交信息 并且可以购买
  30.             print('可购买,正在提交')
  31.             if(SecondSeat > 0):
  32.                 while True:
  33.                 json_initDc = submitOrderRequest(secretStr)
  34.                 if(type(json_initDc) is dict):
  35.                 checkOrderInfo(json_initDc)
  36.                 time.sleep(2)
  37.             else:
  38.                 print('不可购买')
  39. def Convert(val):
  40.     if(val == '无' or val == ''):
  41.         return 0
  42.     elif(val == '有'): #有一般是大于20
  43.         return 20
  44.     return int(val)


返回的车次信息是通过'|'分割的 分割开来就可以看到了 大致有用的信息的元素位置代码中已经标注了




二、提交下单请求

  1. 地址:https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest 
  2. 参数:{
  3.        secretStr:NwCfMpWhQYGrv9zKy/E2bJHhqOoVHXTgo5hTboBBIe/Kzro2zxrnAiA4x1lswwh5upe5V31vZwXQuv7gxy............  # 这个参数是第一步查询的第一个参数,那趟车可买票的时候才会有这个参数
  4.        train_date:2019-02-14  # 发车日期
  5.        back_train_date:2019-01-20 # 返程日期(不是往返票不用管他)一般默认是当天
  6.        tour_flag:dc # 单程
  7.        purpose_codes:ADULT # 普通票
  8.        query_from_station_name:重庆 #起点站
  9.        query_to_station_name:潼南 # 终点站
  10.        undefined:  # 为空的参数
  11.     }
  12. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":"N","messages":[],"validateMessages":{}}

代码:

  1. # 检测是否有未完成的订单,没有的话获取购票人
  2. def submitOrderRequest(secretStr):
  3.     reqdata = {
  4.         "secretStr": secretStr,
  5.         "train_date": TicketDTO['train_date'],
  6.         "back_train_date": time.strftime('%Y-%m-%d', time.localtime(time.time())),
  7.         "tour_flag"'dc',
  8.         "purpose_codes"'ADULT',
  9.         "query_from_station_name": TicketDTO['from_station_name'],
  10.         "query_to_station_name": TicketDTO['to_station_name'],
  11.         "undefined"''
  12.     }
  13.     url = 'https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest'
  14.     data = post(url, reqdata)
  15.     json_result = json.loads(data)
  16.     if(json_result['status']):
  17.         if(json_result['data'] == 'N'):  # 无未完成的订单 去获取参数然后获取购票人
  18.             json_initDc = getinitDc()
  19.             if(getPassenge(json_initDc['REPEAT_SUBMIT_TOKEN'])):
  20.                 return json_initDc
  21.     else:
  22.         print(json_result['messages'])
  23.         return False

如果你有票未支付的状态下,会返回有未完成的订单什么什么的叫你去处理,其他没啥



三、获取一些参数后面用到

  1. https://kyfw.12306.cn/otn/confirmPassenger/initDc #获取一堆参数 后面的请求需要用到
  2. 参数:{
  3.      _json_att:'' # 空参数
  4.    }
  5. 返回的是一个页面,需要用正则去取数据

代码:

  1. def getinitDc():
  2.     html_data = post('https://kyfw.12306.cn/otn/confirmPassenger/initDc',
  3.                 {"_json_att: """})
  4.     REPEAT_SUBMIT_TOKEN = re.findall(re.compile(
  5.         "var globalRepeatSubmitToken = '(.*?)';", re.S), html_data)
  6.     key_check_isChange = re.findall(re.compile(
  7.         "'key_check_isChange':'(.*?)',", re.S), html_data)
  8.     leftTicketStr = re.findall(re.compile(
  9.         "'leftTicketStr':'(.*?)',", re.S), html_data)
  10.     tour_flag = re.findall(re.compile(
  11.         ",'tour_flag':'(.*?)',", re.S), html_data)
  12.     purpose_codes = re.findall(re.compile(
  13.         ",'purpose_codes':'(.*?)',", re.S), html_data)
  14.     train_location = re.findall(re.compile(
  15.         ",'train_location':'(.*?)'", re.S), html_data)
  16.     train_no = re.findall(re.compile(",'train_no':'(.*?)',", re.S), html_data) 
  17.     station_train_code = re.findall(re.compile(
  18.         ",'station_train_code':'(.*?)',", re.S), html_data) 
  19.     from_station_telecode = re.findall(re.compile(
  20.         ",'from_station_telecode':'(.*?)',", re.S), html_data)
  21.     to_station = re.findall(re.compile(
  22.         ",'to_station':'(.*?)',", re.S), html_data)
  23.     json_initDc = {
  24.         'REPEAT_SUBMIT_TOKEN': REPEAT_SUBMIT_TOKEN[0],
  25.         'key_check_isChange': key_check_isChange[0],
  26.         'leftTicketStr': leftTicketStr[0],
  27.         'tour_flag': tour_flag[0],
  28.         'purpose_codes': purpose_codes[0],
  29.         'train_location': train_location[0],
  30.         'train_no': train_no[0],
  31.         'station_train_code': station_train_code[0],
  32.         'from_station_telecode': from_station_telecode[0],
  33.         'to_station': to_station[0]
  34.     }
  35.     return json_initDc




四、获取购票人信息

  1. https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs # 获取购票人信息 (乘车人信息)
  2. 参数:{
  3.         _json_att:''#为空
  4.         REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c # 第三步中获取的一个参数
  5.       }
  6. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"notify_for_gat":"","isExist":true,"exMsg":"","two_isOpenClick":["93","95","97","99"],"other_isOpenClick":["91","93","98","99","95","97"],"normal_passengers":[{"code":"2","passenger_name":"XX","sex_code":"M","sex_name":"男","born_date":"199....","country_code":"CN","passenger_id_type_code":"1","passenger_id_type_name":"中国居民身份证","passenger_id_no":"500223....","passenger_type":"1","passenger_flag":"0","passenger_type_name":"成人","mobile_no":"13...","phone_no":"","email":"...@qq.com","address":"","postalcode":"","first_letter":"CZ","recordCount":"7","total_times":"99","index_id":"0","gat_born_date":"","gat_valid_date_start":"","gat_valid_date_end":"","gat_version":""},{"code":"1","passenger_name":"XX","sex_code":"M","sex_name":"男","born_date":"2016-02-14 00:00:00","country_code":"CN","passenger_id_type_code":"1","passenger_id_type_name":"中国居民身份证","passenger_id_no":"500230....","passenger_type":"1","passenger_flag":"0","passenger_type_name":"成人","mobile_no":"","phone_no":"","email":"","address":"","postalcode":"","first_letter":"AJ","recordCount":"7","total_times":"99","index_id":"1","gat_born_date":"","gat_valid_date_start":"","gat_valid_date_end":"","gat_version":""},{"code":"4","passenger_name":"XXX","sex_code":"F","sex_name":"女","born_date":"1900-01-01 00:00:00","country_code":"CN","passenger_id_type_code":"1","passenger_id_type_name":"中国居民身份证","passenger_id_no":"51160........","passenger_type":"1","passenger_flag":"0","passenger_type_name":"成人","mobile_no":"15.....","phone_no":"","email":"","address":"","postalcode":"","first_letter":"DYM","recordCount":"7","total_times":"99","index_id":"3","gat_born_date":"","gat_valid_date_start":"","gat_valid_date_end":"","gat_version":""}]},"messages":[],"validateMessages":{}}

代码

  1. def getPassenge(REPEAT_SUBMIT_TOKEN):
  2.     if(any(TicketDTO['passengerInfo'])): #如果获取过了就直接返回
  3.         return True
  4.     else:
  5.         url = 'https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs'
  6.         reqdata = {
  7.             "_json_att: """,
  8.             "REPEAT_SUBMIT_TOKEN": REPEAT_SUBMIT_TOKEN[0]
  9.         }
  10.         data = post(url, reqdata)
  11.         json_result = json.loads(data)
  12.         for item in json_result['data']['normal_passengers']:
  13.             if item['passenger_name'in TicketDTO['holder']:
  14.                 TicketDTO['passengerInfo'] = {'passengerTicketStr''O,'+item['passenger_flag']+','+item['passenger_type']+','+item['passenger_name']+','+item['passenger_id_type_code']+','+item['passenger_id_no']+','+item['mobile_no']+',N',
  15.                     'oldPassengerStr': item['passenger_name']+','+item['passenger_id_type_code']+','+item['passenger_id_no'] + ',1_'# 拼接下面要用到的参数
  16.         if(any(TicketDTO['passengerInfo'])):
  17.             print("购票人数据获取成功")
  18.             return True




五、检查订单信息

  1. https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo # 检查订单信息 (是否能购票,是否有未支付的订单)
  2. 参数:{
  3.     cancel_flag:2  # 固定参数
  4.     bed_level_order_num:000000000000000000000000000000  # 固定参数
  5.     passengerTicketStr:O,0,1,XX,1,500223...,132...,N   #购票人方法中拼接好的 购票人信息
  6.     oldPassengerStr:XX,1,5002.....,1_   #购票人中拼接好的
  7.     tour_flag:dc  #单程。。基本固定的,不过第三步中获取过
  8.     randCode:''  #空参数
  9.     whatsSelect:1  # 固定
  10.     _json_att:''  #空参数
  11.     REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c #第三步中获取的
  12. }
  13. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"ifShowPassCode":"N","canChooseBeds":"N","canChooseSeats":"Y","choose_Seats":"OM","isCanChooseMid":"N","ifShowPassCodeTime":"1","submitStatus":true,"smokeStr":""},"messages":[],"validateMessages":{}}

代码:

  1. def checkOrderInfo(json_initDc):
  2.     reqdata = {
  3.         'cancel_flag''2'#固定
  4.         'bed_level_order_num''000000000000000000000000000000',
  5.         'passengerTicketStr': TicketDTO['passengerInfo']['passengerTicketStr'],
  6.         'oldPassengerStr': TicketDTO['passengerInfo']['oldPassengerStr'],
  7.         'tour_flag': json_initDc['tour_flag'],  # 基本固定
  8.         'randCode''',
  9.         'whatsSelect''1',  # 固定
  10.         '_json_att''',  # 为空
  11.         'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
  12.     }
  13.     data = post(
  14.                 'https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo', reqdata)
  15.     json_result = json.loads(data)
  16.     print(json_result)
  17.     if(json_result['status']):
  18.         print('检查订单成功')
  19.     else:
  20.         print('检查订单失败'+json_result['messages'])
  21.         return

返回的可选座位类型之类的东西




6.获取余票与排队人数

  1. https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount # 获取余票,与排队人数
  2. 参数{
  3.         train_date:Thu+Feb+14+2019+00:00:00+GMT+0800+(中国标准时间) #购票时间格式化出来的
  4.         train_no:77000D514708 # 车次 第三步获取过
  5.         stationTrainCode:D5147 #班次 第三步获取过
  6.         seatType:O # O代表二等座,这儿有个固定字典的类型
  7.         fromStationTelecode:CUW #起点站简码 第三步获取过
  8.         toStationTelecode:TVW #终点站简码 第三步获取过
  9.         leftTicket:CLy0iYHVYp2Mctmf3%2FRbBmuCCUGyeyU8JF%2Bjolnv%2FfplIrD2 #第三步获取的
  10.         purpose_codes:00  #第三步获取的
  11.         train_location:W2 #第三步获取的
  12.         _json_att:''   #为空
  13.         REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c #第三步获取的
  14. }
  15. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"count":"0","ticket":"463,85","op_2":"false","countT":"0","op_1":"false"},"messages":[],"validateMessages":{}}

代码:

  1. # 获取余票
  2. def getQueueCount():
  3.     reqdata = {
  4.         'train_date': time.strftime("%a+%b+%d+%Y+00:00:00+GMT+0800", time.strptime(TicketDTO['train_date'], "%Y-%m-%d"))+'+(中国标准时间)',
  5.         'train_no': json_initDc['train_no'],  # 班次号
  6.         'stationTrainCode': json_initDc['station_train_code'],  # 车次
  7.         'seatType''O',# json_result['data']['choose_Seats'],  #  就是你选座的类型 二等座 O
  8.         'fromStationTelecode': json_initDc['from_station_telecode'],  # 起点终点
  9.         'toStationTelecode': json_initDc['to_station'],
  10.         'leftTicket': json_initDc['leftTicketStr'],
  11.         'purpose_codes': json_initDc['purpose_codes'],  # ?? 姑且认为是固定  没有取不到的参数!
  12.         'train_location': json_initDc['train_location'],  # ??
  13.         '_json_att''',
  14.         'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
  15.     }
  16.     reqdata_Str= bytes.decode(urllib.parse.urlencode(reqdata).encode('utf-8')).replace('%2B','+').replace('GMT+0800','GMT%2B0800').replace('%28','(').replace('%29',')')
  17.     _headers= {
  18.         "Accept":"application/json, text/javascript, */*; q=0.01",
  19.         "Accept-Encoding":"gzip, deflate, br",
  20.         "Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
  21.         "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
  22.         "Host":"kyfw.12306.cn",
  23.         "Referer":"https://kyfw.12306.cn/otn/confirmPassenger/initDc",
  24.         "User-Agent""Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0",
  25.         "X-Requested-With":"XMLHttpRequest"
  26.     }
  27.     data = post(
  28.         'https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount',reqdata_Str,headers=_headers)
  29.     json_result = json.loads(data)
  30.     if(json_result['status']):
  31.         print(json_result)
  32.         print('提交下单信息成功,余票信息:'+json_result['data']['ticket']+'  排队人数:'+json_result['data']['count']) #T估计是票总数
  33.         if(json_result['data']['op_2']=='true'):
  34.             print('排队人数超过余票'# 应该需要重新发起请求之类的
  35.     else:
  36.         print('提交下单信息失败,'+json_result['messages'])
  37.         return

需注意:

    1.train_date 参数格式化的问题,在这卡了1天.......,转码不对 提交的请求总会返回"url":"/leftTicket/init","status":false,"httpstatus":200,"messages":["系统忙,请稍后重试"]

返回的信息里ticket代表票的余量 会返回2中票的余量  op_2为true时代表排队人数超过余票






六、选座,提交

  1. https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue 
  2. 参数:{
  3.         passengerTicketStr:O,0,1,XX,1,500....,132.....,N   # 拼接的购票人信息
  4.         oldPassengerStr:XX,1,5002....,1_     # 拼接的购票人信息
  5.         randCode:'' #空数据
  6.         purpose_codes:00   # 第三步获取
  7.         key_check_isChange:5EAB44C37C430901F5D2AA18462DD31BDADFE7DE17048A36906509CD   # 第三步获取
  8.         leftTicketStr:Imh2yxCys%2Fo%2FhDX%2B3ZZOYgnkoNLrsZz6OFEYF5oGBrFRIB2p  # 第三步获取
  9.         train_location:W2  # 第三步获取
  10.         choose_seats:1#选的座位,没有的话他会帮你随机选。。
  11.         seatDetailType:000   # 固定
  12.         whatsSelect:1  # 固定
  13.         roomType:00   # 固定
  14.         dwAll:N   # 固定
  15.         _json_att:''  #空
  16.         REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c    # 第三步获取
  17. }
  18. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}

代码:

  1. def confirmSingleForQueue():
  2.     reqdata = {
  3.         'passengerTicketStr': TicketDTO['passengerInfo']['passengerTicketStr'],
  4.         'oldPassengerStr': TicketDTO['passengerInfo']['oldPassengerStr'],
  5.         'randCode''',
  6.         'purpose_codes': json_initDc['purpose_codes'],
  7.         'key_check_isChange': json_initDc['key_check_isChange'],
  8.         'leftTicketStr': json_initDc['leftTicketStr'],
  9.         'train_location': json_initDc['train_location'],
  10.         'choose_seats''1D',  # ??  选择的座位
  11.         'seatDetailType''000'# 固定
  12.         'whatsSelect''1',# 固定
  13.         'roomType''00',# 固定
  14.         'dwAll''N',# 固定
  15.         '_json_att':'' ,# 固定
  16.         'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
  17.     }
  18.     data = post(
  19.         'https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue', reqdata)
  20.     print(data)
  21.     json_result=json.loads(data)
  22.     if(json_result['status']):
  23.         if(json_result['data']['submitStatus']):
  24.             print('选座成功!')
  25.     else:
  26.         print('选座失败。。 '+json_result['messages'])
  27.         return



七、提交订单 排队,获取订单id

  1. https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1547780170175&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=a8dc2ed1a814b3067cfc32550c17e0ed # 排队
  2. 参数:{
  3.         random1547780170175 #秒级时间戳
  4.         tourFlag:dc # 固定的 普通票
  5.         _json_att:'' 
  6.         REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c  #第三步获取
  7. }
  8. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"queryOrderWaitTimeStatus":true,"count":0,"waitTime":-1,"requestId":6491860556856379439,"waitCount":0,"tourFlag":"dc","orderId":"E702546392"},"messages":[],"validateMessages":{}}

代码:

  1. # 下单等待确定
  2. def queryOrderWaitTime():
  3.     orderId=''
  4.     OrderWait_url = 'https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random='+str(int(time.time()*1000))+'&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN='+json_initDc['REPEAT_SUBMIT_TOKEN']
  5.     html_data = get(OrderWait_url)
  6.     json_result = json.loads(html_data) #这边返回的订单ID 需要作为参数
  7.     print(json_result)
  8.     if(json_result['status'and json_result['data']['queryOrderWaitTimeStatus']):
  9.         print('等待提交订单信息成功')
  10.         orderId=json_result['data']['orderId']
  11.         while(orderId==None):
  12.             time.sleep(json_result['data']['waitTime'])
  13.             html_data = get(OrderWait_url) #需要等一会才能返回订单的id
  14.             json_result = json.loads(html_data)
  15.             print(json_result)
  16.             if(json_result['status'and json_result['data']['queryOrderWaitTimeStatus']):orderId=json_result['data']['orderId']
  17.     else:
  18.         print('等待提交订单信息失败,'+json_result['messages'])
  19.         return

需注意:返回的信息中有个等待时间waitTime代表需要等待这么久 才能获取到orderid 否则是none,orderid下面需要用到



八、确定回执信息

  1. https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue # 确定下单成功
  2. 参数{
  3.         orderSequence_no:E702546390  #上一步返回的订单id
  4.         _json_att:'' #空
  5.         REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c  #第三步的
  6. }
  7. 返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}

代码:

  1. # 回执信息
  2. def resultOrderForDcQueue():
  3.     reqdata = {
  4.         'orderSequence_no':orderId,
  5.         '_json_att':'',
  6.         'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
  7.     }
  8.     data = post(
  9.     'https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue', reqdata)
  10.     print(data)
  11.     json_result = json.loads(data)
  12.     if(json_result['status']):
  13.         if(json_result['data']['submitStatus']):
  14.             print('下单成功!!!!!!!!!去付款吧')
  15.             # Email提醒
  16.     else:
  17.         print('下单失败。。。。。。。 '+json_result['messages'])

五-八我是写在一个方法里的,这里为了展示,分出来

源码地址:https://github.com/YuChenDayCode/Ticket


注意:

"submitStatus":true  !!!!说明下单成功了,去12306里支付就ok!!






欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

剑轩

2019/1/20 21:15:09

帅!帅帅帅帅!大作!

18335584353

2019/9/11 12:31:35

哥。我也是总返回url":"/leftTicket/init","status":false,"httpstatus":200,"messages":["系统忙,请稍后重试"],但是编码后,看的和原数据包一模一样,就是返回false。咋办啊。

雨雨雨雨辰:@18335584353reqdata_Str= bytes.decode(urllib.parse.urlencode(reqdata).encode('utf-8')).replace('%2B','+').replace('GMT+0800','GMT%2B0800').replace('%28','(').replace('%29',')'); 主要是这句 转码之后再把几个符号时间格式转换下 就好了呀 你用这个了吗

2019/9/11 20:50:04 回复

Python学习 1-安装

Ptyhon非常简单易用的面向对象的脚本语言,跨平台 入门简单python分2个版本 Python2、Python3。Python 2.7 将于 2020 年结...

Python学习 2-基本语法

基础:python脚本语言,不需要编译(像C#、Java、PHP、C++需要编译成机器可识别的语言), 而直接由解释器解释,很多地方类似...

Python学习 3-爬虫基本介绍 及简单实例

爬虫爬虫就是一只猪,蜘蛛。。 网络蜘蛛。互联网是一个网由各个网站组成。无数的蜘蛛就在网上到处爬,根据网址从一个网站爬...

Python实例 1-日志抓取处理 补错(附日志小技巧)

有时候数据出了问题,可以从日志中恢复数据(如果你没记日志..没备份..→_→..)一、日志展示介绍个平常自己用的小方法,如...

Python实例 2-12306抢票(一) 登陆

开坑年关将近,终于对12306下手了,,平安夜撸代码,攻克了12306的登陆 2018-12-24 22:16:00没错 这篇博客就写从零开始的异...

Python安装pip以及使用pip安装requests等模块

pip很简单的介绍pip 是一个现代的,通用的 Python 包管理工具。提供了对 Python 包的查找、下载、安装、卸载的功能。如果想...

Python数据集合区别

列表(list):可读写,值可以重复,有序排列,初始化语法:['tom',‘jerry’]元组(tuple):只读,值可以重复,...

使用VS Code开发Python

Vs Code开发Python可以很好的支持代码调试、智能提示、代码对齐等1:下载VS Codehttps://code.visualstudio.com/Downloadvs ...

Python变量与命名

Python使用等号 ( = ) 作为赋值运算符,例如a = 66 就是一条赋值语句,作用就是将66赋值给变量a。Python是弱类型语言,弱类...

Python关键字和内置函数

Python中包含了如下所示的关键字:上面这些关键字都不能作为变量名。另外,Python 3还提供了如下表所示的内置函数:也不能...

Python基础输入和输出

Python使用print()函数向打印程序输出,采用input()函数接收程序输入。print()函数的语法格式如下:print(value,...,sep=&#...

Python基本数据类型

Python包含两大类基本数据类型:数值类型、字符串类型,三大类组合数据类型:集合类型、序列类型和字典类型.数值类型:整型...

Python中通过fake_useragent生成随机UserAgent

fake_useragent第三方库,来实现随机请求头的设置;GitHub ---> https://github.com/hellysmile/fake-useragent安...

Python 升级pip

一条命令即可: python -m pip install --upgrade pip 安装成功后,一般是在python目录下面的Scripts里边的

Python html编码解码

使用方法:html.escape(s)与html.unescape即可 import html s="<div>jsdlfjsl</div>" #html编...