405 lines
18 KiB
Python
405 lines
18 KiB
Python
# -*- coding:utf-8 -*-
|
|
import threading
|
|
from requestUtil import myRequests
|
|
import weChatUtil
|
|
from bs4 import BeautifulSoup
|
|
import re
|
|
import json
|
|
import time
|
|
import datetime
|
|
import chaojiying
|
|
import randomStr
|
|
import log
|
|
|
|
class tuiPiao(threading.Thread):
|
|
def __init__(self, setting):
|
|
threading.Thread.__init__(self)
|
|
self.setting=setting
|
|
self.userName = setting.get("userName")
|
|
self.userPwd = setting.get("userPwd")
|
|
self.idCustomer = setting.get("idCustomer")
|
|
self.goodsid = setting.get("goodsid")
|
|
self.blocks = setting.get("blocks")
|
|
self.block = None
|
|
self.playDay = setting.get("playDay")
|
|
self.ticketTotal = int(setting.get("ticketTotal"))
|
|
self.Delivery = str(setting.get("Delivery"))
|
|
self.sleepTime = int(setting.get("sleepTime"))/1000
|
|
self.idTime = setting.get("idTime")
|
|
self.idHall = setting.get("idHall")
|
|
self.startTime = setting.get("startTime")
|
|
self.etcFee = {"EtcFees": "69#1000^", "EtcFeeAmount": 1000}
|
|
self.captchaInfo={
|
|
"key": '',
|
|
"result": ''
|
|
}
|
|
self.req = myRequests()
|
|
|
|
def __login(self):
|
|
time.sleep(self.sleepTime)
|
|
Referer = "https://www.yes24.com/Templates/FTLogin.aspx"
|
|
log.debug("{}开始执行登陆".format(self.userName))
|
|
loginData = {
|
|
"SMemberID": self.userName,
|
|
"SMemberPassword": self.userPwd,
|
|
"LoginType": "",
|
|
"AutoLogin": 1,
|
|
"FBLoginSub$ReturnParams": "",
|
|
"FBLoginSub$ReturnURL": "",
|
|
"RefererUrl": "http://ticket.yes24.com/",
|
|
"LoginIDSave": "N",
|
|
"FBLoginSub$NaverCode": "",
|
|
"FBLoginSub$NaverState": "",
|
|
"FBLoginSub$Facebook": ""
|
|
}
|
|
r=self.req.post("https://www.yes24.com/Templates/FTLogin.aspx", postData=loginData,postReferer=Referer)
|
|
if r[0:20].find('<script')<0:
|
|
raise Exception("登陆失败")
|
|
|
|
def __logout(self):
|
|
time.sleep(self.sleepTime)
|
|
self.req.get("https://www.yes24.com/Templates/FTLogout.aspx?ReturnURL=http://ticket.yes24.com")
|
|
log.info("{}退出{}日抢票".format(self.userName, self.playDay))
|
|
|
|
|
|
|
|
def __getServerTime(self):
|
|
time.sleep(self.sleepTime)
|
|
return self.req.getServerTime("http://ticket.yes24.com")
|
|
|
|
|
|
def __waiteToStart(self):
|
|
if not self.startTime:
|
|
return
|
|
servertime = self.__getServerTime()
|
|
servertime = datetime.datetime.strptime(
|
|
servertime, '%a, %d %b %Y %H:%M:%S GMT')+datetime.timedelta(hours=9) # 韩国在东9区
|
|
starttime = datetime.datetime.strptime(
|
|
"{} GMT".format(self.startTime), "%Y%m%d %H:%M:%S GMT") # 韩国的售票时间
|
|
offset = time.mktime(starttime.timetuple()) - \
|
|
time.mktime(servertime.timetuple())
|
|
if offset > 0:
|
|
log.info("{}时间未到。睡眠{}秒".format(self.userName, offset))
|
|
time.sleep(offset-0.3) # 考虑网络延迟 少睡0.3秒
|
|
|
|
def __waiteToLogin(self):
|
|
servertime = self.__getServerTime()
|
|
servertime = datetime.datetime.strptime(
|
|
servertime, '%a, %d %b %Y %H:%M:%S GMT')+datetime.timedelta(hours=9) # 韩国在东9区
|
|
starttime = datetime.datetime.strptime(
|
|
"{} GMT".format(self.startTime), "%Y%m%d %H:%M:%S GMT") # 韩国的售票时间
|
|
offset = time.mktime(starttime.timetuple()) - \
|
|
time.mktime(servertime.timetuple())
|
|
offset=offset-1800
|
|
if offset > 0:
|
|
log.info("{}时间未到。睡眠{}分钟后登陆".format(self.userName, offset//60))
|
|
time.sleep(offset)
|
|
|
|
def __initDeliveryInfo(self):
|
|
time.sleep(self.sleepTime)
|
|
log.info("{}开始获取delivery信息".format(self.userName))
|
|
r = self.req.post("http://ticket.yes24.com/Pages/Perf/Sale/Ajax/Perf/DeliveryUserInfo.aspx",
|
|
postReferer="http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx?IdPerf={}&IdTime=".format(self.goodsid))
|
|
soup = BeautifulSoup(r, 'lxml')
|
|
deliveryList = soup.select('input')
|
|
deliveryInfo={}
|
|
for i in deliveryList:
|
|
deliveryInfo[i.get("id")]=i.get("value")
|
|
self.deliveryInfo=deliveryInfo
|
|
|
|
|
|
|
|
def __initIdCustomer(self):
|
|
time.sleep(self.sleepTime)
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx?IdPerf={}&IdTime={}".format(self.goodsid,self.idTime)
|
|
d = {
|
|
"idTime": self.idTime,
|
|
"idHall": self.idHall,
|
|
"block": self.blocks[0],#退票比较特殊 获取ID时还没有具体的block
|
|
"stMax": 10,
|
|
"pHCardAppOpt": 0
|
|
}
|
|
r = self.req.get(
|
|
"http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleHtmlSeat.aspx", getData=d, getReferer=Referer)
|
|
begin = r.find("var IdCustomer")
|
|
end = begin+28
|
|
targetStr = r[begin:end]
|
|
idCustomer = re.sub(r"\D", "", targetStr)
|
|
log.warning("***注意***{}的idCustomer为{} 请保存".format(self.userName, self.idCustomer))
|
|
self.idCustomer= idCustomer
|
|
|
|
def __getServerTime(self):
|
|
time.sleep(self.sleepTime)
|
|
return self.req.getServerTime("http://ticket.yes24.com")
|
|
|
|
|
|
|
|
def __doLock(self,seatValue):
|
|
time.sleep(self.sleepTime)
|
|
lockData = {
|
|
"name": self.idCustomer,
|
|
"idTime": self.idTime,
|
|
"token": seatValue,
|
|
"Block": self.block
|
|
}
|
|
log.debug("{}开始对{}进行锁票".format(self.userName, seatValue))
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleHtmlSeat.aspx?idTime={}&idHall={}".format(self.idTime,self.idHall)
|
|
lockDataRes = self.req.post("http://ticket.yes24.com/OSIF/Book.asmx/Lock", postData=lockData, postReferer=Referer)
|
|
if lockDataRes.find('<int xmlns="http://tempuri.org/">0</int>') > -1:
|
|
log.debug("{}锁票成功".format(seatValue))
|
|
return True
|
|
else:
|
|
log.debug("{}锁票失败".format(seatValue))
|
|
return False
|
|
|
|
#应该可以省略
|
|
def __computeSelectedSeatTotalPrice(self,seatGrade):
|
|
time.sleep(self.sleepTime)
|
|
d = {
|
|
"pIdTime": self.idTime,
|
|
"PCntClass": seatGrade
|
|
}
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx?IdPerf={}&IdTime={}".format(self.goodsid,self.idTime)
|
|
r = self.req.post(
|
|
"http://ticket.yes24.com/Pages/Perf/Sale/Ajax/Perf/TimeSeatFlashEnd.aspx", postData=d, postReferer=Referer)
|
|
#<li class='grade'><div classbyte='T192$252$188$174'><strong class='c_name'>전석</strong> <span class='c_price'>99,000원</span><select id='selSeatClass' price='99000'><option selected value='1'>1매</option></select></div></li>
|
|
soup = BeautifulSoup(r, 'lxml')
|
|
self.classByte = soup.select('div')[0].get('classbyte')
|
|
selSeatClass = soup.select('#selSeatClass')[0]
|
|
price = int(selSeatClass.get('price'))
|
|
seatCount = int(selSeatClass.select(
|
|
'option')[0].get('value'))
|
|
TotalPrice= price*seatCount
|
|
if self.Delivery == "2" :
|
|
TotalPrice += 2800
|
|
TotalPrice += self.etcFee['EtcFeeAmount']
|
|
self.TotalPrice=TotalPrice
|
|
|
|
|
|
def __isNeedCaptcha(self):
|
|
time.sleep(self.sleepTime)
|
|
d = {
|
|
"IdPerf": self.goodsid,
|
|
"IdTime": self.idTime
|
|
}
|
|
|
|
r = self.req.get(
|
|
"http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx", getData=d)
|
|
if r.find("reCAPTCHAUse = 'Y';") > -1:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def __computeCaptchaKeyAndResult(self):
|
|
time.sleep(self.sleepTime)
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx?IdPerf={}".format(self.goodsid)
|
|
captchaKeyDataRes = self.req.postJson(
|
|
"http://ticket.yes24.com/Pages/Perf/Sale/Captcha/CaptchaImage.aspx/GetKeyCaptcha", postReferer=Referer)
|
|
captchaKey = json.loads(captchaKeyDataRes)
|
|
captchaKey = captchaKey['d']['Result']
|
|
time.sleep(self.sleepTime)
|
|
imgDataRes = self.req.getImage(
|
|
"http://ticket.yes24.com/Pages/Perf/Sale/Captcha/CaptchaImage.aspx?key="+captchaKey, getReferer=Referer)
|
|
captchaFileName = "captchas/cap_"+randomStr.getRandomStr()+".jpeg"
|
|
with open(captchaFileName, 'wb') as f:
|
|
f.write(imgDataRes)
|
|
f.close()
|
|
keyData = {
|
|
"key": captchaKey,
|
|
"result": ''
|
|
}
|
|
captchaCount=0
|
|
while True:
|
|
if captchaCount==3:
|
|
raise Exception("验证码连续三次识别失败")
|
|
cap=chaojiying.getCaptcha(captchaFileName)
|
|
if cap=='error':
|
|
captchaCount+=1
|
|
continue
|
|
else:
|
|
keyData['result']=cap
|
|
break
|
|
self.captchaInfo=keyData
|
|
|
|
def __InicisPayInfo(self):
|
|
time.sleep(self.sleepTime)
|
|
d = {
|
|
"pIdPerf": self.goodsid,
|
|
"pPrice": self.TotalPrice,
|
|
"pIdTime":self.idTime,
|
|
"pHCardAppOpt": 0,
|
|
"pIsMPoint": 0
|
|
}
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx?IdPerf={}".format(self.goodsid)
|
|
r = self.req.post(
|
|
"http://ticket.yes24.com/Pages/Perf/Sale/Ajax/Perf/InicisPayInfo.aspx", postData=d, postReferer=Referer)
|
|
soup = BeautifulSoup(r, 'lxml')
|
|
inputList = soup.select("input")
|
|
INicisPay = {}
|
|
for _input in inputList:
|
|
INicisPay[_input.get("id")]=_input.get("value")
|
|
self.INicisPay=INicisPay
|
|
|
|
def __GetBookWhole(self):
|
|
time.sleep(self.sleepTime)
|
|
d = {
|
|
"idHall": self.idHall,
|
|
"idTime": self.idTime,
|
|
"block": self.block,
|
|
"channel": 1,
|
|
"idCustomer": self.idCustomer,
|
|
"idOrg": 1
|
|
}
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleHtmlSeat.aspx?idTime={}&idHall={}".format(self.idTime,self.idHall)
|
|
r = self.req.post("http://ticket.yes24.com/OSIF/Book.asmx/GetBookWhole", postData=d, postReferer=Referer)
|
|
soup = BeautifulSoup(r, 'xml')
|
|
if soup.BlockSeat is None:
|
|
raise Exception("扫描座位信息异常")
|
|
return soup.BlockSeat.string
|
|
|
|
|
|
|
|
|
|
|
|
def __doBuy(self,seatValue,seatGrade,blockSeat):
|
|
time.sleep(self.sleepTime)
|
|
merchantData=[]
|
|
merchantData.append("ksPayMethod=001000000000")
|
|
merchantData.append("ksPrice={}".format(self.TotalPrice))
|
|
merchantData.append("ksMid={}".format(self.INicisPay["hidMid"]))
|
|
merchantData.append("ksIdPerf={}".format(self.goodsid))
|
|
merchantData.append("ksIdTime={}".format(self.idTime))
|
|
merchantData.append("ksTimeOption=2")
|
|
merchantData.append("ksIdSeat={}".format(seatValue))
|
|
merchantData.append("ksIdNonSeat={}-{}".format(self.classByte,seatGrade))
|
|
merchantData.append("ksSeatCnt=1")
|
|
merchantData.append("ksUserMail={}".format(self.INicisPay['hidBuyeremail']))
|
|
merchantData.append("ksUserName={}".format(self.INicisPay['hidBuyername']))
|
|
merchantData.append("ksUserTel={}".format(self.INicisPay['hidBuyertel']))
|
|
merchantData.append("ksUserMobile={}".format(self.INicisPay['hidBuyermobile']))
|
|
merchantData.append("ksGoodsName=공연티켓상품")
|
|
merchantData.append("recaptcha_challenge_field=")
|
|
merchantData.append("recaptcha_response_field=")
|
|
merchantData.append("ksEmergencyName={}".format(self.INicisPay['hidBuyername']))
|
|
merchantData.append("ksEmergencyTel={}".format(self.INicisPay['hidBuyertel']))
|
|
merchantData.append("ksEmergencyMail={}".format(self.INicisPay['hidBuyeremail']))
|
|
merchantData.append("ksEtcFee={}".format(self.etcFee['EtcFees']))
|
|
merchantData.append("ksEtcValidTicketCnt=1")
|
|
merchantData.append("ksPayBank=50")
|
|
merchantData.append("ksIsYesPointYN=N")
|
|
merchantData.append("captchaText={}".format(self.captchaInfo['result']))
|
|
merchantData.append("captchaKey={}".format(self.captchaInfo['key']))
|
|
|
|
|
|
|
|
if self.Delivery == "2" :
|
|
merchantData.append("ksDelivery=8%232800")
|
|
merchantData.append("ksReceiver={}".format(self.INicisPay['hidBuyername']))
|
|
merchantData.append("ksTel={}".format(self.INicisPay['hidBuyertel']))
|
|
merchantData.append("ksZipCode={}".format(self.deliveryInfo['LUAddr_ZipCode1']))
|
|
merchantData.append("ksAddress={} {}".format(self.deliveryInfo['LUAddr_Address1'],self.deliveryInfo['LUAddr_Address2']))
|
|
|
|
d = {
|
|
"merchantData": "&".join(merchantData),
|
|
"version": "1.0",
|
|
"currency": "WON",
|
|
"goodsname": "공연티켓상품",
|
|
"mid": self.INicisPay['hidMid'],
|
|
"oid": self.INicisPay['hidOId'],
|
|
"price": self.TotalPrice,
|
|
"buyername": self.INicisPay['hidBuyername'],
|
|
"buyertel": self.INicisPay['hidBuyertel'],
|
|
"buyeremail": self.INicisPay['hidBuyeremail'],
|
|
"timestamp": self.INicisPay['hidTimestamp'],
|
|
"signature": self.INicisPay['hidSignature'],
|
|
"returnUrl": "http://ticket.yes24.com/Pages/InicisNewPG/INIStdPayResponse.aspx",
|
|
"closeUrl": "http://ticket.yes24.com/Pages/InicisNewPG/INIStdPayClose.aspx?t=pay",
|
|
"mKey": self.INicisPay['hidMKey'],
|
|
"gopaymethod": "Card",
|
|
"payViewType": "overlay",
|
|
"quotabase": "2:3:4:5:6:7:8:9:10:11:12",
|
|
"acceptmethod": "HPP(1):no_receipt:va_receipt:vbanknoreg(0):below1000:popreturn:usespcp:cardpoint"
|
|
}
|
|
Referer = "http://ticket.yes24.com/Pages/Perf/Sale/PerfSaleProcess.aspx?IdPerf={}&IdTime={}".format(self.goodsid,self.idTime)
|
|
r = self.req.post("http://ticket.yes24.com/Pages/InicisNewPG/INIStdPayResponse.aspx", postData=d, postReferer=Referer)
|
|
|
|
if r.find("parent.fts_ProcessSaleFail('');") > 0:
|
|
sBegin = r.find("fts_ProcessSaleSuccess('")
|
|
order_id = r[sBegin:(sBegin+41)]
|
|
order_id = re.sub(r"\D", "", order_id.split("'")[1])
|
|
BankAccountInfoData = {
|
|
"pIdOrder": order_id
|
|
}
|
|
time.sleep(self.sleepTime)
|
|
self.req.post("http://ticket.yes24.com/Pages/Perf/Sale/Ajax/Perf/BankAccountInfo.aspx",
|
|
postData=BankAccountInfoData, postReferer=Referer)
|
|
|
|
log.info("{}购票成功商品id{}座位:{}".format(
|
|
self.userName, self.goodsid, blockSeat))
|
|
weChatUtil.sendTicketSuccess('yes24',self.goodsid,self.playDay,blockSeat,self.userName)
|
|
return True
|
|
else:
|
|
log.debug("{}购票失败,详见日志".format(self.userName))
|
|
return False
|
|
|
|
|
|
|
|
def __do_ticket(self):
|
|
# self.__waiteToLogin()
|
|
self.__login()
|
|
self.__waiteToStart()
|
|
if self.idCustomer is None or self.idCustomer == '':
|
|
self.__initIdCustomer()
|
|
while self.ticketTotal>0:
|
|
for self.block in self.blocks:
|
|
if self.ticketTotal<=0:
|
|
break
|
|
blockSeatStr=self.__GetBookWhole()
|
|
if blockSeatStr is None or blockSeatStr=="":
|
|
log.info("区域{}无座位".format(self.block))
|
|
time.sleep(self.sleepTime)
|
|
continue
|
|
blockSeatList = blockSeatStr.split("^")
|
|
for blockSeat in blockSeatList:
|
|
if self.ticketTotal<=0:
|
|
break
|
|
if blockSeat is None or blockSeat == "":
|
|
continue
|
|
_temp = blockSeat.split('@')
|
|
seatValue = _temp[0]
|
|
seatGrade = _temp[-1]
|
|
if self.__doLock(seatValue) is False:
|
|
continue
|
|
self.__computeSelectedSeatTotalPrice(seatGrade)
|
|
if self.__isNeedCaptcha() is True:
|
|
tryCount=0
|
|
while True:
|
|
if tryCount==3:
|
|
raise Exception("连续三次尝试购票失败")
|
|
self.__computeCaptchaKeyAndResult()
|
|
if tryCount==0:
|
|
self.__initDeliveryInfo()
|
|
self.__InicisPayInfo()#获取一次即可
|
|
ok=self.__doBuy(seatValue,seatGrade,blockSeat)
|
|
if ok is False:
|
|
tryCount+=1
|
|
continue
|
|
else:
|
|
self.ticketTotal-=1
|
|
break
|
|
else:
|
|
self.__initDeliveryInfo()
|
|
self.__InicisPayInfo()
|
|
ok=self.__doBuy(seatValue,seatGrade,blockSeat)
|
|
if ok is True:
|
|
self.ticketTotal-=1#无验证码的 不重试
|
|
self.__logout()
|
|
|
|
|
|
def run(self):
|
|
log.debug("开始用账号{}进行抢票.账号配置详情===>{}".format(self.userName,self.setting))
|
|
try:
|
|
self.__do_ticket()
|
|
except Exception as err:
|
|
log.error("账号{}抢票出现异常情况====> {}".format(self.userName ,err))
|
|
log.info("账号{}结束抢票".format(self.userName)) |