Python爬虫之12306-买票器小白源码
研究不易
import requests import re import urllib.parse import json import datetime from collections import OrderedDict self=requests.session() self.verify=False self.headers={ 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding':'gzip, deflate, br', 'Accept-Language':'zh-CN,zh;q=0.9', 'Connection':'keep-alive', 'Host':'kyfw.12306.cn', 'Cache-Control':'no-cache', 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36', } def dow(): url='https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand' requests.packages.urllib3.disable_warnings() res=self.get(url) print(res.status_code) if res.status_code==200: with open('login.jpg','wb') as f: f.write(res.content) else: dow() #验证 def yanzhengma(): #获取验证码 dow() # 1 2 3 4 5 6 7 8 xylist=['35,35','105,35','175,35','245,35','35,105','105,105','175,105','245,105'] print('请依次输入验证码的位置并换行,0表示结束') ilist=[] while True: i=input() if i=='0': break ilist.append(xylist[int(i)-1]) xy=','.join(ilist) print(xy) data={ 'answer':xy, 'login_site':'E', 'rand':'sjrand' } url='https://kyfw.12306.cn/passport/captcha/captcha-check' requests.packages.urllib3.disable_warnings() res=self.post(url,data=data) print(res.url) print(res.text) code=re.search('_code.*?(\d)',res.text,re.S).group(1) print(code) # 成功返回True,失败递归调用 if code=='4': print('验证成功,请登陆') elif code=='5': print('验证错误') yanzhengma() elif code=='7': print('验证超时') yanzhengma() else: print('验证失败') yanzhengma() def login(): yanzhengma()#验证码 data={ 'username':'账号, 'password':'密码, 'appid':'otn' } url='https://kyfw.12306.cn/passport/web/login' requests.packages.urllib3.disable_warnings() res=self.post(url,data=data) print(res.text) code=re.search('_code.*?(\d)',res.text,re.S).group(1) # 成功返回True,失败递归调用 if code=='0': yzdata={ 'appid':'otn' } tk_url='https://kyfw.12306.cn/passport/web/auth/uamtk' resp3=self.post(tk_url,data=yzdata) print('-----------------第一次验证-----------------') print(resp3.text) login_message=resp3.json()['newapptk'] print('loginMessage=',login_message) yz2data={ 'tk':login_message } client_url='https://kyfw.12306.cn/otn/uamauthclient' resp4=self.post(client_url,data=yz2data) print('-----------------第二次验证-----------------') print(resp4.text) print('登陆成功,可以开始购票') elif code=='1': print('密码输入错误。如果输错次数超过4次,用户将被锁定。') login() else: print('登录失败') login() def shoppiao(): data={ 'leftTicketDTO.train_date':'2018-10-15',#时间 'leftTicketDTO.from_station':'BJQ',#出发站码 'leftTicketDTO.to_station':'CBN',#目的地码 'purpose_codes':'ADULT'#票类型ADULT成人,STUDENT学生 } url='https://kyfw.12306.cn/otn/leftTicket/queryA?' requests.packages.urllib3.disable_warnings() res=self.get(url,params=data) print(res.status_code) print(res.text) dictr=json.loads(res.text) pklist=dictr.get('data').get('result') for item in pklist: sp_item = item.split('|') for index, item in enumerate(sp_item,0): print('{}:\t{}'.format(index, item)) ''' 通过分析 0:列车信息(订票时候需要) 1:信息 2:火车编号 3:列车号 4:始发站 5:终点站 6:起始站 7:目标站 8:出发时间 9:到达时间 10:行车时间 11:有3种状态:Y, N, IS_TIME_NOT_BUY 分别对应,可以预定,不可以预定,其他原因----对应的是第1项 12:参数leftTicket 13:日期 14: 15:参数train_location 21:高级动卧 22: 23:软卧 24:软座 25:特等座 26:无座 28:硬卧 29:硬座 30:二等座 31:一等座 32:商务座 33:动卧 ''' if sp_item[11]=='Y':#可以买票,开始订票 if sp_item[30]!='无':#买二等座 print(sp_item[30]) if vel_longin_2():#验证登陆 print('已经登陆可以开始下单') self.headers['X-Requested-With']='XMLHttpRequest' secretStr=urllib.parse.unquote(sp_item[0]) self.headers['Referer'] = 'https://kyfw.12306.cn/otn/leftTicket/init' submitOrderRequest(secretStr) data = OrderedDict() data["_json_att"] ='' data["fromStationTelecode"] =sp_item[6] data["leftTicket"] = sp_item[12] data['purpose_codes']='00' data['REPEAT_SUBMIT_TOKEN']='' data["seatType"] = 'O' data["stationTrainCode"] = sp_item[3] data["toStationTelecode"] =sp_item[7] data["train_date"] = str(datetime.datetime.strptime(sp_item[13], '%Y%m%d').strftime('%a %b %d %Y %H:%M:%S GMT+0800')) data["train_location"] = sp_item[15] data["train_no"] =sp_item[2] ''' data={ 'train_date':str(datetime.datetime.strptime(sp_item[13], '%Y%m%d').strftime('%a %b %d %Y %H:%M:%S GMT+0800')), 'train_no':sp_item[2],#火车编号 'stationTrainCode':sp_item[3],#列车号 'seatType':'O',#座位类型 1是硬座(无座),2是软座,3是硬卧,4是软卧, O 大写字母 是高铁二等座,M是高铁一等座,商务座(9),特等座(P),高级软卧(6) 'fromStationTelecode':sp_item[6],#起始站编号 'toStationTelecode':sp_item[7],#目标站编号 'leftTicket':sp_item[12], 'train_location':sp_item[15],#15项Q6 } ''' shopdin(data) else: login() shoppiao() break def submitOrderRequest(secretStr): data = OrderedDict() data["secretStr"] = secretStr data["train_date"] = '2018-09-20' data["tour_flag"] = "dc" data["purpose_codes"] = "ADULT" data["query_from_station_name"] ='深圳' data["query_to_station_name"] = '赤壁' data["undefined"] = '' url='https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest' res=self.post(url,data=data) print(res.text) #开始下单 def shopdin(data1): #获取1个参数 liop=initDc() #改变Headers self.headers['Referer'] = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc' self.headers['Origin']='https://kyfw.12306.cn' print(self.headers) #请求并获取联系人参数 pasdir=PassengerDTOs(liop[0]) print(type(pasdir)) # 二等座 0 (车票类型:ticket_type_codes 成人票1) ,1,张三(passenger_name), 1(证件类型:passenger_id_type_code),320xxxxxx(passenger_id_no),151xxxx(mobile_no),N passengerTicketStr=data1['seatType']+ ',0,'+pasdir['passenger_type']+','+pasdir['passenger_name']+','+pasdir['passenger_id_type_code']+','+pasdir['passenger_id_no']+','+pasdir['mobile_no']+',N' #张三(passenger_name),1(证件类型:passenger_id_type_code),320xxxxxx(passenger_id_no),1(passenger_type)_ oldPassengerStr=pasdir['passenger_name']+','+pasdir['passenger_id_type_code']+','+pasdir['passenger_id_no']+','+pasdir['passenger_type']+'_' data = OrderedDict() data["_json_att"] ='' data["ed_level_order_num"] ='000000000000000000000000000000' data["cancel_flag"] ='2' data["oldPassengerStr"] = oldPassengerStr data["passengerTicketStr"] = passengerTicketStr data["randCode"] ='' data["tour_flag"] ='dc' data["whatsSelect"] ='1' ''' data={ 'cancel_flag':'2',#固定 'ed_level_order_num':'000000000000000000000000000000',#固定 'passengerTicketStr':passengerTicketStr,#联系人 'oldPassengerStr':oldPassengerStr,#联系人 'tour_flag':'dc',#单程 'randCode':'',#空 'whatsSelect':'1',#是否选择了联系人, } ''' OrderInfo(data) data1['REPEAT_SUBMIT_TOKEN']=liop[0] #提交订单 getQueueCount(data1) data2= OrderedDict() data2["_json_att"] =''#空 data2["choose_seats"] =''#空 data2["dwAll"] ='N'#固定 data2["key_check_isChange"] =liop[1]#页面找 data2["leftTicketStr"] = data1['leftTicket']#列车 data2["oldPassengerStr"] =oldPassengerStr#联系人 data2["passengerTicketStr"] =passengerTicketStr#联系人 data2["purpose_codes"] ='00'# data2["randCode"] =''#随机数 空 data2["REPEAT_SUBMIT_TOKEN"] =liop[0] data2["roomType"] ='00'#固定 data2["seatDetailType"] ='000'#选铺 暂不支持 默认参数 data2["train_location"] = data1['train_location']#q6 data2["whatsSelect"] ='1'#固定 tijiao(data2) def tijiao(data): url='https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue' res=self.post(url,data=data) print(res.status_code) print(res.text) #获取联系人 def PassengerDTOs(REPEAT_SUBMIT_TOKEN): data={ '_json_att':'',#空 'REPEAT_SUBMIT_TOKEN':REPEAT_SUBMIT_TOKEN#页面找 } url='https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs' res=self.post(url,data=data) #print(res.text) passen=res.json().get('data').get('normal_passengers')[1] print(passen) return passen #订单页面----获取参数 def initDc(): url='https://kyfw.12306.cn/otn/confirmPassenger/initDc' res=self.post(url,data={'_json_att':''}) REPEAT_SUBMIT_TOKEN=re.search('globalRepeatSubmitToken = .(.*?).;',res.text,re.S).group(1) ticketInfoForPassengerForm_name = re.compile(r'var ticketInfoForPassengerForm=(\{.+\})?') order_request_params_name = re.compile(r'var orderRequestDTO=(\{.+\})?') re_tfpf = re.findall(ticketInfoForPassengerForm_name, res.text) re_orp = re.findall(order_request_params_name, res.text) key_check_isChange=re.search('key_check_isChange.:.(.*?).,',res.text,re.S).group(1) return [REPEAT_SUBMIT_TOKEN,key_check_isChange] #选乘客票种提交 def OrderInfo(data): url='https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo' res=self.post(url,data=data) print(res.status_code) print(res.text) #提交订单列车余票。 def getQueueCount(data): url='https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount' res=self.post(url,data=data) print(res.status_code) print(res.text) def vel_longin_2(): url='https://kyfw.12306.cn/otn/login/checkUser' requests.packages.urllib3.disable_warnings() res=self.post(url,data={'_json_att':''}) login_vel=res.json().get('data')['flag'] return login_vel if __name__ == '__main__': #yanzhengma() shoppiao() vel_longin_2()