diff --git a/README.md b/README.md index 76fafaf..13b030c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ - [ ] 邮件通知 ## 使用 -py12306 需要运行 python 3.6 以上版本(其它版本暂未测试) +py12306 需要运行在 python 3.6 以上版本(其它版本暂未测试) **1. 安装依赖** ```bash @@ -57,6 +57,8 @@ python main.py -t -n python main.py ``` +## 下单成功图片 +![下单成功图片](./data/images/order_success.png) ## Thanks 感谢大佬 [testerSunshine](https://github.com/testerSunshine/12306),借鉴了部分实现 diff --git a/data/images/order_success.png b/data/images/order_success.png new file mode 100644 index 0000000..91eba4b Binary files /dev/null and b/data/images/order_success.png differ diff --git a/py12306/helpers/app.py b/py12306/helpers/app.py index 5651278..9b668a0 100644 --- a/py12306/helpers/app.py +++ b/py12306/helpers/app.py @@ -56,3 +56,24 @@ class App: if not cls.check_user_account_is_empty(): CommonLog.add_quick_log(CommonLog.MESSAGE_CHECK_EMPTY_USER_ACCOUNT).flush(exit=True) if Const.IS_TEST_NOTIFICATION: cls.test_send_notifications() + + +# Expand +class Dict(dict): + def get(self, key, default=None, sep='.'): + keys = key.split(sep) + for i, key in enumerate(keys): + try: + value = self[key] + if len(keys[i + 1:]) and isinstance(value, Dict): + return value.get(sep.join(keys[i + 1:]), default=default, sep=sep) + return value + except: + return self.dict_to_dict(default) + + def __getitem__(self, k): + return self.dict_to_dict(super().__getitem__(k)) + + @staticmethod + def dict_to_dict(value): + return Dict(value) if isinstance(value, dict) else value diff --git a/py12306/helpers/func.py b/py12306/helpers/func.py index cbf5621..4bf406b 100644 --- a/py12306/helpers/func.py +++ b/py12306/helpers/func.py @@ -4,11 +4,11 @@ import threading import functools from time import sleep +from types import MethodType from py12306 import config - def singleton(cls): """ 将一个类作为单例 @@ -120,6 +120,13 @@ def sleep_forever_when_in_test(): if Const.IS_TEST: sleep_forever() +def expand_class(cls, key, value, keep_old=True): + if (keep_old): + setattr(cls, 'old_' + key, getattr(cls, key)) + setattr(cls, key, MethodType(value, cls)) + return cls + + @singleton class Const: IS_TEST = False diff --git a/py12306/helpers/request.py b/py12306/helpers/request.py index 762432a..f47f662 100644 --- a/py12306/helpers/request.py +++ b/py12306/helpers/request.py @@ -1,4 +1,6 @@ -from requests_html import HTMLSession +from py12306.helpers.app import * +from py12306.helpers.func import * +from requests_html import HTMLSession, HTMLResponse class Request(HTMLSession): @@ -18,3 +20,27 @@ class Request(HTMLSession): for chunk in response.iter_content(chunk_size=1024): f.write(chunk) return response + + @staticmethod + def _handle_response(response, **kwargs) -> HTMLResponse: + """ + 扩充 response + :param response: + :param kwargs: + :return: + """ + response = HTMLSession._handle_response(response, **kwargs) + expand_class(response, 'json', Request.json) + return response + + def json(self, default={}): + """ + 重写 json 方法,拦截错误 + :return: + """ + from py12306.helpers.app import Dict + try: + result = self.old_json() + return Dict(result) + except: + return Dict(default) diff --git a/py12306/log/query_log.py b/py12306/log/query_log.py index dd01a86..c7a086d 100644 --- a/py12306/log/query_log.py +++ b/py12306/log/query_log.py @@ -101,9 +101,9 @@ class QueryLog(BaseLog): self = cls() self.add_quick_log('查询余票请求失败') if code: - self.add_quick_log('状态码{} '.format(code)) + self.add_quick_log('状态码 {} '.format(code)) if reason: - self.add_quick_log('错误原因{} '.format(reason)) + self.add_quick_log('错误原因 {} '.format(reason)) self.flush(sep='\t') return self diff --git a/py12306/order/order.py b/py12306/order/order.py index d5a9408..cd5e42d 100644 --- a/py12306/order/order.py +++ b/py12306/order/order.py @@ -105,6 +105,8 @@ class Order: OrderLog.add_quick_log(OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_SUCCESS).flush() return True else: + if (str(result.get('messages', '')).find('未处理') >= 0): # 未处理订单 + stay_second(self.retry_time) OrderLog.add_quick_log( OrderLog.MESSAGE_SUBMIT_ORDER_REQUEST_FAIL.format(result.get('messages', '-'))).flush() return False @@ -134,9 +136,9 @@ class Order: } response = self.session.post(API_CHECK_ORDER_INFO, data) result = response.json() - if 'data' in result and result['data'].get('submitStatus'): # 成功 + if result.get('data.submitStatus'): # 成功 OrderLog.add_quick_log(OrderLog.MESSAGE_CHECK_ORDER_INFO_SUCCESS).flush() - if result['data'].get("ifShowPassCode") != 'N': + if result.get('data.ifShowPassCode') != 'N': self.is_need_auth_code = True return True else: @@ -181,7 +183,7 @@ class Order: } response = self.session.post(API_GET_QUEUE_COUNT, data) result = response.json() - if 'data' in result and ('countT' in result['data'] or 'ticket' in result['data']): # 成功 + if result.get('data.countT') or result.get('data.ticket'): # 成功 """ "data": { "count": "66", @@ -191,7 +193,7 @@ class Order: "op_1": "true" } """ - ticket = result['data']['ticket'].split(',') # 暂不清楚具体作用 + ticket = result.get('data.ticket').split(',') # 暂不清楚具体作用 ticket_number = sum(map(int, ticket)) current_position = int(data.get('countT', 0)) OrderLog.add_quick_log( @@ -251,13 +253,13 @@ class Order: "submitStatus": true } """ - if result['data'].get('submitStatus'): # 成功 + if result.get('data.submitStatus'): # 成功 OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_SUCCESS).flush() return True else: # 加入小黑屋 TODO OrderLog.add_quick_log( - OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR.format(result['data'].get('errMsg', '-'))).flush() + OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_ERROR.format(result.get('data.errMsg', '-'))).flush() else: OrderLog.add_quick_log(OrderLog.MESSAGE_CONFIRM_SINGLE_FOR_QUEUE_FAIL.format( result.get('messages', '-'))).flush() diff --git a/py12306/query/job.py b/py12306/query/job.py index b43dad3..f9bf06b 100644 --- a/py12306/query/job.py +++ b/py12306/query/job.py @@ -159,11 +159,7 @@ class Job: """ if response.status_code != 200: QueryLog.print_query_error(response.reason, response.status_code) - try: - result_data = response.json().get('data', {}) - result = result_data.get('result', []) - except: - pass # TODO + result = response.json().get('data.result') return result if result else False def is_has_ticket(self, ticket_info): diff --git a/py12306/query/query.py b/py12306/query/query.py index 0cfda03..2febbc5 100644 --- a/py12306/query/query.py +++ b/py12306/query/query.py @@ -1,9 +1,9 @@ import threading -from requests_html import HTMLSession from py12306.helpers.app import app_available_check from py12306.helpers.func import * +from py12306.helpers.request import Request from py12306.log.query_log import QueryLog from py12306.query.job import Job @@ -21,7 +21,7 @@ class Query: def __init__(self): self.interval = init_interval_by_number(config.QUERY_INTERVAL) - self.session = HTMLSession() + self.session = Request() @classmethod def run(cls): diff --git a/py12306/user/job.py b/py12306/user/job.py index a9e52ff..31cc40c 100644 --- a/py12306/user/job.py +++ b/py12306/user/job.py @@ -178,10 +178,9 @@ class UserJob: def get_user_info(self): response = self.session.get(API_USER_INFO.get('url')) result = response.json() - user_data = result.get('data') - if user_data.get('userDTO') and user_data['userDTO'].get('loginUserDTO'): - user_data = user_data['userDTO']['loginUserDTO'] - self.update_user_info({**user_data, **{'user_name': user_data['name']}}) + user_data = result.get('data.userDTO.loginUserDTO') + if user_data: + self.update_user_info({**user_data, **{'user_name': user_data.get('name')}}) return True return None @@ -201,8 +200,8 @@ class UserJob: if self.passengers: return self.passengers response = self.session.post(API_USER_PASSENGERS) result = response.json() - if result.get('data') and result.get('data').get('normal_passengers'): - self.passengers = result.get('data').get('normal_passengers') + if result.get('data.normal_passengers'): + self.passengers = result.get('data.normal_passengers') return self.passengers else: UserLog.add_quick_log(