当前位置: 首页 > 工具软件 > captcha-12306 > 使用案例 >

python12306买票_Python-爬虫-12306购票业务实现

锺离德庸
2023-12-01

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("购票信息确认失败!")

 类似资料: