diff --git a/README.md b/README.md index 853104f..0b9f55a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ - python版本支持 - 2.7 - 依赖库 - - 依赖打码兔 需要去打码兔注册(用户)账号,打码兔账号地址:http://www.dama2.com,一般充值1元就够用了,充值打码兔之后,首次运行是需要到官网黑白名单授权 + - 依赖打码兔 需要去打码兔注册(用户)账号,打码兔账号地址:http://www.dama2.com, 一般充值1元就够用了,充值打码兔之后,首次运行是需要到官网黑白名单授权 - 依赖若快 若快注册地址:http://www.ruokuai.com/client/index?6726 推荐用若快,打码兔在12306验证码更新之后识别率不是很高 - 项目依赖包 requirements.txt - 安装方法 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt @@ -204,4 +204,8 @@ - 修改已知bug - 最后感谢群里提供测试和代码的小伙伴,能为你们买到一张回家的票真的感到灰常开心 +- 2018.2.28更新,收12306风控影响,代码可能有时候会进入慢排队,请自行调整刷新参数 + - 修改已知bug + - 优化接口提交参数规范 + diff --git a/config/urlConf.py b/config/urlConf.py index 5b16164..360b111 100644 --- a/config/urlConf.py +++ b/config/urlConf.py @@ -10,6 +10,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -20,6 +21,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, @@ -31,6 +33,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": False, "is_json": False, }, @@ -41,6 +44,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -51,6 +55,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": False, "is_json": False, }, @@ -61,6 +66,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": False, "is_json": False, }, @@ -71,6 +77,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -81,6 +88,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -91,6 +99,29 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, + "is_logger": False, + "is_json": False, + }, + "GetJS": { + "req_url": "/otn/HttpZF/GetJS", + "req_type": "get", + "Referer": "https://kyfw.12306.cn/otn/confirmPassenger/initDc", + "Host": "kyfw.12306.cn", + "re_try": 10, + "re_time": 0.1, + "s_time": 0.01, + "is_logger": False, + "is_json": False, + }, + "odxmfwg": { + "req_url": "/otn/dynamicJs/odxmfwg", + "req_type": "get", + "Referer": "https://kyfw.12306.cn/otn/confirmPassenger/initDc", + "Host": "kyfw.12306.cn", + "re_try": 10, + "re_time": 0.1, + "s_time": 0.01, "is_logger": False, "is_json": False, }, @@ -101,6 +132,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -111,6 +143,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": False, "is_json": True, }, @@ -121,6 +154,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.3, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -131,6 +165,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -141,6 +176,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -151,6 +187,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -161,6 +198,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -171,6 +209,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -181,6 +220,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": False, "is_json": False, }, @@ -191,6 +231,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -201,6 +242,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -211,6 +253,7 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, @@ -221,6 +264,40 @@ urls = { "Host": "kyfw.12306.cn", "re_try": 10, "re_time": 0.1, + "s_time": 0.01, + "is_logger": True, + "is_json": True, + }, + "autoSubmitOrderRequest": { + "req_url": "/otn/confirmPassenger/autoSubmitOrderRequest", + "req_type": "post", + "Referer": "https://kyfw.12306.cn/otn/queryOrder/initNoComplete", + "Host": "kyfw.12306.cn", + "re_try": 10, + "re_time": 0.1, + "s_time": 0.01, + "is_logger": True, + "is_json": True, + }, + "getQueueCountAsync": { + "req_url": "/otn/confirmPassenger/getQueueCountAsync", + "req_type": "post", + "Referer": "https://kyfw.12306.cn/otn/queryOrder/initNoComplete", + "Host": "kyfw.12306.cn", + "re_try": 10, + "re_time": 0.1, + "s_time": 0.01, + "is_logger": True, + "is_json": True, + }, + "confirmSingleForQueueAsys": { + "req_url": "/otn/confirmPassenger/confirmSingleForQueueAsys", + "req_type": "post", + "Referer": "https://kyfw.12306.cn/otn/queryOrder/initNoComplete", + "Host": "kyfw.12306.cn", + "re_try": 10, + "re_time": 0.1, + "s_time": 0.01, "is_logger": True, "is_json": True, }, diff --git a/init/login.py b/init/login.py index 73036ec..dfe33c5 100644 --- a/init/login.py +++ b/init/login.py @@ -220,7 +220,10 @@ class GoLogin: login_num = 0 while True: self.cookietp() - self.httpClint.set_cookies(_jc_save_wfdc_flag="dc", _jc_save_fromStation="%u4E0A%u6D77%u8679%u6865%2CAOH", _jc_save_toStation="%u5170%u5DDE%u897F%2CLAJ", _jc_save_fromDate="2018-02-14", _jc_save_toDate="2018-01-16", RAIL_DEVICEID="EN_3_EGSe2GWGHXJeCkFQ52kHvNCrNlkz9n1GOqqQ1wR0i98WsD8Gj-a3YHZ-XYKeESWgCiJyyucgSwkFOzVHhHqfpidLPcm2vK9n83uzOPuShO3Pl4lCydAtQu4BdFqz-RVmiduNFixrcrN_Ny43135JiEtqLaI") + 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 fe10c43..dcb9eed 100644 --- a/init/select_ticket_info.py +++ b/init/select_ticket_info.py @@ -10,6 +10,8 @@ import time import urllib from collections import OrderedDict +import collections + from agency.cdn_utils import CDNProxy from config import urlConf from config.emailConf import sendEmail @@ -48,6 +50,7 @@ class select: self.is_download_img = False self.randCode = "" self.cdn_list = [] + self.buy_ticket_time = "" def get_ticket_info(self): """ @@ -64,7 +67,8 @@ 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 "*"*20 + print u"*"*20 + print u"12306刷票小助手,最后更新于2018.2.28,请勿作为商业用途,交流群号:286271084\n" print u"当前配置:出发站:{0}\n到达站:{1}\n乘车日期:{2}\n坐席:{3}\n是否有票自动提交:{4}\n乘车人:{5}\n刷新间隔:{6}\n候选购买车次:{7}\n僵尸票关小黑屋时长:{8}\n".format\ ( from_station, @@ -77,7 +81,7 @@ class select: ",".join(station_trains), ticket_black_list_time, ) - print "*"*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): @@ -138,8 +142,8 @@ class select: :return: """ today = datetime.date.today() - tomorrow = today+datetime.timedelta(1) - return tomorrow.strftime('%Y-%m-%d') + # tomorrow = today+datetime.timedelta(1) + return today.strftime('%Y-%m-%d') def callReadImg(self, code_url): """ @@ -147,7 +151,6 @@ class select: :param code_url: 验证码url :return: """ - print(code_url) self.login.readImg(code_url=code_url) self.is_aotu_code = True @@ -183,6 +186,12 @@ class select: else: pass + def GetJS(self): + getJSUrl = self.confUrl["GetJS"] + self.httpClint.send(getJSUrl) + odxmfwgUrl = self.confUrl["odxmfwg"] + self.httpClint.send(odxmfwgUrl) + def getPassengerDTOs(self): """ 获取乘客信息 @@ -253,15 +262,17 @@ class select: break else: print (u'正在尝试提交订票...') - + self.buy_ticket_time = datetime.datetime.now() # self.submitOrderRequestFunc(from_station, to_station, self.time()) self.submit_station() self.getPassengerTicketStr(self._station_seat[j].encode("utf8")) self.getRepeatSubmitToken() if not self.user_info: # 修改每次都调用用户接口导致用户接口不能用 self.user_info = self.getPassengerDTOs() + codeImgByOrder = self.confUrl["codeImgByOrder"] + self.login.readImg(codeImgByOrder) if self.checkOrderInfo(train_no, self._station_seat[j].encode("utf8")): - break + break else: pass else: @@ -305,7 +316,7 @@ class select: """ submit_station_url = self.confUrl["submit_station_url"] data = [('secretStr', urllib.unquote(self.secretStr)), # 字符串加密 - ('train_date', self.time()), # 出发时间 + ('train_date', self.station_dates[0]), # 出发时间 ('back_train_date', self.time()), # 返程时间 ('tour_flag', 'dc'), # 旅途类型 ('purpose_codes', 'ADULT'), # 成人票还是学生票 @@ -384,7 +395,7 @@ class select: """ passengerTicketStrList, oldPassengerStr = self.getPassengerTicketStrListAndOldPassengerStr() checkOrderInfoUrl = self.confUrl["checkOrderInfoUrl"] - data = OrderedDict() + data = collections.OrderedDict() data['cancel_flag'] = 2 data['bed_level_order_num'] = "000000000000000000000000000000" data['passengerTicketStr'] = self.set_type + "," + ",".join(passengerTicketStrList).rstrip("_{0}".format(self.set_type)) @@ -422,18 +433,17 @@ class select: l_time = time.localtime(time.time()) new_train_date = time.strftime("%a %b %d %Y", l_time) getQueueCountUrl = self.confUrl["getQueueCountUrl"] - data = { - 'train_date': str(new_train_date) + " 00:00:00 GMT+0800 (中国标准时间)", - 'train_no': self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['train_no'], - 'stationTrainCode': self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['station_train_code'], - 'seatType': self.set_type, - 'fromStationTelecode': self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['from_station'], - 'toStationTelecode': self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['to_station'], - 'leftTicket': self.get_ticketInfoForPassengerForm()['leftTicketStr'], - 'purpose_codes': self.get_ticketInfoForPassengerForm()['purpose_codes'], - 'train_location': self.get_ticketInfoForPassengerForm()['train_location'], - 'REPEAT_SUBMIT_TOKEN': self.get_token(), - } + data = collections.OrderedDict() + data['train_date'] = str(new_train_date) + " 00:00:00 GMT+0800 (中国标准时间)", + data['train_no'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['train_no'], + data['stationTrainCode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['station_train_code'], + data['seatType'] = self.set_type, + data['fromStationTelecode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['from_station'], + data['toStationTelecode'] = self.get_ticketInfoForPassengerForm()['queryLeftTicketRequestDTO']['to_station'], + data['leftTicket'] = 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(), getQueueCountResult = self.httpClint.send(getQueueCountUrl, data) if "status" in getQueueCountResult and getQueueCountResult["status"] is True: if "countT" in getQueueCountResult["data"]: @@ -497,15 +507,14 @@ class select: "dwAll": "N", "whatsSelect": 1, "_json_at": "", + "randCode": "", + "choose_seats": "", "REPEAT_SUBMIT_TOKEN": self.get_token(), } - try: if is_node_code: print(u"正在使用自动识别验证码功能") for i in range(3): - codeImgByOrder = self.confUrl["codeImgByOrder"] - self.login.readImg(codeImgByOrder) randCode = self.login.getRandCode() checkcode = self.checkRandCodeAnsyn(randCode) if checkcode == 'TRUE': @@ -517,7 +526,9 @@ class select: print(u"验证码超过限定次数3次,放弃此次订票机会!") else: print(u"不需要验证码") - time.sleep(0.5) + buy_end_time = (datetime.datetime.now() - self.buy_ticket_time).seconds + print("总共花费时长{0}S".format(buy_end_time)) + time.sleep(8-buy_end_time if buy_end_time<8 else 0) checkQueueOrderResult = self.httpClint.send(checkQueueOrderUrl, data) if "status" in checkQueueOrderResult and checkQueueOrderResult["status"]: c_data = checkQueueOrderResult["data"] if "data" in checkQueueOrderResult else {} @@ -670,7 +681,7 @@ class select: start_time = datetime.datetime.now() http.cdn = cdn[i].replace("\n", "") rep = http.send(urls) - if rep and "message" not in rep and (datetime.datetime.now() - start_time).microseconds / 1000 < 300: + if rep and "message" not in rep and (datetime.datetime.now() - start_time).microseconds / 1000 < 200: self.cdn_list.append(cdn[i].replace("\n", "")) print(u"所有cdn解析完成...") @@ -707,10 +718,14 @@ class select: # 5分钟检查一次用户是否登录 self.check_user() time.sleep(self.select_refresh_interval) - if time.strftime('%H:%M:%S', time.localtime(time.time())) > "23:00:00": - print u"12306休息时间,本程序自动停止,明天早上6点将自动运行" - time.sleep(60 * 60 * 7) - self.call_login() + 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) + if time.strftime('%H:%M:%S', time.localtime(time.time())) > "06:00:00": + print(u"休息时间已过,重新开启检票功能") + self.call_login() + break 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, diff --git a/myUrllib/httpUtils.py b/myUrllib/httpUtils.py index fe2abd2..72fdf49 100644 --- a/myUrllib/httpUtils.py +++ b/myUrllib/httpUtils.py @@ -50,10 +50,14 @@ class HTTPClient(object): """设置header""" return { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", - "X-Requested-With": "xmlHttpRequest", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36", + "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", } def setHeaders(self, headers): @@ -86,6 +90,14 @@ class HTTPClient(object): def cdn(self, cdn): self._cdn = cdn + # def send_socket(self, urls, data=None, **kwargs): + # data = """ + # POST {0} HTTP/1.1 + # {0} + # """.format(urls["req_url"], self._set_header()) + # fack = socket.create_connection(urls["Host"], 443) + # fack.send() + def send(self, urls, data=None, **kwargs): """send request to url.If response 200,return response, else return None.""" allow_redirects = False @@ -108,6 +120,7 @@ class HTTPClient(object): url_host = urls["Host"] for i in range(urls["re_try"]): try: + sleep(urls["s_time"]) if "s_time" in urls else sleep(0.001) requests.packages.urllib3.disable_warnings() response = self._s.request(method=method, timeout=2, diff --git a/tkcode b/tkcode index f74a4b8..d5c92f1 100644 Binary files a/tkcode and b/tkcode differ