├── run.bat ├── user_info.txt ├── README.md └── ticket.py /run.bat: -------------------------------------------------------------------------------- 1 | python ticket.py -------------------------------------------------------------------------------- /user_info.txt: -------------------------------------------------------------------------------- 1 | userID 2 | userPassword 3 | nickName 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Concert_Ticket 2 | 大麦网演唱会抢票程序 3 | * Python3.6 4 | * Selenium 5 | ## 准备工作 6 | * 下载anaconda,对应python3.6 7 | * 下载火狐浏览器(推荐)以及对应的geckodriver.exe,并将此exe转移到python.exe旁边。谷歌浏览器的操作类似。 8 | * 打开命令提示窗口,输入pip install selenium 9 | * 如果提示其他包未安装,请用相同的方式下载 10 | * 在user_info.txt中输入提示的信息 11 | * 在主函数中按要求输入演唱会信息 12 | * 运行代码,在这个过程中注意观察串口输出 13 | 14 | ## (注:自从我在知乎分享了代码后,很多人关注了我的代码,自己也很荣幸,哈哈。但是好多人运行我的代码却出现了这样那样的问题,我刚开始也很纳闷,明明自己当初试了好几个演唱会都可以,为什么现在有这么多问题,上段时间自己正好有事自己也没太关注这个,所以我也只是站在原来研究的基础上进行回答,很多回答可能并没有点到关键上。最近正好有人QQ找我,我好好看了下,发现原来是大麦网的页面源码变了,变了很多很多,所以我原来的代码差不多失效大半了。我的方法太过于依赖页面源码的元素ID、xpath、class_name等等标签,所以应付不了这种变化。em,更新代码这件事,我最近先放放。不过大致思路是对的,大家在我的代码基础上修改下又是可以用的啦~) 15 | -------------------------------------------------------------------------------- /ticket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #autor:Oliver0047 3 | from selenium.webdriver.common.action_chains import ActionChains 4 | from selenium import webdriver 5 | from time import sleep 6 | import re 7 | from tkinter import * 8 | import time 9 | import pickle 10 | import os 11 | from selenium.webdriver.common.by import By 12 | from selenium.webdriver.support.ui import WebDriverWait 13 | from selenium.webdriver.support import expected_conditions as EC 14 | 15 | damai_url="https://www.damai.cn/" 16 | login_url="https://passport.damai.cn/login?ru=https%3A%2F%2Fwww.damai.cn%2F" 17 | class Concert(object): 18 | def __init__(self,name,date,price,place,real_name,method=1): 19 | self.name=name#歌星 20 | self.date=date#日期序号优先级,比如,如果第二个时间可行,就选第二个,不然就选其他,最终只选一个 21 | self.price=price#票价序号优先级,道理同上 22 | self.place=place#地点 23 | self.status=0#状态,表示如今进行到何种程度 24 | self.login_method=method#{0:模拟登录,1:Cookie登录}自行选择登录方式 25 | self.real_name=real_name#实名者序号 26 | with open('./user_info.txt','r') as f:#读入用户名与密码和昵称 27 | self.uid=f.readline().strip('\n').strip('\r\n').strip() 28 | self.upw=f.readline().strip('\n').strip('\r\n').strip() 29 | self.usr_name=f.readline().strip('\n').strip('\r\n').strip() 30 | 31 | def get_cookie(self): 32 | self.driver.get(damai_url) 33 | print("###请点击登录###") 34 | while self.driver.title.find('大麦网-全球演出赛事官方购票平台')!=-1: 35 | sleep(1) 36 | print("###请扫码登录###") 37 | while self.driver.title=='中文登录': 38 | sleep(1) 39 | print("###扫码成功###") 40 | pickle.dump(self.driver.get_cookies(), open("cookies.pkl", "wb")) 41 | print("###Cookie保存成功###") 42 | 43 | def set_cookie(self): 44 | try: 45 | cookies = pickle.load(open("cookies.pkl", "rb"))#载入cookie 46 | for cookie in cookies: 47 | cookie_dict = { 48 | 'domain':'.damai.cn',#必须有,不然就是假登录 49 | 'name': cookie.get('name'), 50 | 'value': cookie.get('value'), 51 | "expires": "", 52 | 'path': '/', 53 | 'httpOnly': False, 54 | 'HostOnly': False, 55 | 'Secure': False} 56 | self.driver.add_cookie(cookie_dict) 57 | print('###载入Cookie###') 58 | except Exception as e: 59 | print(e) 60 | 61 | def login(self): 62 | if self.login_method==0: 63 | self.driver.get(login_url)#载入登录界面 64 | print('###开始登录###') 65 | try: 66 | element = WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.ID, 'alibaba-login-box'))) 67 | except: 68 | print('###定位不到登录框###') 69 | self.driver.switch_to.frame('alibaba-login-box')#里面这个是iframe的id 70 | self.driver.find_element_by_id('fm-login-id').send_keys(self.uid) 71 | self.driver.find_element_by_id('fm-login-password').send_keys(self.upw) 72 | self.driver.find_element_by_tag_name("button").click() 73 | try: 74 | ActionChains(self.driver).click_and_hold(self.driver.find_element_by_id('nc_1_n1z')).perform()#按住滑块不动 75 | ActionChains(self.driver).move_by_offset(xoffset=250, yoffset=0).perform()#直接到终点,可能速度太快,会被系统判错误操作 76 | for i in range(2): 77 | ActionChains(self.driver).move_by_offset(xoffset=10, yoffset=0).perform()#再慢慢滑两步 78 | sleep(0.1) 79 | ActionChains(self.driver).release().perform()#松开点击 80 | sleep(1)#滑完了之后稍等下,让系统判断完毕 81 | self.driver.find_element_by_tag_name("button").click() 82 | self.driver.switch_to.default_content() 83 | except Exception as e: 84 | print(e) 85 | elif self.login_method==1: 86 | if not os.path.exists('cookies.pkl'):#如果不存在cookie.pkl,就获取一下 87 | self.get_cookie() 88 | else: 89 | self.driver.get(damai_url) 90 | self.set_cookie() 91 | 92 | def enter_concert(self): 93 | print('###打开浏览器,进入大麦网###') 94 | self.driver=webdriver.Firefox()#默认火狐浏览器 95 | self.driver.maximize_window() 96 | self.login()#先登录再说 97 | self.driver.refresh() 98 | try: 99 | locator = (By.XPATH, "/html/body/div[1]/div/div[3]/div[1]/a[2]/div") 100 | element = WebDriverWait(self.driver, 3).until(EC.text_to_be_present_in_element(locator,self.usr_name)) 101 | self.status=1 102 | print("###登录成功###") 103 | except: 104 | self.status=0 105 | print("###登录失败###") 106 | if self.status==1: 107 | self.driver.find_elements_by_xpath('/html/body/div[1]/div/div[4]/input')[0].send_keys(self.name)#搜索栏输入歌星 108 | self.driver.find_elements_by_xpath('/html/body/div[1]/div/div[4]/div[1]')[0].click()#点击搜索 109 | try: 110 | element = WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.ID, 'category_filter_id'))) 111 | kinds=element.find_elements_by_tag_name('li')#选择演唱会类别 112 | except Exception as e: 113 | print(e) 114 | for k in kinds: 115 | if k.text=='演唱会': 116 | k.click() 117 | break 118 | lists=self.driver.find_elements_by_id('content_list')[0].find_elements_by_tag_name('li')#获取所有可能演唱会 119 | titles=[] 120 | links=[] 121 | #注释的代码表示用图形界面手动选择演唱会,可以自行体会 122 | # root = Tk() 123 | # root.title("选择演唱会") 124 | # v = IntVar() 125 | # v.set(1) 126 | self.choose_result=0 127 | # def selection(): 128 | # self.choose_result=v.get() 129 | # root.destroy() 130 | for li in lists: 131 | word_link=li.find_element_by_tag_name('h3') 132 | titles.append(word_link.text) 133 | temp_s=word_link.get_attribute('innerHTML').find('href')+6 134 | temp_e=word_link.get_attribute('innerHTML').find('target')-2 135 | links.append(word_link.get_attribute('innerHTML')[temp_s:temp_e]) 136 | if li.find_element_by_tag_name('h3').text.find(self.place)!=-1:#选择地点正确的演唱会 137 | self.choose_result=len(titles) 138 | break 139 | # b = Radiobutton(root,text = titles[-1],variable = v,value = len(titles),command=selection) 140 | # b.pack(anchor = W) 141 | # root.mainloop() 142 | # while self.choose_result==0: 143 | # sleep(1) 144 | self.url="https:"+links[self.choose_result-1] 145 | self.driver.get(self.url)#载入至购买界面 146 | self.status=2 147 | print("###选择演唱会###") 148 | 149 | def choose_ticket(self): 150 | if self.status==2: 151 | self.num=1#第一次尝试 152 | time_start=time.time() 153 | while self.driver.title.find('订单结算')==-1:#如果跳转到了订单结算界面就算这部成功了 154 | if self.num!=1:#如果前一次失败了,那就刷新界面重新开始 155 | self.status=2 156 | self.driver.get(self.url) 157 | try: 158 | element = WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.ID, "performList"))) 159 | except Exception as e: 160 | print(e) 161 | datelist=element.find_elements_by_tag_name('li') 162 | for i in self.date:#根据优先级选择一个可行日期 163 | j=datelist[i-1].get_attribute('class') 164 | if j=='itm': 165 | datelist[i-1].click() 166 | sleep(1) 167 | break 168 | elif j=='itm itm-sel': 169 | break 170 | elif j=='itm itm-oos': 171 | continue 172 | pricelist=self.driver.find_element_by_id("priceList").find_elements_by_tag_name('li')#根据优先级选择一个可行票价 173 | for i in self.price: 174 | j=pricelist[i-1].get_attribute('class') 175 | if j=='itm': 176 | pricelist[i-1].click() 177 | sleep(2) 178 | break 179 | elif j=='itm itm-sel': 180 | break 181 | elif j=='itm itm-oos': 182 | continue 183 | print("###选择演唱会时间与票价###") 184 | cart=self.driver.find_element_by_id('cartList') 185 | try:#各种按钮的点击 186 | try: 187 | cart.find_element_by_class_name('ops').find_element_by_link_text("立即预定").click() 188 | self.status=3 189 | except: 190 | cart.find_element_by_class_name('ops').find_element_by_link_text("立即购买").click() 191 | self.status=4 192 | except: 193 | cart.find_element_by_class_name('ops').find_element_by_link_text("选座购买").click() 194 | self.status=5 195 | self.num+=1 196 | try: 197 | element = WebDriverWait(self.driver, 3).until(EC.title_contains('订单结算')) 198 | except: 199 | print('###未跳转到订单结算界面###') 200 | time_end=time.time() 201 | print("###经过%d轮奋斗,共耗时%f秒,抢票成功!请确认订单信息###"%(self.num-1,round(time_end-time_start,3))) 202 | 203 | def check_order(self): 204 | if self.status in [3,4,5]: 205 | print('###开始确认订单###') 206 | print('###默认购票人信息###') 207 | rn_button=self.driver.find_elements_by_xpath('/html/body/div[3]/div[3]/div[2]/div[2]/div/a') 208 | if len(rn_button)==1:#如果要求实名制 209 | print('###选择实名制信息###') 210 | rn_button[0].click() 211 | #选择实名信息 212 | try: 213 | tb = WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.XPATH, '/html/body/div[3]/div[3]/div[12]/div'))) 214 | except Exception as e: 215 | print("###实名信息选择框没有显示###") 216 | print(e) 217 | lb=tb.find_elements_by_tag_name('label')[self.real_name]#选择第self.real_name个实名者 218 | lb.find_elements_by_tag_name('td')[0].click() 219 | tb.find_element_by_class_name('one-btn').click() 220 | print('###默认选择付款方式###') 221 | print('###确认商品清单###') 222 | rn_button=self.driver.find_elements_by_xpath('/html/body/div[3]/div[3]/div[3]/div[2]/div[2]/div/div/h2/a[1]') 223 | if len(rn_button)==1:#如果要求实名制 224 | print('###选择购票人信息###') 225 | rn_button[0].click() 226 | #选择实名信息 227 | try: 228 | tb = WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.XPATH, '/html/body/div[3]/div[3]/div[13]/div'))) 229 | except Exception as e: 230 | print("###实名信息选择框没有显示###") 231 | print(e) 232 | lb=tb.find_elements_by_tag_name('label')[self.real_name]#选择第self.real_name个实名者 233 | lb.find_elements_by_tag_name('td')[0].click() 234 | tb.find_element_by_class_name('one-btn').click() 235 | print('###不选择订单优惠###') 236 | print('###请在付款完成后下载大麦APP进入订单详情页申请开具###') 237 | self.driver.find_element_by_id('orderConfirmSubmit').click()#同意以上协议并提交订单 238 | try: 239 | element = WebDriverWait(self.driver, 5).until(EC.title_contains('支付')) 240 | self.status=6 241 | print('###成功提交订单,请手动支付###') 242 | except: 243 | print('###提交订单失败,请查看问题###') 244 | 245 | def finish(self): 246 | self.driver.quit() 247 | 248 | 249 | if __name__ == '__main__': 250 | try: 251 | con=Concert('张杰',[1],[2],'上海',1)#具体如果填写请查看类中的初始化函数 252 | con.enter_concert() 253 | con.choose_ticket() 254 | con.check_order() 255 | except Exception as e: 256 | print(e) 257 | con.finish() 258 | --------------------------------------------------------------------------------