待续。。。
1 import requests 2 from requests import Request,Session 3 import requests.cookies 4 import urllib.parse as parse 5 6 import re 7 import random 8 import lxml 9 import os 10 11 class TicketObject(): 12 13 def __init__(self): 14 #这里没有写登录,则通过网页登录后,根据车次,日期查询后的cookie信息粘贴这里,暂时的(里面包含车次查询条件信息字段) 15 self.Cookies='JSESSIONID=19EED0F307740A8B8A80B69917547E15; tk=wYsN5viUHaif1cAWJNkDnb6bdkTp5QdD4OuMwwafz2z0; RAIL_EXPIRATION=1547346452769; RAIL_DEVICEID=TamgucQvy9ZbuKgQQPahgPaiAaGBPANQhca1rft3YUSmWw6ra-J47njLWFqEm1TwFYZVWNNA6pIYyhPySCECMb7qjfkBgXfCM3C7a9yNAH9juhwJvKasPdWs3_hZ2N2v710gIoY2XYT7bvUwogWBEH1Z8sYNT7wM; _jc_save_fromStation=%u5317%u4EAC%2CBJP; _jc_save_toStation=%u54C8%u5C14%u6EE8%2CHBB; _jc_save_fromDate=2019-01-31; _jc_save_toDate=2019-01-09; _jc_save_wfdc_flag=dc; BIGipServerotn=468713994.50210.0000; BIGipServerpassport=1005060362.50215.0000; route=9036359bb8a8a461c164a04f8f50b252; BIGipServerpool_passport=183304714.50215.0000; current_captcha_type=Z' 16 self.trainInfoJSON={}#查询车次json列表 17 self.trainCodeItems=[]#车次[车次,车次...] 18 self.trainSecretStrDict={}#查询是每个车次都有一个这个字符串,在订单提交时需要提交该字段值 {车次:secretStr,....} 19 self.trainStartDict={}#每个车次起始站 20 self.trainEndDict={} #每个车次终点站 21 self.trainStartTime={}#发车时间 22 self.trainEndTime={} #到站时间 23 self.trainDuration={} #历时多久 24 self.trainSeatTotal_swz={}#商务座数量 25 self.trainSeatTotal_ydz={}#一等座数量 26 self.trainSeatTotal_edz={}#二等座数量 27 self.trainSeatTotal_gjrw = {} # 高级软卧座数量 28 self.trainSeatTotal_rw = {} # 软卧一等座数量 29 self.trainSeatTotal_dw= {} # 动卧数量 30 self.trainSeatTotal_rz= {} # 软座数量 31 self.trainSeatTotal_yz= {} # 硬座座数量 32 self.trainSeatTotal_wz= {} # 无座数量 33 34 self.repeatSubmitToken="" 35 self.keyIsChange="" 36 self.leftTicketStr="" 37 38 #查询车票条件 39 self.date='2019-01-31'#查询日期 40 self.station_1='BJP'#起始站 41 self.station_2='HBB'#终点站 42 self.trainCode='K4011'#预定车次 43 44 # 封装cookie[下面封装了也暂时没用上,因为上面使用了cookies字符串] 45 self.cookiesJar = requests.cookies.RequestsCookieJar() 46 #联系人信息json 47 self.passengersDict=None 48 49 50 #登陆后查询车次信息列表url,即登陆后,点击查询按钮发送的请求URL[查询车次列表信息] 51 self.queryURL="https://kyfw.12306.cn/otn/leftTicket/queryZ" 52 #点击预定,则会有两个URL,1、检查用户是否在线 2、提交订单 即下面两个url请求 53 # (点击预定),先检查用户是否在线)检查用户是否在线URL 54 self.checkUserURL = 'https://kyfw.12306.cn/otn/login/checkUser' 55 #(点击预定)https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest 56 self.submitOrderRequestURL="https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest" 57 #点击预定按钮访问的界面,获取token使用,注意必须是上面submitOrderRequestURL提交成功后,才可以通过下面url获取token 58 self.initDcURL = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc' 59 #点击预定查询联系人列表,待用户勾选 60 self.passengerDTOURL='https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs' 61 #点击提交,确认订单信息 62 self.chekOrderURL='https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo' 63 #将购票订单加入队列URL 64 self.queueURL='https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount' 65 #等待订单结果URL 66 self.orderResultURL='https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue' 67 68 self.session = Session() 69 70 #异步请求headers 71 self.headers={ 72 "Cookie":self.Cookies, 73 "User - Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0", 74 "Host":"kyfw.12306.cn", 75 "X-Requested-With":"XMLHttpRequest" 76 } 77 78 #一、根据始发站和目的站点,查询日期 获取车次信息 79 def sendRequest(self): 80 81 req = Request('GET', self.queryURL, headers=self.headers, params={ 'leftTicketDTO.train_date':self.date, 82 "leftTicketDTO.from_station": self.station_1,"leftTicketDTO.to_station":self.station_2,"purpose_codes":"ADULT"}) 83 prep = self.session.prepare_request(req) 84 res = self.session.send(prep) 85 print(res.status_code) 86 print(res.json()) 87 self.trainInfoJSON=res.json() 88 for a in res.json()['data']['result']: 89 code=a.split("|")[3]#车次 90 self.trainCodeItems.append(code)#封装车次 91 self.trainSecretStrDict[code]=a.split('|')[0]#封装车次对应的secretStr,在点击预定提交订单使用;注意每次查询同一个车次的secretStr是不同的,每次都在变 92 93 self.trainSeatTotal_swz[code] = a.split('|')[32] # 商务座数量 94 self.trainSeatTotal_ydz[code] = a.split('|')[31] # 一等座数量 95 self.trainSeatTotal_edz[code] = a.split('|')[30] # 二等座数量 96 self.trainSeatTotal_gjrw[code] = a.split('|')[21] # 高级软卧座数量 97 self.trainSeatTotal_rw[code] = a.split('|')[25] # 软卧一等座数量 98 self.trainSeatTotal_dw[code] = a.split('|')[33] # 动卧数量 99 self.trainSeatTotal_rz[code] = a.split('|')[24] # 软座数量100 self.trainSeatTotal_yz[code] = a.split('|')[29] # 硬座座数量101 self.trainSeatTotal_wz[code] = a.split('|')[26] # 无座数量102 self.trainStartTime[code] = a.split('|')[8] # 发车时间103 self.trainEndTime[code] = a.split('|')[9] # 到站时间104 self.trainDuration[code] = a.split('|')[10] # 历时多久105 106 #封装cookies107 self.cookiesJar = requests.cookies.RequestsCookieJar()108 for items in self.Cookies.split(";"):109 k, v = items.split('=', 1) # =号分割,分割成2个字段110 self.cookiesJar.set(k, v)111 #print(k,v)112 113 print('车次列表:',self.trainCodeItems)#获取车次114 print('车次secretStr:',self.trainSecretStrDict)#该字段存在,表示该车次可以提交订单115 print(self.trainSeatTotal_swz)116 print(self.trainSeatTotal_ydz)117 print(self.trainSeatTotal_edz)118 print(self.trainSeatTotal_wz)119 print("开始时间:",self.trainStartTime)120 print("结束时间:",self.trainEndTime)121 print("历时:",self.trainDuration)122 print(">>>查询车次信息成功")123 124 125 #二、检查 用户是否在线;点击预定,走二和三两个请求126 def check_user(self):127 data = { "_json_att": ""}128 try:129 response = self.session.post(url=self.checkUserURL, data=data, headers=self.headers,130 verify=False)131 print(response.status_code)132 print(response.json())133 dic = response.json()134 if dic['data']['flag']:135 print(">>>用户检查是否在线>>>用户在线验证成功")136 return True137 else:138 print('>>>用户检查是否在线>>>检查到用户不在线,请重新登陆')139 return False140 except BaseException:141 print(">>>用户检查是否在线>>>网络异常!")142 return False143 144 145 #三、点击预定,走二和三两个请求;注意:该请求cookie中一定要包含车次信息146 def submit_order(self):147 #print(self.trainSecretStrDict['K4011'])148 #print(self.trainStartTime['K4011'])149 #print(self.trainEndTime['K4011'])150 data = { "secretStr":parse.unquote(self.trainSecretStrDict[self.trainCode]),#注意这里一定要解码 parse.unquote解码,否则提交不会成功(在查询和提交时通过浏览器debug发现该字段不同,问题就在这里)151 "train_date": self.date,152 "back_train_date": self.cookiesJar.get("_jc_save_toDate"),153 "tour_flag": "dc",154 "purpose_codes": "ADULT",155 "query_from_station_name": '北京',#self.trainInfoJSON['data']['map'][self.station_1], #注意这里提交时候参数为汉字156 "query_to_station_name": '哈尔滨',#self.trainInfoJSON['data']['map'][self.station_2],157 "undefined": ""158 }159 160 response = self.session.post(url=self.submitOrderRequestURL, data=data, headers=self.headers, verify=False)161 print(response.status_code)162 try:163 dic = response.json()164 print(dic)165 except BaseException:166 return "NetWorkError"167 168 if dic['status']:169 print('>>>提交订单成功')170 return True171 elif dic['messages'] != []:172 if dic['messages'][0] == "车票信息已过期,请重新查询最新车票信息":173 print('>>>车票信息已过期,请重新查询最新车票信息')174 return "ticketInfoOutData"175 else:176 print(">>>提交失败")177 return False178 179 # 四、访问https://kyfw.12306.cn/otn/confirmPassenger/initDc获取响应页面中js的globalRepeatSubmitToken 字段值;即后期查询联系人时需要请求的token值180 # globalRepeatSubmitToken、以及在 ticketInfoForPassengerForm 字段中的两个key值:key_check_isChange和leftTicketStr两个key181 # 点击预定时,查询的联系人信息列表url'https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs'需要两个数据:_json_att=&REPEAT_SUBMIT_TOKEN182 # 后者那个Token就是下面getToken方法要获取的;注意;只有提交成功后才会获取到该code183 def getToken(self):184 data = { "_json_att": ''}185 response = self.session.post(url=self.initDcURL, data=data, headers=self.headers,186 verify=False)187 self.repeatSubmitToken = re.findall(u'globalRepeatSubmitToken = \'(\S+?)\'', response.text)[0]188 print("repeatSubmitToken", self.repeatSubmitToken)189 self.keyIsChange = re.findall(u'key_check_isChange\':\'(\S+?)\'', response.text)[0]190 print("keyIsChange:", self.keyIsChange)191 self.leftTicketStr = re.findall(u'leftTicketStr\':\'(\S+?)\'', response.text)[0]192 print("leftTicketStr", self.leftTicketStr)193 print(">>>成功获取token")194 195 #五、上面方法提交后,获取用户信息列表注意:该请求前要获取一个token196 def loadPassengers(self):197 response = self.session.post(url=self.passengerDTOURL, data={ "_json_att": "","REPEAT_SUBMIT_TOKEN":self.repeatSubmitToken}, headers=self.headers, verify=False)198 print(response.status_code)199 print(">>>联系人获取是否成功:",response.json()['status'])200 #联系人信息201 self.passengersDict=response.json()202 203 #六、点击提交,确认订单信息204 def checkOrder(self):205 #确认订单联系人信息数据,如下两个字段206 passengerKicketStr = ""207 oldPassengerStr = ""208 data = {209 "cancel_flag": "2",210 "bed_level_order_num": "000000000000000000000000000000",211 "passengerTicketStr":"1,0,1,"+self.passengersDict['data']['normal_passengers'][0]['passenger_name']+',1,'+self.passengersDict['data']['normal_passengers'][0]['passenger_id_no']+','+self.passengersDict['data']['normal_passengers'][0]['mobile_no']+',N_' ,212 #这里我只选了默认第一个用户,只选了个无座;passengerTicketStr:座位类型,0,车票类型,姓名,身份正号,电话(多个的话,以逗号分隔) 多人用_下划线隔开213 #例如:1,0,1,张三,1,身份证号码略,18622455880,N_1,0,1,李四,1,身份证号码略,N 注意最后这个N[这里没介绍是什么字段]214 215 "oldPassengerStr": self.passengersDict['data']['normal_passengers'][0]['passenger_name']+',1,'+self.passengersDict['data']['normal_passengers'][0]['passenger_id_no']+'1_',216 #这里我只选了默认第一个用户;oldPassengerStr:姓名,证件类别,证件号码,用户类型 多个用户_下划线隔开217 #例如:张三,1,身份证号码略,1_李四,1,身份证号码略,1_218 219 "tour_flag": "dc",220 "randCode": "", #randCode:预定验证码221 "whatsSelect": "1",222 "_json_att": "",223 "REPEAT_SUBMIT_TOKEN": self.repeatSubmitToken224 }225 226 response = self.session.post(url=self.chekOrderURL, data=data, headers=self.headers, verify=False)227 dic=response.json()228 print(dic)#注意:'ifShowPassCode': 'N',响应后的json中如果该字段为Y,则需要填验证码验证;一般非购票高峰期,没这个验证码229 if dic['data']['submitStatus'] is True:230 if dic['data']['ifShowPassCode'] == 'N':231 return True232 if dic['data']['ifShowPassCode'] == 'Y':233 return "需要填验证码!"234 else:235 print("checkOrderFail")236 return False237 238 #提交订单可能出现验证码,这里是获取那个验证码图片239 def getCodeImage(self):240 url = 'https://kyfw.12306.cn/otn/passcodeNew/getPassCodeNew?module=passenger&rand=randp&{}'.format(random.random())241 response = self.session.get(url=url, headers=self.headers, verify=False)242 #path = os.path.abspath('..')243 with open("img.jpg", 'wb') as f:244 f.write(response.content)245 246 #将订单加入购票队列247 248 #等待订单结果249 250 251 if __name__=="__main__":252 obj=TicketObject()253 #-------------填日期,起始和终点站,点击查询-------------#254 obj.sendRequest()#查询所有车次信息255 #-------------点击预定------------#256 flag=obj.check_user()#验证用户是否在线257 if flag:258 submitFlag=obj.submit_order()#提交订单259 260 if submitFlag:261 #-----------提交成功后,获取用户列表--------------#262 obj.getToken()#获取提交订单时查询列表需要使用token,该token必须是提交成功后后去到263 obj.loadPassengers()264 #-----------勾选乘客,确认订单提交----------#265 checkFlag=obj.checkOrder()266 if checkFlag:267 print("购票订单确认成功,可加入购票队列!")268 #---------加入购票等待队列(排队)---------#269 270 else:271 print("购票信息确认失败!")