博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python-爬虫-12306购票业务实现
阅读量:4992 次
发布时间:2019-06-12

本文共 13103 字,大约阅读时间需要 43 分钟。

待续。。。

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

 

转载于:https://www.cnblogs.com/ygzhaof/p/10242929.html

你可能感兴趣的文章
CODEVS 1217 借教室
查看>>
VM ware 安装时候的一些坑和解决办法
查看>>
【原】最长上升子序列——动态规划
查看>>
26. Remove Duplicates from Sorted Array
查看>>
使用weak property声明Outlet
查看>>
RN开发-Navigator
查看>>
innodb二进制文件相关的参数
查看>>
前谷歌高管给初入职场新人的14条忠告
查看>>
01-html介绍和head标签
查看>>
Python之Linux下的 virtualenv
查看>>
ASP.NET Web开发框架之三 报表开发
查看>>
大家好
查看>>
PHP文件上传类
查看>>
Python基础 --- 使用 dict 和 set
查看>>
仿迅雷播放器教程 -- 基于VLC的MFC播放器 (6)
查看>>
Python之数据结构基础
查看>>
WPF:如何高速更新Model中的属性
查看>>
hdu 1010(DFS) 骨头的诱惑
查看>>
(转)Android SDK Manager国内无法更新的解决方案
查看>>
SQL语句修改表
查看>>