1 importrequests2 from requests importRequest,Session3 importrequests.cookies4 importurllib.parse as parse5
6 importre7 importrandom8 importlxml9 importos10
11 classTicketObject():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=None48
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 defsendRequest(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 #封装cookies
107 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 defcheck_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 returnTrue137 else:138 print('>>>用户检查是否在线>>>检查到用户不在线,请重新登陆')139 returnFalse140 exceptBaseException:141 print(">>>用户检查是否在线>>>网络异常!")142 returnFalse143
144
145 #三、点击预定,走二和三两个请求;注意:该请求cookie中一定要包含车次信息
146 defsubmit_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 exceptBaseException:166 return "NetWorkError"
167
168 if dic['status']:169 print('>>>提交订单成功')170 returnTrue171 elif dic['messages'] !=[]:172 if dic['messages'][0] == "车票信息已过期,请重新查询最新车票信息":173 print('>>>车票信息已过期,请重新查询最新车票信息')174 return "ticketInfoOutData"
175 else:176 print(">>>提交失败")177 returnFalse178
179 #四、访问https://kyfw.12306.cn/otn/confirmPassenger/initDc获取响应页面中js的globalRepeatSubmitToken 字段值;即后期查询联系人时需要请求的token值
180 #globalRepeatSubmitToken、以及在 ticketInfoForPassengerForm 字段中的两个key值:key_check_isChange和leftTicketStr两个key
181 #点击预定时,查询的联系人信息列表url'https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs'需要两个数据:_json_att=&REPEAT_SUBMIT_TOKEN
182 #后者那个Token就是下面getToken方法要获取的;注意;只有提交成功后才会获取到该code
183 defgetToken(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 #五、上面方法提交后,获取用户信息列表注意:该请求前要获取一个token
196 defloadPassengers(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 defcheckOrder(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'] isTrue:230 if dic['data']['ifShowPassCode'] == 'N':231 returnTrue232 if dic['data']['ifShowPassCode'] == 'Y':233 return "需要填验证码!"
234 else:235 print("checkOrderFail")236 returnFalse237
238 #提交订单可能出现验证码,这里是获取那个验证码图片
239 defgetCodeImage(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 ifflag:258 submitFlag=obj.submit_order()#提交订单
259
260 ifsubmitFlag:261 #-----------提交成功后,获取用户列表--------------#
262 obj.getToken()#获取提交订单时查询列表需要使用token,该token必须是提交成功后后去到
263 obj.loadPassengers()264 #-----------勾选乘客,确认订单提交----------#
265 checkFlag=obj.checkOrder()266 ifcheckFlag:267 print("购票订单确认成功,可加入购票队列!")268 #---------加入购票等待队列(排队)---------#
269
270 else:271 print("购票信息确认失败!")