rename old

This commit is contained in:
Jalin
2019-05-14 13:21:25 +08:00
parent 8d4ea3066a
commit f49b081bc5
77 changed files with 11 additions and 254 deletions

View File

356
old_py12306/query/job.py Normal file
View File

@@ -0,0 +1,356 @@
import sys
from datetime import timedelta
from py12306.app import app_available_check
from py12306.cluster.cluster import Cluster
from py12306.config import Config
from py12306.helpers.api import LEFT_TICKETS
from py12306.helpers.station import Station
from py12306.helpers.type import OrderSeatType, SeatType
from py12306.log.query_log import QueryLog
from py12306.helpers.func import *
from py12306.log.user_log import UserLog
from py12306.order.order import Order
from py12306.user.user import User
from py12306.helpers.event import Event
class Job:
"""
查询任务
"""
id = 0
is_alive = True
job_name = None
left_dates = []
left_date = None
stations = []
left_station = ''
arrive_station = ''
left_station_code = ''
arrive_station_code = ''
from_time = timedelta(hours=0)
to_time = timedelta(hours=24)
account_key = 0
allow_seats = []
current_seat = None
current_seat_name = ''
current_order_seat = None
allow_train_numbers = []
except_train_numbers = []
members = []
member_num = 0
member_num_take = 0 # 最终提交的人数
passengers = []
allow_less_member = False
retry_time = 3
interval = {}
interval_additional = 0
interval_additional_max = 5
query = None
cluster = None
ticket_info = {}
is_cdn = False
query_time_out = 3
INDEX_TICKET_NUM = 11
INDEX_TRAIN_NUMBER = 3
INDEX_TRAIN_NO = 2
INDEX_LEFT_DATE = 13
INDEX_LEFT_STATION = 6 # 4 5 始发 终点
INDEX_ARRIVE_STATION = 7
INDEX_ORDER_TEXT = 1 # 下单文字
INDEX_SECRET_STR = 0
INDEX_LEFT_TIME = 8
INDEX_ARRIVE_TIME = 9
def __init__(self, info, query):
self.cluster = Cluster()
self.query = query
self.init_data(info)
self.update_interval()
def init_data(self, info):
self.id = md5(info)
self.left_dates = info.get('left_dates')
self.stations = info.get('stations')
self.stations = [self.stations] if isinstance(self.stations, dict) else self.stations
if not self.job_name: # name 不能被修改
self.job_name = info.get('job_name',
'{} -> {}'.format(self.stations[0]['left'], self.stations[0]['arrive']))
self.account_key = str(info.get('account_key'))
self.allow_seats = info.get('seats')
self.allow_train_numbers = info.get('train_numbers')
self.except_train_numbers = info.get('except_train_numbers')
self.members = list(map(str, info.get('members')))
self.member_num = len(self.members)
self.member_num_take = self.member_num
self.allow_less_member = bool(info.get('allow_less_member'))
period = info.get('period')
if isinstance(period, dict):
if 'from' in period:
parts = period['from'].split(':')
if len(parts) == 2:
self.from_time = timedelta(
hours=int(parts[0]), seconds=int(parts[1]))
if 'to' in period:
parts = period['to'].split(':')
if len(parts) == 2:
self.to_time = timedelta(
hours=int(parts[0]), seconds=int(parts[1]))
def update_interval(self):
self.interval = self.query.interval
def run(self):
self.start()
def start(self):
"""
处理单个任务
根据日期循环查询, 展示处理时间
:param job:
:return:
"""
while True and self.is_alive:
app_available_check()
QueryLog.print_job_start(self.job_name)
for station in self.stations:
self.refresh_station(station)
for date in self.left_dates:
self.left_date = date
response = self.query_by_date(date)
self.handle_response(response)
QueryLog.add_query_time_log(time=response.elapsed.total_seconds(), is_cdn=self.is_cdn)
if not self.is_alive: return
self.safe_stay()
if is_main_thread():
QueryLog.flush(sep='\t\t', publish=False)
if not Config().QUERY_JOB_THREAD_ENABLED:
QueryLog.add_quick_log('').flush(publish=False)
break
else:
QueryLog.add_log('\n').flush(sep='\t\t', publish=False)
if Const.IS_TEST: return
def query_by_date(self, date):
"""
通过日期进行查询
:return:
"""
from py12306.helpers.cdn import Cdn
QueryLog.add_log(('\n' if not is_main_thread() else '') + QueryLog.MESSAGE_QUERY_START_BY_DATE.format(date,
self.left_station,
self.arrive_station))
url = LEFT_TICKETS.get('url').format(left_date=date, left_station=self.left_station_code,
arrive_station=self.arrive_station_code, type=self.query.api_type)
if Config.is_cdn_enabled() and Cdn().is_ready:
self.is_cdn = True
return self.query.session.cdn_request(url, timeout=self.query_time_out, allow_redirects=False)
self.is_cdn = False
return self.query.session.get(url, timeout=self.query_time_out, allow_redirects=False)
def handle_response(self, response):
"""
错误判断
余票判断
小黑屋判断
座位判断
乘车人判断
:param result:
:return:
"""
results = self.get_results(response)
if not results:
return False
for result in results:
self.ticket_info = ticket_info = result.split('|')
if not self.is_trains_number_valid(): # 车次是否有效
continue
QueryLog.add_log(QueryLog.MESSAGE_QUERY_LOG_OF_EVERY_TRAIN.format(self.get_info_of_train_number()))
if not self.is_has_ticket(ticket_info):
continue
allow_seats = self.allow_seats if self.allow_seats else list(
Config.SEAT_TYPES.values()) # 未设置 则所有可用 TODO 合法检测
self.handle_seats(allow_seats, ticket_info)
if not self.is_alive: return
def handle_seats(self, allow_seats, ticket_info):
for seat in allow_seats: # 检查座位是否有票
self.set_seat(seat)
ticket_of_seat = ticket_info[self.current_seat]
if not self.is_has_ticket_by_seat(ticket_of_seat): # 座位是否有效
continue
QueryLog.print_ticket_seat_available(left_date=self.get_info_of_left_date(),
train_number=self.get_info_of_train_number(), seat_type=seat,
rest_num=ticket_of_seat)
if not self.is_member_number_valid(ticket_of_seat): # 乘车人数是否有效
if self.allow_less_member:
self.member_num_take = int(ticket_of_seat)
QueryLog.print_ticket_num_less_than_specified(ticket_of_seat, self)
else:
QueryLog.add_quick_log(
QueryLog.MESSAGE_GIVE_UP_CHANCE_CAUSE_TICKET_NUM_LESS_THAN_SPECIFIED).flush()
continue
if Const.IS_TEST: return
# 检查完成 开始提交订单
QueryLog.print_ticket_available(left_date=self.get_info_of_left_date(),
train_number=self.get_info_of_train_number(),
rest_num=ticket_of_seat)
if User.is_empty():
QueryLog.add_quick_log(QueryLog.MESSAGE_USER_IS_EMPTY_WHEN_DO_ORDER.format(self.retry_time))
return stay_second(self.retry_time)
order_result = False
user = self.get_user()
if not user:
QueryLog.add_quick_log(QueryLog.MESSAGE_ORDER_USER_IS_EMPTY.format(self.retry_time))
return stay_second(self.retry_time)
lock_id = Cluster.KEY_LOCK_DO_ORDER + '_' + user.key
if Config().is_cluster_enabled():
if self.cluster.get_lock(lock_id, Cluster.lock_do_order_time,
{'node': self.cluster.node_name}): # 获得下单锁
order_result = self.do_order(user)
if not order_result: # 下单失败,解锁
self.cluster.release_lock(lock_id)
else:
QueryLog.add_quick_log(
QueryLog.MESSAGE_SKIP_ORDER.format(self.cluster.get_lock_info(lock_id).get('node'),
user.user_name))
stay_second(self.retry_time) # 防止过多重复
else:
order_result = self.do_order(user)
# 任务已成功 通知集群停止任务
if order_result:
Event().job_destroy({'name': self.job_name})
def do_order(self, user):
self.check_passengers()
order = Order(user=user, query=self)
return order.order()
def get_results(self, response):
"""
解析查询返回结果
:param response:
:return:
"""
if response.status_code != 200:
QueryLog.print_query_error(response.reason, response.status_code)
if self.interval_additional < self.interval_additional_max:
self.interval_additional += self.interval.get('min')
else:
self.interval_additional = 0
result = response.json().get('data.result')
return result if result else False
def is_has_ticket(self, ticket_info):
return self.get_info_of_ticket_num() == 'Y' and self.get_info_of_order_text() == '预订'
def is_has_ticket_by_seat(self, seat):
return seat != '' and seat != '' and seat != '*'
def is_trains_number_valid(self):
train_left_time = self.get_info_of_train_left_time()
time_parts = train_left_time.split(':')
left_time = timedelta(
hours=int(time_parts[0]), seconds=int(time_parts[1]))
if left_time < self.from_time or left_time > self.to_time:
return False
if self.except_train_numbers:
return self.get_info_of_train_number().upper() not in map(str.upper, self.except_train_numbers)
if self.allow_train_numbers:
return self.get_info_of_train_number().upper() in map(str.upper, self.allow_train_numbers)
return True
def is_member_number_valid(self, seat):
return seat == '' or self.member_num <= int(seat)
def destroy(self):
"""
退出任务
:return:
"""
from py12306.query.query import Query
self.is_alive = False
QueryLog.add_quick_log(QueryLog.MESSAGE_QUERY_JOB_BEING_DESTROY.format(self.job_name)).flush()
# sys.exit(1) # 无法退出线程...
# 手动移出jobs 防止单线程死循环
index = Query().jobs.index(self)
Query().jobs.pop(index)
def safe_stay(self):
origin_interval = get_interval_num(self.interval)
interval = origin_interval + self.interval_additional
QueryLog.add_stay_log(
'%s + %s' % (origin_interval, self.interval_additional) if self.interval_additional else origin_interval)
stay_second(interval)
def set_passengers(self, passengers):
UserLog.print_user_passenger_init_success(passengers)
self.passengers = passengers
def set_seat(self, seat):
self.current_seat_name = seat
self.current_seat = SeatType.dicts.get(seat)
self.current_order_seat = OrderSeatType.dicts.get(seat)
def get_user(self):
user = User.get_user(self.account_key)
# if not user.check_is_ready(): # 这里不需要检测了,后面获取乘客时已经检测过
# #
# pass
return user
def check_passengers(self):
if not self.passengers:
QueryLog.add_quick_log(QueryLog.MESSAGE_CHECK_PASSENGERS.format(self.job_name)).flush()
passengers = User.get_passenger_for_members(self.members, self.account_key)
if passengers:
self.set_passengers(passengers)
else: # 退出当前查询任务
self.destroy()
return True
def refresh_station(self, station):
self.left_station = station.get('left')
self.arrive_station = station.get('arrive')
self.left_station_code = Station.get_station_key_by_name(self.left_station)
self.arrive_station_code = Station.get_station_key_by_name(self.arrive_station)
# 提供一些便利方法
def get_info_of_left_date(self):
return self.ticket_info[self.INDEX_LEFT_DATE]
def get_info_of_ticket_num(self):
return self.ticket_info[self.INDEX_TICKET_NUM]
def get_info_of_train_number(self):
return self.ticket_info[self.INDEX_TRAIN_NUMBER]
def get_info_of_train_no(self):
return self.ticket_info[self.INDEX_TRAIN_NO]
def get_info_of_left_station(self):
return Station.get_station_name_by_key(self.ticket_info[self.INDEX_LEFT_STATION])
def get_info_of_arrive_station(self):
return Station.get_station_name_by_key(self.ticket_info[self.INDEX_ARRIVE_STATION])
def get_info_of_order_text(self):
return self.ticket_info[self.INDEX_ORDER_TEXT]
def get_info_of_secret_str(self):
return self.ticket_info[self.INDEX_SECRET_STR]
def get_info_of_train_left_time(self):
return self.ticket_info[self.INDEX_LEFT_TIME]
def get_info_of_train_arrive_time(self):
return self.ticket_info[self.INDEX_ARRIVE_TIME]

174
old_py12306/query/query.py Normal file
View File

@@ -0,0 +1,174 @@
from py12306.config import Config
from py12306.cluster.cluster import Cluster
from py12306.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
from py12306.helpers.api import API_QUERY_INIT_PAGE
@singleton
class Query:
"""
余票查询
"""
jobs = []
query_jobs = []
session = {}
# 查询间隔
interval = {}
cluster = None
is_in_thread = False
retry_time = 3
is_ready = False
api_type = None # Query api url, Current know value leftTicket/queryX | leftTicket/queryZ
def __init__(self):
self.session = Request()
self.cluster = Cluster()
self.update_query_interval()
self.update_query_jobs()
self.get_query_api_type()
def update_query_interval(self, auto=False):
self.interval = init_interval_by_number(Config().QUERY_INTERVAL)
if auto:
jobs_do(self.jobs, 'update_interval')
def update_query_jobs(self, auto=False):
self.query_jobs = Config().QUERY_JOBS
if auto:
QueryLog.add_quick_log(QueryLog.MESSAGE_JOBS_DID_CHANGED).flush()
self.refresh_jobs()
if not Config().is_slave():
jobs_do(self.jobs, 'check_passengers')
@classmethod
def run(cls):
self = cls()
app_available_check()
self.start()
pass
@classmethod
def check_before_run(cls):
self = cls()
self.init_jobs()
self.is_ready = True
def start(self):
# return # DEBUG
QueryLog.init_data()
stay_second(3)
# 多线程
while True:
if Config().QUERY_JOB_THREAD_ENABLED: # 多线程
if not self.is_in_thread:
self.is_in_thread = True
create_thread_and_run(jobs=self.jobs, callback_name='run', wait=Const.IS_TEST)
if Const.IS_TEST: return
stay_second(self.retry_time)
else:
if not self.jobs: break
self.is_in_thread = False
jobs_do(self.jobs, 'run')
if Const.IS_TEST: return
# while True:
# app_available_check()
# if Config().QUERY_JOB_THREAD_ENABLED: # 多线程
# create_thread_and_run(jobs=self.jobs, callback_name='run')
# else:
# for job in self.jobs: job.run()
# if Const.IS_TEST: return
# self.refresh_jobs() # 刷新任务
def refresh_jobs(self):
"""
更新任务
:return:
"""
allow_jobs = []
for job in self.query_jobs:
id = md5(job)
job_ins = objects_find_object_by_key_value(self.jobs, 'id', id) # [1 ,2]
if not job_ins:
job_ins = self.init_job(job)
if Config().QUERY_JOB_THREAD_ENABLED: # 多线程重新添加
create_thread_and_run(jobs=job_ins, callback_name='run', wait=Const.IS_TEST)
allow_jobs.append(job_ins)
for job in self.jobs: # 退出已删除 Job
if job not in allow_jobs: job.destroy()
QueryLog.print_init_jobs(jobs=self.jobs)
def init_jobs(self):
for job in self.query_jobs:
self.init_job(job)
QueryLog.print_init_jobs(jobs=self.jobs)
def init_job(self, job):
job = Job(info=job, query=self)
self.jobs.append(job)
return job
@classmethod
def wait_for_ready(cls):
self = cls()
if self.is_ready: return self
stay_second(self.retry_time)
return self.wait_for_ready()
@classmethod
def job_by_name(cls, name) -> Job:
self = cls()
for job in self.jobs:
if job.job_name == name: return job
return None
@classmethod
def job_by_name(cls, name) -> Job:
self = cls()
return objects_find_object_by_key_value(self.jobs, 'job_name', name)
@classmethod
def job_by_account_key(cls, account_key) -> Job:
self = cls()
return objects_find_object_by_key_value(self.jobs, 'account_key', account_key)
@classmethod
def get_query_api_type(cls):
import re
self = cls()
if self.api_type:
return self.api_type
response = self.session.get(API_QUERY_INIT_PAGE)
if response.status_code == 200:
res = re.search(r'var CLeftTicketUrl = \'(.*)\';', response.text)
try:
self.api_type = res.group(1)
except IndexError:
pass
return cls.get_query_api_type()
# def get_jobs_from_cluster(self):
# jobs = self.cluster.session.get_dict(Cluster.KEY_JOBS)
# return jobs
#
# def update_jobs_of_cluster(self):
# if config.CLUSTER_ENABLED and config.NODE_IS_MASTER:
# return self.cluster.session.set_dict(Cluster.KEY_JOBS, self.query_jobs)
#
# def refresh_jobs(self):
# if not config.CLUSTER_ENABLED: return
# jobs = self.get_jobs_from_cluster()
# if jobs != self.query_jobs:
# self.jobs = []
# self.query_jobs = jobs
# QueryLog.add_quick_log(QueryLog.MESSAGE_JOBS_DID_CHANGED).flush()
# self.init_jobs()