This commit is contained in:
375
ticket_script.py
Normal file
375
ticket_script.py
Normal file
@@ -0,0 +1,375 @@
|
||||
# coding: utf-8
|
||||
from json import loads
|
||||
from time import sleep, time
|
||||
from pickle import dump, load
|
||||
from os.path import exists
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
from selenium.common import exceptions
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
|
||||
class Concert(object):
|
||||
def __init__(self, date, session, price, real_name, nick_name, ticket_num, viewer_person, damai_url, target_url, driver_path):
|
||||
self.date = date # 日期序号
|
||||
self.session = session # 场次序号优先级
|
||||
self.price = price # 票价序号优先级
|
||||
self.real_name = real_name # 实名者序号
|
||||
self.status = 0 # 状态标记
|
||||
self.time_start = 0 # 开始时间
|
||||
self.time_end = 0 # 结束时间
|
||||
self.num = 0 # 尝试次数
|
||||
self.ticket_num = ticket_num # 购买票数
|
||||
self.viewer_person = viewer_person # 观影人序号优先级
|
||||
self.nick_name = nick_name # 用户昵称
|
||||
self.damai_url = damai_url # 大麦网官网网址
|
||||
self.target_url = target_url # 目标购票网址
|
||||
self.driver_path = driver_path # 浏览器驱动地址
|
||||
self.driver = None
|
||||
|
||||
def isClassPresent(self, item, name, ret=False):
|
||||
try:
|
||||
result = item.find_element(by=By.CLASS_NAME, value=name)
|
||||
if ret:
|
||||
return result
|
||||
else:
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
# 获取账号的cookie信息
|
||||
def get_cookie(self):
|
||||
self.driver.get(self.damai_url)
|
||||
print(u"###请点击登录###")
|
||||
self.driver.find_element(by=By.CLASS_NAME, value='login-user').click()
|
||||
while self.driver.title.find('大麦网-全球演出赛事官方购票平台') != -1: # 等待网页加载完成
|
||||
sleep(1)
|
||||
print(u"###请扫码登录###")
|
||||
while self.driver.title == '大麦登录': # 等待扫码完成
|
||||
sleep(1)
|
||||
dump(self.driver.get_cookies(), open("cookies.pkl", "wb"))
|
||||
print(u"###Cookie保存成功###")
|
||||
|
||||
def set_cookie(self):
|
||||
try:
|
||||
cookies = load(open("cookies.pkl", "rb")) # 载入cookie
|
||||
for cookie in cookies:
|
||||
cookie_dict = {
|
||||
'domain': '.damai.cn', # 必须有,不然就是假登录
|
||||
'name': cookie.get('name'),
|
||||
'value': cookie.get('value'),
|
||||
"expires": "",
|
||||
'path': '/',
|
||||
'httpOnly': False,
|
||||
'HostOnly': False,
|
||||
'Secure': False}
|
||||
self.driver.add_cookie(cookie_dict)
|
||||
print(u'###载入Cookie###')
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def login(self):
|
||||
print(u'###开始登录###')
|
||||
self.driver.get(self.target_url)
|
||||
WebDriverWait(self.driver, 10, 0.1).until(EC.title_contains('商品详情'))
|
||||
self.set_cookie()
|
||||
|
||||
def enter_concert(self):
|
||||
print(u'###打开浏览器,进入大麦网###')
|
||||
if not exists('cookies.pkl'): # 如果不存在cookie.pkl,就获取一下
|
||||
self.driver = webdriver.Chrome(executable_path=self.driver_path)
|
||||
self.get_cookie()
|
||||
print(u'###成功获取Cookie,重启浏览器###')
|
||||
self.driver.quit()
|
||||
|
||||
options = webdriver.ChromeOptions()
|
||||
# 禁止图片、js、css加载
|
||||
prefs = {"profile.managed_default_content_settings.images": 2,
|
||||
"profile.managed_default_content_settings.javascript": 1,
|
||||
'permissions.default.stylesheet': 2}
|
||||
mobile_emulation = {"deviceName": "Nexus 6"}
|
||||
options.add_experimental_option("prefs", prefs)
|
||||
options.add_experimental_option("mobileEmulation", mobile_emulation)
|
||||
# 就是这一行告诉chrome去掉了webdriver痕迹,令navigator.webdriver=false,极其关键
|
||||
options.add_argument("--disable-blink-features=AutomationControlled")
|
||||
|
||||
# 更换等待策略为不等待浏览器加载完全就进行下一步操作
|
||||
capa = DesiredCapabilities.CHROME
|
||||
# normal, eager, none
|
||||
capa["pageLoadStrategy"] = "eager"
|
||||
self.driver = webdriver.Chrome(
|
||||
executable_path=self.driver_path, options=options, desired_capabilities=capa)
|
||||
# 登录到具体抢购页面
|
||||
self.login()
|
||||
self.driver.refresh()
|
||||
# try:
|
||||
# # 等待nickname出现
|
||||
# locator = (By.XPATH, "/html/body/div[1]/div/div[3]/div[1]/a[2]/div")
|
||||
# WebDriverWait(self.driver, 5, 0.3).until(EC.text_to_be_present_in_element(locator, self.nick_name))
|
||||
# self.status = 1
|
||||
# print(u"###登录成功###")
|
||||
# self.time_start = time()
|
||||
# except:
|
||||
# self.status = 0
|
||||
# self.driver.quit()
|
||||
# raise Exception(u"***错误:登录失败,请删除cookie后重试***")
|
||||
|
||||
def click_util(self, btn, locator):
|
||||
while True:
|
||||
btn.click()
|
||||
try:
|
||||
return WebDriverWait(self.driver, 1, 0.1).until(EC.presence_of_element_located(locator))
|
||||
except:
|
||||
continue
|
||||
|
||||
# 实现购买函数
|
||||
|
||||
def choose_ticket(self):
|
||||
print(u"###进入抢票界面###")
|
||||
# 如果跳转到了确认界面就算这步成功了,否则继续执行此步
|
||||
while self.driver.title.find('订单确认') == -1:
|
||||
self.num += 1 # 尝试次数加1
|
||||
|
||||
if con.driver.current_url.find("buy.damai.cn") != -1:
|
||||
break
|
||||
|
||||
# 确认页面刷新成功
|
||||
try:
|
||||
box = WebDriverWait(self.driver, 3, 0.1).until(
|
||||
EC.presence_of_element_located((By.ID, 'app')))
|
||||
except:
|
||||
raise Exception(u"***Error: 页面刷新出错***")
|
||||
|
||||
try:
|
||||
realname_popup = box.find_elements(
|
||||
by=By.XPATH, value="//div[@class='realname-popup']") # 寻找实名身份遮罩
|
||||
if len(realname_popup) != 0:
|
||||
known_button = realname_popup[0].find_element(
|
||||
by=By.XPATH, value="//div[@class='operate']//div[@class='button']")
|
||||
known_button[0].click()
|
||||
except:
|
||||
raise Exception(u"***Error: 实名制遮罩关闭失败***")
|
||||
|
||||
try:
|
||||
buybutton = box.find_element(by=By.CLASS_NAME, value='buy__button')
|
||||
sleep(0.5)
|
||||
buybutton_text: str = buybutton.text
|
||||
except Exception as e:
|
||||
raise Exception(f"***Error: buybutton 位置找不到***: {e}")
|
||||
|
||||
if "即将开抢" in buybutton_text:
|
||||
self.status = 2
|
||||
raise Exception(u"---尚未开售,刷新等待---")
|
||||
|
||||
if "缺货" in buybutton_text:
|
||||
raise Exception("---已经缺货了---")
|
||||
|
||||
sleep(0.1)
|
||||
buybutton.click()
|
||||
box = WebDriverWait(self.driver, 2, 0.1).until(
|
||||
EC.presence_of_element_located((By.CSS_SELECTOR, '.sku-pop-wrapper')))
|
||||
|
||||
try:
|
||||
# 日期选择
|
||||
toBeClicks = []
|
||||
try:
|
||||
date = WebDriverWait(self.driver, 2, 0.1).until(
|
||||
EC.presence_of_element_located((By.CLASS_NAME, 'bui-dm-sku-calendar')))
|
||||
except Exception as e:
|
||||
date = None
|
||||
if date is not None:
|
||||
date_list = date.find_elements(
|
||||
by=By.CLASS_NAME, value='bui-calendar-day-box')
|
||||
for i in self.date:
|
||||
j: WebElement = date_list[i-1]
|
||||
toBeClicks.append(j)
|
||||
break
|
||||
for i in toBeClicks:
|
||||
i.click()
|
||||
sleep(0.05)
|
||||
|
||||
# 选定场次
|
||||
session = WebDriverWait(self.driver, 2, 0.1).until(
|
||||
EC.presence_of_element_located((By.CLASS_NAME, 'sku-times-card'))) # 日期、场次和票档进行定位
|
||||
session_list = session.find_elements(
|
||||
by=By.CLASS_NAME, value='bui-dm-sku-card-item')
|
||||
|
||||
toBeClicks = []
|
||||
for i in self.session: # 根据优先级选择一个可行场次
|
||||
if i > len(session_list):
|
||||
i = len(session_list)
|
||||
j: WebElement = session_list[i-1]
|
||||
# TODO 不确定已满的场次带的是什么Tag
|
||||
|
||||
k = self.isClassPresent(j, 'item-tag', True)
|
||||
if k: # 如果找到了带presell的类
|
||||
if k.text == '无票':
|
||||
continue
|
||||
elif k.text == '预售':
|
||||
toBeClicks.append(j)
|
||||
break
|
||||
elif k.text == '惠':
|
||||
toBeClicks.append(j)
|
||||
break
|
||||
else:
|
||||
toBeClicks.append(j)
|
||||
break
|
||||
|
||||
# 多场次的场要先选择场次才会出现票档
|
||||
for i in toBeClicks:
|
||||
i.click()
|
||||
sleep(0.05)
|
||||
|
||||
# 选定票档
|
||||
toBeClicks = []
|
||||
price = WebDriverWait(self.driver, 2, 0.1).until(
|
||||
EC.presence_of_element_located((By.CLASS_NAME, 'sku-tickets-card'))) # 日期、场次和票档进行定位
|
||||
|
||||
price_list = price.find_elements(
|
||||
by=By.CLASS_NAME, value='bui-dm-sku-card-item') # 选定票档
|
||||
# print('可选票档数量为:{}'.format(len(price_list)))
|
||||
for i in self.price:
|
||||
if i > len(price_list):
|
||||
i = len(price_list)
|
||||
j = price_list[i-1]
|
||||
# k = j.find_element(by=By.CLASS_NAME, value='item-tag')
|
||||
k = self.isClassPresent(j, 'item-tag', True)
|
||||
if k: # 存在notticket代表存在缺货登记,跳过
|
||||
continue
|
||||
else:
|
||||
toBeClicks.append(j)
|
||||
break
|
||||
|
||||
for i in toBeClicks:
|
||||
i.click()
|
||||
sleep(0.1)
|
||||
|
||||
buybutton = box.find_element(
|
||||
by=By.CLASS_NAME, value='sku-footer-buy-button')
|
||||
sleep(1.0)
|
||||
buybutton_text = buybutton.text
|
||||
if buybutton_text == "":
|
||||
raise Exception(u"***Error: 提交票档按钮文字获取为空,适当调整 sleep 时间***")
|
||||
|
||||
|
||||
try:
|
||||
WebDriverWait(self.driver, 2, 0.1).until(
|
||||
EC.presence_of_element_located((By.CLASS_NAME, 'bui-dm-sku-counter')))
|
||||
except:
|
||||
raise Exception(u"***购票按钮未开始***")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"***Error: 选择日期or场次or票档不成功***: {e}")
|
||||
|
||||
try:
|
||||
ticket_num_up = box.find_element(
|
||||
by=By.CLASS_NAME, value='plus-enable')
|
||||
except:
|
||||
if buybutton_text == "选座购买": # 选座购买没有增减票数键
|
||||
buybutton.click()
|
||||
self.status = 5
|
||||
print(u"###请自行选择位置和票价###")
|
||||
break
|
||||
elif buybutton_text == "提交缺货登记":
|
||||
raise Exception(u'###票已被抢完,持续捡漏中...或请关闭程序并手动提交缺货登记###')
|
||||
else:
|
||||
raise Exception(u"***Error: ticket_num_up 位置找不到***")
|
||||
|
||||
if buybutton_text == "立即预订" or buybutton_text == "立即购买" or buybutton_text == '确定':
|
||||
for i in range(self.ticket_num-1): # 设置增加票数
|
||||
ticket_num_up.click()
|
||||
buybutton.click()
|
||||
self.status = 4
|
||||
WebDriverWait(self.driver, 3, 0.1).until(
|
||||
EC.title_contains("确认"))
|
||||
break
|
||||
else:
|
||||
raise Exception(f"未定义按钮:{buybutton_text}")
|
||||
|
||||
def check_order(self):
|
||||
if self.status in [3, 4, 5]:
|
||||
# 选择观影人
|
||||
toBeClicks = []
|
||||
WebDriverWait(self.driver, 5, 0.1).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//*[@id="dmViewerBlock_DmViewerBlock"]/div[2]/div/div')))
|
||||
people = self.driver.find_elements(
|
||||
By.XPATH, '//*[@id="dmViewerBlock_DmViewerBlock"]/div[2]/div/div')
|
||||
sleep(0.2)
|
||||
|
||||
for i in self.viewer_person:
|
||||
if i > len(people):
|
||||
break
|
||||
j = people[i-1]
|
||||
j.click()
|
||||
sleep(0.05)
|
||||
|
||||
WebDriverWait(self.driver, 5, 0.1).until(
|
||||
EC.presence_of_element_located((By.XPATH, '//*[@id="dmOrderSubmitBlock_DmOrderSubmitBlock"]/div[2]/div/div[2]/div[3]/div[2]')))
|
||||
comfirmBtn = self.driver.find_element(
|
||||
By.XPATH, '//*[@id="dmOrderSubmitBlock_DmOrderSubmitBlock"]/div[2]/div/div[2]/div[3]/div[2]')
|
||||
sleep(0.5)
|
||||
comfirmBtn.click()
|
||||
# 判断title是不是支付宝
|
||||
print(u"###等待跳转到--付款界面--,可自行刷新,若长期不跳转可选择-- CRTL+C --重新抢票###")
|
||||
|
||||
while True:
|
||||
try:
|
||||
WebDriverWait(self.driver, 4, 0.1).until(
|
||||
EC.title_contains('支付宝'))
|
||||
except:
|
||||
# 通过人工判断是否继续等待支付宝跳转界面
|
||||
c ="""
|
||||
|
||||
等待输入指示:
|
||||
1.抢票成功
|
||||
2.抢票失败,未知原因没跳转到支付宝界面,进入下一轮抢票
|
||||
"""
|
||||
|
||||
step = input('等待跳转到支付宝页面,请输入:')
|
||||
if step == '1':
|
||||
# 成功
|
||||
break
|
||||
# elif step == '2':
|
||||
# # 有遮罩弹窗,包括各种错误提示
|
||||
# try:
|
||||
# confirm = WebDriverWait(self.driver, 3, 0.1).until(
|
||||
# EC.presence_of_element_located((By.ID, 'confirm')))
|
||||
# except:
|
||||
# raise Exception(u"***Error: 页面刷新出错***")
|
||||
else:
|
||||
raise Exception(u'***Error: 长期跳转不到付款界面***')
|
||||
break
|
||||
|
||||
self.status = 6
|
||||
print(u'###成功提交订单,请手动支付###')
|
||||
self.time_end = time()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
with open('./config.json', 'r', encoding='utf-8') as f:
|
||||
config = loads(f.read())
|
||||
# params: 场次优先级,票价优先级,实名者序号, 用户昵称, 购买票数, 官网网址, 目标网址, 浏览器驱动地址
|
||||
con = Concert(config['date'], config['sess'], config['price'], config['real_name'], config['nick_name'],
|
||||
config['ticket_num'], config['viewer_person'], config['damai_url'], config['target_url'], config['driver_path'])
|
||||
con.enter_concert() # 进入到具体抢购页面
|
||||
except Exception as e:
|
||||
print(e)
|
||||
exit(1)
|
||||
|
||||
while True:
|
||||
try:
|
||||
con.choose_ticket()
|
||||
con.check_order()
|
||||
except Exception as e:
|
||||
con.driver.get(con.target_url)
|
||||
print(e)
|
||||
continue
|
||||
|
||||
if con.status == 6:
|
||||
print(u"###经过%d轮奋斗,共耗时%.1f秒,抢票成功!请确认订单信息###" %
|
||||
(con.num, round(con.time_end-con.time_start, 3)))
|
||||
break
|
||||
Reference in New Issue
Block a user