Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Fix issue606 #677

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### 12306 购票小助手
#### python版本
- [ ] 2.7.10 - 2.7.15
- [x] 3.6 - 3.7.4
- [x] 3.7 - 3.7.4
- [ ] 2.7.9

#### 已有功能
Expand Down
2 changes: 1 addition & 1 deletion TickerConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,4 @@
MIN_TIME = 1

# 软件版本
RE_VERSION = "1.2.004"
RE_VERSION = "1.2.005"
5 changes: 4 additions & 1 deletion Update.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,7 @@

- 2019.09.18更新
- 修改下单问题
- 优化车次打印
- 优化车次打印

- 2019.12.30更新
- 使用python3.7协程加速cdn测试过程
67 changes: 29 additions & 38 deletions agency/cdn_utils.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
# encoding=utf8
import datetime
import operator
import os
import requests
from config import urlConf
import threading
import asyncio
from concurrent.futures import ThreadPoolExecutor
from config.urlConf import urls

from myUrllib.httpUtils import HTTPClient

cdn_list = []
finish_count = 0


class CDNProxy(threading.Thread):
def __init__(self, cdns):
super().__init__()
self.cdns = cdns
self.urlConf = urlConf.urls
self.httpClint = requests
self.city_list = []
self.timeout = 5
def calculate_rtt_blocking(cdn):
global finish_count
http = HTTPClient(0)
url = urls["loginInitCdn"]
http._cdn = cdn
rtt_dict = {"rtt": 9999}
http.send(url, elapsed=rtt_dict)
rtt = rtt_dict["rtt"]
finish_count += 1
# 过滤逻辑为当前cdn响应值小于1000毫秒
if rtt < 2000:
print(f"ip:{cdn},延迟:{rtt},已测试个数: {finish_count}")
return {"ip": cdn, "time": rtt}
else:
print(f"ip:{cdn},延迟:N/A,已测试个数: {finish_count}")
return None

def run(self):
for cdn in self.cdns:
http = HTTPClient(0)
url = urls["loginInitCdn"]
http._cdn = cdn.replace("\n", "")
start_time = datetime.datetime.now()
rep = http.send(url)
retTime = (datetime.datetime.now() - start_time).microseconds / 1000
if rep and "message" not in rep and retTime < 3000:
if cdn.replace("\n", "") not in cdn_list: # 如果有重复的cdn,则放弃加入
print(f"加入cdn: {cdn}")
cdn_list.append({"ip": cdn.replace("\n", ""), "time": retTime})

async def calculate_all_rtt_async(cdns, loop, executor):
done, _ = await asyncio.wait(fs=[loop.run_in_executor(executor, calculate_rtt_blocking, cdn) for cdn in cdns],
return_when=asyncio.ALL_COMPLETED)
cdn_list = [task.result() for task in done if task.exception() is None and task.result() is not None]
return cdn_list


def open_cdn_file(cdnFile):
Expand All @@ -52,7 +52,7 @@ def open_cdn_file(cdnFile):
return cdn


def sortCdn():
def sortCdn(cdn_list):
"""
对cdn进行排序
:return:
Expand All @@ -73,20 +73,11 @@ def filterCdn():
:return:
"""
cdns = open_cdn_file("cdn_list")
cdnss = [cdns[i:i + 50] for i in range(0, len(cdns), 50)]
cdnThread = []
for cdn in cdnss:
t = CDNProxy(cdn)
cdnThread.append(t)
for cdn_t in cdnThread:
cdn_t.start()

for cdn_j in cdnThread:
cdn_j.join()

loop = asyncio.get_event_loop()
cdn_list = loop.run_until_complete(calculate_all_rtt_async(cdns, loop, ThreadPoolExecutor()))
print(f"当前有效cdn个数为: {len(cdn_list)}")
if cdn_list:
ips = sortCdn()
ips = sortCdn(cdn_list)
path = os.path.join(os.path.dirname(__file__), f'../filter_cdn_list')
f = open(path, "a+")
f.seek(0)
Expand Down
30 changes: 30 additions & 0 deletions config/StatusCode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from enum import Enum


class StatusCode(Enum):
"""
程序返回状态码
name: 状态名
value: 状态值
description: 状态描述
"""
OK = 0, u"正常"
RetryTimeHasReachedMaxValue = 101, u"重试次数达到上限"
CdnListEmpty = 102, u"cdn列表为空"
UnknownError = 999, u"未知错误"

def __new__(cls, *args, **kwargs):
obj = object.__new__(cls)
obj._value_ = args[0]
return obj

def __init__(self, _: str, description: str = None):
self._description_ = description

def __str__(self):
return str(self.value)

# description is read-only
@property
def description(self):
return self._description_
2 changes: 0 additions & 2 deletions config/TicketEnmu.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ class ticket(object):
QUERY_C = u"查询到有余票,尝试提交订单"
QUERY_IN_BLACK_LIST = u"该车次{} 正在被关小黑屋,跳过此车次"

SUCCESS_CODE = 000000
FAIL_CODE = 999999
AUTO_SUBMIT_ORDER_REQUEST_C = u"提交订单成功"
AUTO_SUBMIT_ORDER_REQUEST_F = u"提交订单失败,重新刷票中"
AUTO_SUBMIT_NEED_CODE = u"需要验证码"
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
build:
context: .
dockerfile: ./Dockerfile37
image: ticket:v1.2.004
image: ticket:v1.2.005
environment:
- PYTHONUNBUFFERED=1
- CAPTCHALOCAL=1
Expand Down
21 changes: 18 additions & 3 deletions init/select_ticket_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
import time
import TickerConfig
import wrapcache
from agency.cdn_utils import CDNProxy, open_cdn_file
from agency.cdn_utils import open_cdn_file
from config import urlConf, configCommon
from config.TicketEnmu import ticket
from config.configCommon import seat_conf_2, seat_conf
from config.getCookie import getDrvicesID
from config.StatusCode import StatusCode
from init.login import GoLogin
from inter.AutoSubmitOrderRequest import autoSubmitOrderRequest
from inter.ChechFace import chechFace
Expand Down Expand Up @@ -60,8 +61,8 @@ def get_ticket_info():
print(u"*" * 50)
print(f"检查当前版本为: {TickerConfig.RE_VERSION}")
version = sys.version.split(" ")[0]
print(u"检查当前python版本为:{},目前版本只支持3.6以上".format(version))
if version < "3.6.0":
print(u"检查当前python版本为:{},目前版本只支持3.7以上".format(version))
if version < "3.7.0":
raise Exception
print(u"12306刷票小助手,最后更新于2019.09.18,请勿作为商业用途,交流群号:"
u" 1群:286271084(已满)\n"
Expand Down Expand Up @@ -116,6 +117,9 @@ def call_login(self, auth=False):
configCommon.checkSleepTime(self) # 防止网上启动晚上到点休眠
self.login.go_login()

def read_cdn_list(self):
self.cdn_list = open_cdn_file("filter_cdn_list")

def main(self):
l = liftTicketInit(self)
l.reqLiftTicketInit()
Expand Down Expand Up @@ -161,6 +165,17 @@ def main(self):
ticke_peoples_num=len(TickerConfig.TICKET_PEOPLES),
)
queryResult = q.sendQuery()

# 检查结果状态
status_value = queryResult.get("code", StatusCode.OK.value)
if status_value != StatusCode.OK.value:
# 状态出错
if status_value == StatusCode.CdnListEmpty.value:
from agency.cdn_utils import filterCdn
filterCdn()
self.read_cdn_list()
continue

# 查询接口
if queryResult.get("status"):
train_no = queryResult.get("train_no", "")
Expand Down
3 changes: 2 additions & 1 deletion inter/GetPassengerDTOs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json

from config.TicketEnmu import ticket
from config.StatusCode import StatusCode
from myException.PassengerUserException import PassengerUserException
import wrapcache
import TickerConfig
Expand Down Expand Up @@ -116,7 +117,7 @@ def getPassengerTicketStrListAndOldPassengerStr(self, secretStr, secretList):
"passengerTicketStrList": set_type + "," + ",".join(passengerTicketStrList),
"passengerTicketStrByAfterLate": "".join(tickers),
"oldPassengerStr": "".join(oldPassengerStr),
"code": ticket.SUCCESS_CODE,
"code": StatusCode.OK.value,
"set_type": set_type,
"status": True,
"user_info": user_info,
Expand Down
9 changes: 7 additions & 2 deletions inter/Query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from config.TicketEnmu import ticket
from myUrllib.httpUtils import HTTPClient
from config.configCommon import seat_conf_2
from config.StatusCode import StatusCode
import TickerConfig


Expand Down Expand Up @@ -70,6 +71,10 @@ def sendQuery(self):
select_url["req_url"] = select_url["req_url"].format(station_date, self.from_station, self.to_station,
self.session.queryUrl)
station_ticket = self.httpClint.send(select_url)
status_code = station_ticket.get("code", StatusCode.OK.value)
if status_code != StatusCode.OK.value:
return station_ticket

value = station_ticket.get("data", "")
if not value:
print(u'{0}-{1} 车次坐席查询为空,查询url: https://kyfw.12306.cn{2}, 可以手动查询是否有票'.format(
Expand Down Expand Up @@ -146,7 +151,7 @@ def sendQuery(self):
"seat": seat,
"leftTicket": leftTicket,
"train_location": train_location,
"code": ticket.SUCCESS_CODE,
"code": StatusCode.OK.value,
"is_more_ticket_num": is_more_ticket_num,
"cdn": self.httpClint.cdn,
"status": True,
Expand Down Expand Up @@ -182,7 +187,7 @@ def sendQuery(self):
else:
print(u"车次配置信息有误,或者返回数据异常,请检查 {}".format(station_ticket))
self.session.flag = False
return {"code": ticket.FAIL_CODE, "status": False, "cdn": self.httpClint.cdn, }
return {"code": StatusCode.UnknownError.value, "status": False, "cdn": self.httpClint.cdn, }


if __name__ == "__main__":
Expand Down
15 changes: 12 additions & 3 deletions myUrllib/httpUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import TickerConfig
from agency.agency_tools import proxy
from config import logger
from config.StatusCode import StatusCode


def _set_header_default():
Expand Down Expand Up @@ -123,7 +124,7 @@ def cdn(self):
def cdn(self, cdn):
self._cdn = cdn

def send(self, urls, data=None, **kwargs):
def send(self, urls, data=None, elapsed=None, **kwargs):
"""send request to url.If response 200,return response, else return None."""
allow_redirects = False
is_logger = urls.get("is_logger", False)
Expand All @@ -132,7 +133,8 @@ def send(self, urls, data=None, **kwargs):
s_time = urls.get("s_time", 0)
is_cdn = urls.get("is_cdn", False)
is_test_cdn = urls.get("is_test_cdn", False)
error_data = {"code": 99999, "message": u"重试次数达到上限"}
retry_error = StatusCode.RetryTimeHasReachedMaxValue
error_data = {"code": retry_error.value, "message": retry_error.description}
if data:
method = "post"
self.setHeaders({"Content-Length": "{0}".format(len(data))})
Expand Down Expand Up @@ -173,6 +175,8 @@ def send(self, urls, data=None, **kwargs):
allow_redirects=allow_redirects,
verify=False,
**kwargs)
if is_test_cdn:
elapsed["rtt"] = round(response.elapsed.total_seconds() * 1000, 3)
if response.status_code == 200 or response.status_code == 302:
if urls.get("not_decode", False):
return response.content
Expand All @@ -193,7 +197,12 @@ def send(self, urls, data=None, **kwargs):
u"url: {} 返回参数为空".format(urls["req_url"]))
if self.cdnList:
# 如果下单或者登陆出现cdn 302的情况,立马切换cdn
url_host = self.cdnList.pop(random.randint(0, 4))
if len(self.cdnList) >= 5:
url_host = self.cdnList.pop(random.randint(0, 4))
else:
cdn_empty_error = StatusCode.CdnListEmpty
print(f"出错:{cdn_empty_error.description}")
return {"code": cdn_empty_error.value, "message": cdn_empty_error.description}
continue
else:
sleep(urls["re_time"])
Expand Down