From 7d263baa5694aa1737b68c2a1452e37156df048b Mon Sep 17 00:00:00 2001 From: wenxianping <931128603@qq.com> Date: Wed, 22 Aug 2018 17:03:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=85=A2=E6=8E=92=E9=98=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/ticket_config.yaml | 55 +++++++----- init/login.py | 4 - init/select_ticket_info.py | 168 ++++++++++++++++++++++--------------- myUrllib/httpUtils.py | 23 +++-- tkcode | Bin 0 -> 13695 bytes 5 files changed, 148 insertions(+), 102 deletions(-) diff --git a/config/ticket_config.yaml b/config/ticket_config.yaml index 5f377bd..5948551 100644 --- a/config/ticket_config.yaml +++ b/config/ticket_config.yaml @@ -41,26 +41,47 @@ set: station_dates: - - "2018-03-19" -# - "2018-02-21" + - "2018-09-05" station_trains: - - "G4831" + - "G6504" + - "G6224" + - "G6032" + - "G6346" + - "G6218" + - "G6030" + - "G1006" + - "G818" + - "G532" + - "G6552" + - "G6208" + - "G822" + - "G6506" + - "G1008" + - "G6508" + - "G9710" + - "G6214" + - "G6234" + - "G6510" + - "G6016" + - "G6514" + - "G6512" + - "G1010" + - "G2904" + - "G6204" + - "G6034" - from_station: "上海" - to_station: "长沙" + from_station: "深圳" + to_station: "广州" set_type: - "二等座" - - "商务座" - - "一等座" is_more_ticket: True ticke_peoples: - "文贤平" -# - "彭淑杰" 12306count: -# - uesr: "" -# - pwd: "apple1995" - - uesr: "@qq.com" +# - uesr: "qqxin1011" +# - pwd: "quxm19861011" + - uesr: "931128603@qq.com" - pwd: "QWERTY" select_refresh_interval: 1 @@ -75,14 +96,10 @@ damatu: email_conf: is_email: True - email: "@qq.com " - notice_email_list: "@qq.com" - username: "" - password: "" + email: "931128603@qq.com " + notice_email_list: "931128603@qq.com" + username: "931128603" + password: "xrvenridfpnnbehh" host: "smtp.qq.com" is_cdn: 2 - - - - diff --git a/init/login.py b/init/login.py index c66ee55..74deee9 100644 --- a/init/login.py +++ b/init/login.py @@ -216,10 +216,6 @@ class GoLogin: login_num = 0 while True: self.cookietp() - self.httpClint.set_cookies(_jc_save_showIns="true", - _jc_save_wfdc_flag="dc", - _jc_save_fromDate=_get_yaml()["set"]["station_dates"][0], - _jc_save_toDate=_get_yaml()["set"]["station_dates"][0]) self.urlConf["getCodeImg"]["req_url"] = self.urlConf["getCodeImg"]["req_url"].format(random.random()) self.readImg(self.urlConf["getCodeImg"]) self.randCode = self.getRandCode() diff --git a/init/select_ticket_info.py b/init/select_ticket_info.py index 4473414..d8c37cd 100644 --- a/init/select_ticket_info.py +++ b/init/select_ticket_info.py @@ -67,22 +67,22 @@ class select: select_refresh_interval = ticket_info_config["select_refresh_interval"] station_trains = ticket_info_config["set"]["station_trains"] ticket_black_list_time = ticket_info_config["ticket_black_list_time"] - print u"*"*20 + print u"*" * 20 print u"12306刷票小助手,最后更新于2018.2.28,请勿作为商业用途,交流群号:286271084" print u"如果有好的margin,请联系作者,表示非常感激\n" - print u"当前配置:出发站:{0}\n到达站:{1}\n乘车日期:{2}\n坐席:{3}\n是否有票自动提交:{4}\n乘车人:{5}\n刷新间隔:{6}s(如果想随机刷新,请自行修改)\n候选购买车次:{7}\n僵尸票关小黑屋时长:{8}\n".format\ - ( - from_station, - to_station, - station_dates, - ",".join(set_type), - is_more_ticket, - ",".join(ticke_peoples), - select_refresh_interval, - ",".join(station_trains), - ticket_black_list_time, + print u"当前配置:出发站:{0}\n到达站:{1}\n乘车日期:{2}\n坐席:{3}\n是否有票自动提交:{4}\n乘车人:{5}\n刷新间隔:{6}s(如果想随机刷新,请自行修改)\n候选购买车次:{7}\n僵尸票关小黑屋时长:{8}\n".format \ + ( + from_station, + to_station, + station_dates, + ",".join(set_type), + is_more_ticket, + ",".join(ticke_peoples), + select_refresh_interval, + ",".join(station_trains), + ticket_black_list_time, ) - print u"*"*20 + print u"*" * 20 return from_station, to_station, station_dates, set_type, is_more_ticket, ticke_peoples, select_refresh_interval, station_trains, ticket_black_list_time def get_order_request_params(self): @@ -171,7 +171,7 @@ class select: :return: token """ initdc_url = self.confUrl["initdc_url"] - initdc_result = self.httpClint.send(initdc_url,) + initdc_result = self.httpClint.send(initdc_url, ) token_name = re.compile(r"var globalRepeatSubmitToken = '(\S+)'") ticketInfoForPassengerForm_name = re.compile(r'var ticketInfoForPassengerForm=(\{.+\})?') order_request_params_name = re.compile(r'var orderRequestDTO=(\{.+\})?') @@ -207,7 +207,8 @@ class select: if 'data' in jsonData and jsonData['data'] and 'normal_passengers' in jsonData['data'] and jsonData['data'][ 'normal_passengers']: normal_passengers = jsonData['data']['normal_passengers'] - _normal_passenger = [normal_passengers[i] for i in range(len(normal_passengers))if normal_passengers[i]["passenger_name"] in self.ticke_peoples] + _normal_passenger = [normal_passengers[i] for i in range(len(normal_passengers)) if + normal_passengers[i]["passenger_name"] in self.ticke_peoples] return _normal_passenger if _normal_passenger else [normal_passengers[0]] # 如果配置乘车人没有在账号,则默认返回第一个用户 else: if 'data' in jsonData and 'exMsg' in jsonData['data'] and jsonData['data']['exMsg']: @@ -224,7 +225,7 @@ class select: station_ticket = self.httpClint.send(self.confUrl["select_url"]) return json.loads(station_ticket) - def submitOrderRequestImplement(self, from_station, to_station,): + def submitOrderRequestImplement(self, from_station, to_station, ): """ 提交车次信息 车次对应字典 @@ -239,7 +240,8 @@ class select: } 参照station_seat()方法 :return: """ - station_tickets = [self.submitOrderRequestFunc(from_station, to_station, station_date) for station_date in self.station_dates] + station_tickets = [self.submitOrderRequestFunc(from_station, to_station, station_date) for station_date in + self.station_dates] for station_ticket in station_tickets: value = station_ticket['data'] if not value: @@ -252,13 +254,17 @@ class select: for j in range(len(self._station_seat)): is_ticket_pass = ticket_info[self.station_seat(self._station_seat[j].encode("utf8"))] # print self._station_seat[j] - if is_ticket_pass != '' and is_ticket_pass != '无' and ticket_info[3] in self.station_trains and is_ticket_pass != '*': # 过滤有效目标车次 + if is_ticket_pass != '' and is_ticket_pass != '无' and ticket_info[ + 3] in self.station_trains and is_ticket_pass != '*': # 过滤有效目标车次 # tiket_values = [k for k in value['map'].values()] self.secretStr = ticket_info[0] train_no = ticket_info[3] print (u'车次: ' + train_no + ' 始发车站: ' + self.from_station + ' 终点站: ' + - self.to_station + ' ' + self._station_seat[j].encode("utf8") + ':' + ticket_info[self.station_seat(self._station_seat[j].encode("utf8"))]) - if self.ticket_black_list.has_key(train_no) and (datetime.datetime.now() - self.ticket_black_list[train_no]).seconds/60 < int(self.ticket_black_list_time): + self.to_station + ' ' + self._station_seat[j].encode("utf8") + ':' + + ticket_info[self.station_seat(self._station_seat[j].encode("utf8"))]) + if self.ticket_black_list.has_key(train_no) and ( + datetime.datetime.now() - self.ticket_black_list[ + train_no]).seconds / 60 < int(self.ticket_black_list_time): print(u"该车次{} 正在被关小黑屋,跳过此车次".format(train_no)) break else: @@ -375,7 +381,7 @@ class select: self.user_info[0]['mobile_no'] + ',N') oldPassengerStr.append( self.user_info[0]['passenger_name'] + "," + self.user_info[0]['passenger_id_type_code'] + "," + - self.user_info[0]['passenger_id_no'] + "," + self.user_info[0]['passenger_type'] + '_') + self.user_info[0]['passenger_id_no'] + "," + self.user_info[0]['passenger_type']) else: for i in range(len(self.user_info)): passengerTicketStrList.append( @@ -397,24 +403,27 @@ class select: passengerTicketStrList, oldPassengerStr = self.getPassengerTicketStrListAndOldPassengerStr() checkOrderInfoUrl = self.confUrl["checkOrderInfoUrl"] data = collections.OrderedDict() + data['passengerTicketStr'] = self.set_type + "," + ",".join(passengerTicketStrList).rstrip( + "_{0}".format(self.set_type)) + data['oldPassengerStr'] = "".join(oldPassengerStr) + data['REPEAT_SUBMIT_TOKEN'] = self.token + data['randCode'] = "" data['cancel_flag'] = 2 data['bed_level_order_num'] = "000000000000000000000000000000" - data['passengerTicketStr'] = self.set_type + "," + ",".join(passengerTicketStrList).rstrip("_{0}".format(self.set_type)) - data['oldPassengerStr'] = "".join(oldPassengerStr) data['tour_flag'] = 'dc' - data['whatsSelect'] = 1 - data['REPEAT_SUBMIT_TOKEN'] = self.token + data['_json_att'] = "" checkOrderInfo = self.httpClint.send(checkOrderInfoUrl, data) if 'data' in checkOrderInfo: + ifShowPassCodeTime = int(checkOrderInfo["data"]["ifShowPassCodeTime"]) / float(1000) if "ifShowPassCode" in checkOrderInfo["data"] and checkOrderInfo["data"]["ifShowPassCode"] == "Y": is_need_code = True - if self.getQueueCount(train_no, set_type, is_need_code): + if self.getQueueCount(train_no, set_type, is_need_code, ifShowPassCodeTime): return True if "ifShowPassCode" in checkOrderInfo["data"] and checkOrderInfo['data']['submitStatus'] is True: - print (u'车票提交通过,正在尝试排队') - is_need_code = False - if self.getQueueCount(train_no, set_type, is_need_code): - return True + print (u'车票提交通过,正在尝试排队') + is_need_code = False + if self.getQueueCount(train_no, set_type, is_need_code, ifShowPassCodeTime): + return True else: if "errMsg" in checkOrderInfo['data'] and checkOrderInfo['data']["errMsg"]: print checkOrderInfo['data']["errMsg"] @@ -424,24 +433,31 @@ class select: elif 'messages' in checkOrderInfo and checkOrderInfo['messages']: print (checkOrderInfo['messages'][0]) - def getQueueCount(self, train_no, set_type, is_need_code): + def getQueueCount(self, train_no, set_type, is_need_code, ifShowPassCodeTime): """ # 模拟查询当前的列车排队人数的方法 # 返回信息组成的提示字符串 :param token: :return: """ - l_time = time.localtime(time.time()) - new_train_date = time.strftime("%a %b %d %Y", l_time) + new_train_date = str(time.asctime(time.strptime(self.station_dates[0], "%Y-%m-%d"))).split(" ") getQueueCountUrl = self.confUrl["getQueueCountUrl"] data = collections.OrderedDict() - data['train_date'] = str(new_train_date) + " 00:00:00 GMT+0800 (中国标准时间)", + data['train_date'] = "{0} {1} {2} {3} {4} GMT+0800 (CST)".format( + new_train_date[0], + new_train_date[1], + new_train_date[3], + new_train_date[5], + time.strftime("%H:%M:%S", time.localtime(time.time())) + ), data['train_no'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['train_no'], - data['stationTrainCode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['station_train_code'], + data['stationTrainCode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO'][ + 'station_train_code'], data['seatType'] = self.set_type, - data['fromStationTelecode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['from_station'], + data['fromStationTelecode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO'][ + 'from_station'], data['toStationTelecode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['to_station'], - data['leftTicket'] = self.get_ticketInfoForPassengerForm()['leftTicketStr'], + data['leftTicket'] = urllib.unquote(self.get_ticketInfoForPassengerForm()['leftTicketStr']), data['purpose_codes'] = self.get_ticketInfoForPassengerForm()['purpose_codes'], data['train_location'] = self.get_ticketInfoForPassengerForm()['train_location'], data['REPEAT_SUBMIT_TOKEN'] = self.get_token(), @@ -456,7 +472,7 @@ class select: print(u"当前余票数小于乘车人数,放弃订票") else: print(u"排队成功, 当前余票还剩余: {0} 张".format(ticket_split)) - if self.checkQueueOrder(is_need_code): + if self.checkQueueOrder(ifShowPassCodeTime, is_need_code): return True else: print(u"当前排队人数: {1} 当前余票还剩余:{0} 张,继续排队中".format(ticket_split, countT)) @@ -488,7 +504,7 @@ class select: fresult = self.httpClint.send(checkRandCodeAnsyn, randData) # 校验验证码是否正确 return fresult['data']['msg'] - def checkQueueOrder(self, is_node_code=False): + def checkQueueOrder(self, ifShowPassCodeTime, is_node_code=False): """ 模拟提交订单是确认按钮,参数获取方法还是get_ticketInfoForPassengerForm 中获取 :return: @@ -497,13 +513,14 @@ class select: passengerTicketStrList, oldPassengerStr = self.getPassengerTicketStrListAndOldPassengerStr() checkQueueOrderUrl = self.confUrl["checkQueueOrderUrl"] data = { - "passengerTicketStr": self.set_type + "," + ",".join(passengerTicketStrList).rstrip("_{0}".format(self.set_type)), + "passengerTicketStr": self.set_type + "," + ",".join(passengerTicketStrList).rstrip( + "_{0}".format(self.set_type)), "oldPassengerStr": "".join(oldPassengerStr), "purpose_codes": self.get_ticketInfoForPassengerForm()["purpose_codes"], - "key_check_isChange": self.get_ticketInfoForPassengerForm()["key_check_isChange"], - "leftTicketStr": self.get_ticketInfoForPassengerForm()["leftTicketStr"], + "key_check_isChange": urllib.unquote(self.get_ticketInfoForPassengerForm()["key_check_isChange"]), + "leftTicketStr": urllib.unquote(self.get_ticketInfoForPassengerForm()["leftTicketStr"]), "train_location": self.get_ticketInfoForPassengerForm()["train_location"], - "seatDetailType": "000", # 开始需要选择座位,但是目前12306不支持自动选择作为,那这个参数为默认 + "seatDetailType": "", # 开始需要选择座位,但是目前12306不支持自动选择作为,那这个参数为默认 "roomType": "00", # 好像是根据一个id来判断选中的,两种 第一种是00,第二种是10,但是我在12306的页面没找到该id,目前写死是00,不知道会出什么错 "dwAll": "N", "whatsSelect": 1, @@ -523,13 +540,14 @@ class select: data['randCode'] = randCode break else: - print (u"验证码有误, {0}次尝试重试".format(i+1)) + print (u"验证码有误, {0}次尝试重试".format(i + 1)) print(u"验证码超过限定次数3次,放弃此次订票机会!") else: print(u"不需要验证码") buy_end_time = (datetime.datetime.now() - self.buy_ticket_time).seconds print(u"总共花费时长{0}S".format(buy_end_time)) - time.sleep(8-buy_end_time if buy_end_time<8 else 0) + print(ifShowPassCodeTime) + time.sleep(ifShowPassCodeTime) checkQueueOrderResult = self.httpClint.send(checkQueueOrderUrl, data) if "status" in checkQueueOrderResult and checkQueueOrderResult["status"]: c_data = checkQueueOrderResult["data"] if "data" in checkQueueOrderResult else {} @@ -572,20 +590,24 @@ class select: queryOrderWaitTimeResult = {} if queryOrderWaitTimeResult: if "status" in queryOrderWaitTimeResult and queryOrderWaitTimeResult["status"]: - if "orderId" in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"]["orderId"] is not None: - sendEmail(u"恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306,访问‘未完成订单’,在30分钟内完成支付!".format(queryOrderWaitTimeResult["data"]["orderId"])) - raise ticketIsExitsException(u"恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306,访问‘未完成订单’,在30分钟内完成支付!".format(queryOrderWaitTimeResult["data"]["orderId"])) + if "orderId" in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"][ + "orderId"] is not None: + sendEmail(u"恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306,访问‘未完成订单’,在30分钟内完成支付!".format( + queryOrderWaitTimeResult["data"]["orderId"])) + raise ticketIsExitsException(u"恭喜您订票成功,订单号为:{0}, 请立即打开浏览器登录12306,访问‘未完成订单’,在30分钟内完成支付!".format( + queryOrderWaitTimeResult["data"]["orderId"])) elif "msg" in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"]["msg"]: print queryOrderWaitTimeResult["data"]["msg"] break - elif "waitTime"in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"]["waitTime"]: - print(u"排队等待时间预计还剩 {0} ms".format(0-queryOrderWaitTimeResult["data"]["waitTime"])) + elif "waitTime" in queryOrderWaitTimeResult["data"] and queryOrderWaitTimeResult["data"][ + "waitTime"]: + print(u"排队等待时间预计还剩 {0} ms".format(0 - queryOrderWaitTimeResult["data"]["waitTime"])) else: print ("正在等待中") elif "messages" in queryOrderWaitTimeResult and queryOrderWaitTimeResult["messages"]: print(u"排队等待失败: " + queryOrderWaitTimeResult["messages"]) else: - print(u"第{}次排队中,请耐心等待".format(num+1)) + print(u"第{}次排队中,请耐心等待".format(num + 1)) else: print(u"排队中") time.sleep(2) @@ -606,13 +628,17 @@ class select: except ValueError: queryMyOrderNoCompleteResult = {} if queryMyOrderNoCompleteResult: - if "data" in queryMyOrderNoCompleteResult and queryMyOrderNoCompleteResult["data"] and "orderDBList" in queryMyOrderNoCompleteResult["data"] and queryMyOrderNoCompleteResult["data"]["orderDBList"]: + if "data" in queryMyOrderNoCompleteResult and queryMyOrderNoCompleteResult["data"] and "orderDBList" in \ + queryMyOrderNoCompleteResult["data"] and queryMyOrderNoCompleteResult["data"]["orderDBList"]: orderId = queryMyOrderNoCompleteResult["data"]["orderDBList"][0]["sequence_no"] return orderId - elif "data" in queryMyOrderNoCompleteResult and "orderCacheDTO" in queryMyOrderNoCompleteResult["data"] and queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]: - if "message" in queryMyOrderNoCompleteResult["data"]["orderCacheDTO"] and queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]["message"]: + elif "data" in queryMyOrderNoCompleteResult and "orderCacheDTO" in queryMyOrderNoCompleteResult["data"] and \ + queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]: + if "message" in queryMyOrderNoCompleteResult["data"]["orderCacheDTO"] and \ + queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]["message"]: print(queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]["message"]["message"]) - raise ticketNumOutException(queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]["message"]["message"]) + raise ticketNumOutException( + queryMyOrderNoCompleteResult["data"]["orderCacheDTO"]["message"]["message"]) else: if "message" in queryMyOrderNoCompleteResult and queryMyOrderNoCompleteResult["message"]: print queryMyOrderNoCompleteResult["message"] @@ -645,7 +671,8 @@ class select: "_json_att": "" } cancelNoCompleteMyOrderResult = self.httpClint.send(cancelNoCompleteMyOrderUrl, cancelNoCompleteMyOrderData) - if "data" in cancelNoCompleteMyOrderResult and "existError" in cancelNoCompleteMyOrderResult["data"] and cancelNoCompleteMyOrderResult["data"]["existError"] == "N": + if "data" in cancelNoCompleteMyOrderResult and "existError" in cancelNoCompleteMyOrderResult["data"] and \ + cancelNoCompleteMyOrderResult["data"]["existError"] == "N": print(u"排队超时,已为您自动取消订单,订单编号: {0}".format(sequence_no)) time.sleep(2) return True @@ -660,7 +687,7 @@ class select: if self.is_cdn == 1: while True: if self.cdn_list: - self.httpClint.cdn = self.cdn_list[random.randint(0, len(self.cdn_list)-1)] + self.httpClint.cdn = self.cdn_list[random.randint(0, len(self.cdn_list) - 1)] break else: pass @@ -676,7 +703,7 @@ class select: self.login.go_login() def cdn_req(self, cdn): - for i in range(len(cdn)-1): + for i in range(len(cdn) - 1): http = HTTPClient() urls = self.confUrl["loginInit"] start_time = datetime.datetime.now() @@ -715,11 +742,14 @@ class select: while 1: try: num += 1 - if "user_time" in self.is_check_user and (datetime.datetime.now() - self.is_check_user["user_time"]).seconds/60 > 5: + if "user_time" in self.is_check_user and ( + datetime.datetime.now() - self.is_check_user["user_time"]).seconds / 60 > 5: # 5分钟检查一次用户是否登录 self.check_user() time.sleep(self.select_refresh_interval) - if time.strftime('%H:%M:%S', time.localtime(time.time())) > "23:00:00" or time.strftime('%H:%M:%S', time.localtime(time.time())) < "06:00:00": + if time.strftime('%H:%M:%S', time.localtime(time.time())) > "23:00:00" or time.strftime('%H:%M:%S', + time.localtime( + time.time())) < "06:00:00": print(u"12306休息时间,本程序自动停止,明天早上6点将自动运行") while 1: time.sleep(1) @@ -730,10 +760,14 @@ class select: start_time = datetime.datetime.now() self.submitOrderRequestImplement(from_station, to_station) print u"正在第{0}次查询 乘车日期: {1} 车次{2} 查询无票 cdn轮询IP {4} 当前cdn总数{5} 总耗时{3}ms".format(num, - ",".join(self.station_dates), - ",".join(self.station_trains), - (datetime.datetime.now()-start_time).microseconds/1000, self.httpClint.cdn, - len(self.cdn_list)) + ",".join( + self.station_dates), + ",".join( + self.station_trains), + ( + datetime.datetime.now() - start_time).microseconds / 1000, + self.httpClint.cdn, + len(self.cdn_list)) self.set_cdn() except PassengerUserException as e: print e.message @@ -757,8 +791,8 @@ class select: print(e.message) except KeyError as e: print(e.message) - except TypeError as e: - print(u"12306接口无响应,正在重试 {0}".format(e.message)) + # except TypeError as e: + # print(u"12306接口无响应,正在重试 {0}".format(e.message)) except socket.error as e: print(e.message) @@ -766,4 +800,4 @@ class select: if __name__ == '__main__': login() # a = select('上海', '北京') - # a.main() \ No newline at end of file + # a.main() diff --git a/myUrllib/httpUtils.py b/myUrllib/httpUtils.py index 0a4a2f4..0937fb9 100644 --- a/myUrllib/httpUtils.py +++ b/myUrllib/httpUtils.py @@ -1,6 +1,7 @@ # -*- coding: utf8 -*- import json import socket +from collections import OrderedDict from time import sleep import requests @@ -48,17 +49,16 @@ class HTTPClient(object): def _set_header(self): """设置header""" - return { - "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", - "X-Requested-With": "application/json, text/javascript, */*; q=0.01", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5", - "Referer": "https://kyfw.12306.cn/otn/login/init", - "Accept": "*/*", - "Accept-Encoding": "br, gzip, deflate", - "Origin": "https://kyfw.12306.cn", - "Accept-Language": "zh-cn", - "Connection": "keep-alive", - } + header_dict = OrderedDict() + header_dict["Host"] = "kyfw.12306.cn" + header_dict["keep-alive"] = "keep-alive" + header_dict["Accept"] = "application/json, text/plain, */*" + header_dict[ + "User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) 12306-electron/1.0.1 Chrome/59.0.3071.115 Electron/1.8.4 Safari/537.36" + header_dict["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8" + header_dict["Accept-Encoding"] = "gzip, deflate" + header_dict["Accept-Language"] = "zh-CN" + return header_dict def setHeaders(self, headers): self._s.headers.update(headers) @@ -103,7 +103,6 @@ class HTTPClient(object): allow_redirects = False is_logger = urls["is_logger"] error_data = {"code": 99999, "message": u"重试次数达到上限"} - self.setHeadersReferer(urls["Referer"]) if data: method = "post" self.setHeaders({"Content-Length": "{0}".format(len(data))}) diff --git a/tkcode b/tkcode index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4e53bf92f90f486ca71a87b0ad9d6922cda9c02f 100644 GIT binary patch literal 13695 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 literal 0 HcmV?d00001