当前位置:   article > 正文

基于selenium的大麦网自动抢票脚本实测(新年第一篇)_大麦脚本会被检测到吗

大麦脚本会被检测到吗

本次试验对象为大麦网 2020上海林俊杰演唱会 ,针对大家关心的能不能抢到票的问题。先在开头说明结论

1.以目前的脚本情况,在不经过大量优化的情况下 寄托于python的速度去和票贩子抢票是 不可能的

2.这个项目不太适用于纯小白,因为selenium坑太多,但博主这种有一些前端基础的半小白还是收获很多。

3.本项目来自于知乎用户Oliver0047,地点https://zhuanlan.zhihu.com/p/56697166,由于时间原因,大麦网的前端格式发生了很大的变化,因此主体内容需要根据情况更改,但原理一致。

4.在线求  两张 林俊杰上海演唱会门票,单价780以下的联系我!!!

—————————————以下进入正文内容—————————————————

1. selenium安装与使用

selenium是一种打包好的web自动化库,用来模拟用户操作,本项目的内容重点在于模拟流程,即使用爬虫和自动化手段,将你平时的登陆,点击按钮等行为转成py代码操作。

首先默认python装好,假如使用的是pycharm直接在setting里找到selenium安装即可

除此之外你还要安装浏览器对应的driver,推荐使用Chrome和火狐,如果是谷歌就要安装Chromedriver

并且根据浏览器版本的不同,Chromedriver的版本也不同,在设置中找到版本号并与之对应下载。

并将其与脚本的py文件放到同一目录下。

安装完成后首先导入库,并将

  1. from selenium.webdriver.common.action_chains import ActionChains
  2. from selenium import webdriver
  3. from time import sleep
  4. import time
  5. import pickle
  6. import os
  7. from selenium.webdriver.common.by import By
  8. from selenium.webdriver.support.ui import WebDriverWait
  9. from selenium.webdriver.support import expected_conditions as EC
  10. #本次加载登陆的网页
  11. login_url = "https://passport.damai.cn/login?ru=https%3A%2F%2Fwww.damai.cn%2F"
  12. #使用此命令访问,在完整代码中我会把下面三行注销掉
  13. driver = webdriver.Chrome()
  14. driver.maximize_window()
  15. driver.get(login_url)

运行结果如下图所示,当发现上方出现Chrome正在受自动软件的控制时,说明已经启用成功,我们将从这个页面登陆。

 

2. 平台账号登陆

原作者将所有内容都写在一个class下面,非常清晰,值得学习。

  1. class Concert(object):
  2. def __init__(self, name, date, price, place, real_name, method=1):
  3. self.name = name # 歌星
  4. self.date = date # 日期序号优先级,比如,如果第二个时间可行,就选第二个
  5. self.price = price # 票价序号优先级,道理同上
  6. self.place = place # 地点
  7. self.status = 0 # 状态,表示如今进行到何种程度
  8. self.login_method = 1 # {0:模拟登录,1:Cookie登录}自行选择登录方式
  9. self.real_name = real_name # 实名者序号

针对账号自动化登陆,原作者给出了两个方法:一是模拟账号输入登陆二是使用cookie登陆

一是模拟账号点击登陆;二是使用cookie登陆。

一号方法经过测试现在已经不能使用,原因是大麦网的反爬能力,当其检测到浏览器下在selenium下时,就无法登陆到内部,当然网上也有一些绕过检测的教程 https://www.jianshu.com/p/4dd2737a3048 有兴趣的同学可以学习下。

二号方法,是通过 自己扫码登陆 记录下的cookie信息实现自动化登陆,记录的cookie信息可能一段时间就需要重新生成一下。

  1. #生成cookie的函数
  2. def get_cookie(self):
  3. self.driver.get(login_url)
  4. #切换到iframe内容
  5. self.driver.switch_to.frame("alibaba-login-box");
  6. #点击扫码登陆
  7. self.driver.find_element_by_xpath("/html/body/div[1]/div/div[1]/div[3]").click()
  8. #开始扫码
  9. sleep(10)
  10. #保存cookie
  11. pickle.dump(self.driver.get_cookies(), open("cookies.pkl", "wb"))
  12. print("###Cookie保存成功###")

其中有个小点,登陆页面里还内置了另外一个页面。所以不能直接全网页的方式爬取,需要先使用self.driver.switch_to.frame定位到内置页面,再定位到扫码处。

  1. #载入函数
  2. def set_cookie(self):
  3. try:
  4. cookies = pickle.load(open("cookies.pkl", "rb")) # 载入cookie
  5. for cookie in cookies:
  6. cookie_dict = {
  7. 'domain': '.damai.cn', # 必须有,不然就是假登录
  8. 'name': cookie.get('name'),
  9. 'value': cookie.get('value'),
  10. "expires": "",
  11. 'path': '/',
  12. 'httpOnly': False,
  13. 'HostOnly': False,
  14. 'Secure': False}
  15. #加载cookie
  16. self.driver.add_cookie(cookie_dict)

登陆代码,只需获取和载入两步。

  1. #登陆函数
  2. def login(self):
  3. if self.login_method == 1:
  4. if not os.path.exists('cookies.pkl'): # 如果不存在cookie.pkl,就获取一下
  5. self.get_cookie()
  6. else:
  7. self.driver.get(login_url)
  8. self.set_cookie()

3. 演唱会选择和购票

接下来,主要就是爬虫相关的内容,具体的话就是定位到这个搜索框,填入内容,然后点击啥的,没啥特别好说的,关于如何定位,建议用Xpath全定位和class定位,至于页面结构 利用CTRL+SHIFT+C,移动到想使用的组件处就能定位其位置,在F12里右键copy Xpath 即可得到位置,有时候一个div里有多个相同的class,那就需要使用层级定位,先定到父级再定到下面的子级。

  1. def enter_concert(self):
  2. print('###打开浏览器,进入大麦网###')
  3. self.driver = webdriver.Chrome() # 谷歌浏览器
  4. self.driver.maximize_window()
  5. self.login() # 先登录再说
  6. self.driver.get("https://www.damai.cn/")
  7. try:
  8. #主要是验证是否登入成功,用户名检查
  9. locator = (By.XPATH,"/html/body/div[2]/div/div[3]/div[1]/a[2]/div")
  10. #显示等待3s直到验证成功
  11. element = WebDriverWait(self.driver,3).until(EC.text_to_be_present_in_element(locator, self.usr_name))
  12. self.status = 1
  13. print("###登录成功###")
  14. except Exception as d:
  15. print(d)
  16. if self.status == 1:
  17. self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[4]/input')[0].send_keys(self.name) # 搜索栏输入歌星
  18. self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[4]/div[1]')[0].click() # 点击搜索
  19. self.driver.find_elements_by_xpath('/html/body/div[2]/div[2]/div[1]/div[1]/div[2]/div/div/div/span')[0].click()
  20. # 嫌麻烦就对原版代码进行了一些删减
  21. # 获取所有可能演唱会
  22. titles = []
  23. links = []
  24. # 注释的代码表示用图形界面手动选择演唱会,可以自行体会
  25. # root = Tk()
  26. # root.title("选择演唱会")
  27. # v = IntVar()
  28. # v.set(1)
  29. self.choose_result = 0
  30. # def selection():
  31. # self.choose_result=v.get()
  32. # root.destroy()
  33. # 此处的意义为定位到购买页面的url,和演唱会所处的地点是否符合预期
  34. lists = self.driver.find_elements_by_class_name('items__txt__title')
  35. i=0
  36. for li in lists:
  37. word_link=li
  38. #titles.append(word_link.text)
  39. print(word_link)
  40. temp_s = word_link.get_attribute('innerHTML').find('href')+8
  41. temp_e = word_link.get_attribute('innerHTML').find('target')-2
  42. links.append(word_link.get_attribute('innerHTML')[temp_s:temp_e])
  43. print('运行完成')
  44. #确定地点
  45. didian=li.find_element_by_tag_name('a').text[-3:-1]
  46. print(didian)
  47. if didian != self.place:
  48. print('地址错误')
  49. else:
  50. print('地址正确')
  51. self.url = "https:" + links[i]
  52. i=i+1
  53. self.driver.get(self.url) # 载入至购买界面
  54. self.status = 2
  55. print("###选择演唱会###")

其中有个小点,find_element_by_id 和 find_elements_by_id是不一样的,后者需要加个[0]。

同样下一步是在购买页面,选择票价,时间,然后点击购买,对于票价我们需要判断其是否有货,有货的先选,其余的按照价格排序选择,时间的话根据个人弹性选择。

  1. def choose_ticket(self):
  2. if self.status == 2:
  3. self.num = 1 # 第一次尝试
  4. time_start = time.time()
  5. while self.driver.title.find('确认订单') == -1: # 如果跳转到了订单结算界面就算这部成功了
  6. if self.num != 1: # 如果前一次失败了,那就刷新界面重新开始
  7. self.status = 2
  8. self.driver.get(self.url)
  9. try:
  10. element = WebDriverWait(self.driver, 3).until(EC.presence_of_element_located((By.CLASS_NAME,"select_right_list_item")))
  11. print('通过验证')
  12. except Exception as e:
  13. print(e)
  14. self.driver.find_elements_by_class_name('perform__order__select perform__order__select__performs')
  15. #datelist= self.driver.find_elements_by_class_name('select_right_list_item')
  16. print('成功找到')
  17. datelist=self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[4]/div[3]/div[2]/div')[0].find_elements_by_tag_name('div')
  18. print(len(datelist))
  19. #j = datelist[0].get_attribute('span')
  20. #通过票务判断是否先买
  21. info=self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[4]/div[3]/div[2]/div')[0].find_elements_by_class_name('presell')
  22. #判断是否有票
  23. for i in range(len(info)):
  24. INFO=info[i].text
  25. if INFO == '有票':
  26. datelist[i].click()
  27. break
  28. else:
  29. datelist[1].click()
  30. #datelist[2].click()
  31. sleep(1)
  32. print('点击成功')
  33. # 根据优先级选择一个可行票价,
  34. # 先买不缺货的,再买最便宜的
  35. pricepalce=self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[4]/div[5]/div[2]/div')[0].find_elements_by_class_name('select_right_list_item')
  36. pricelist=self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[4]/div[5]/div[2]/div')[0].find_elements_by_class_name('notticket')
  37. for i in range(len(pricelist)):
  38. PH=pricelist[i].text
  39. if PH == '缺货登记':
  40. continue
  41. else:
  42. pricepalce[i].click()
  43. break
  44. print("###选择演唱会时间与票价###")
  45. #点击购买按钮
  46. cart = self.driver.find_elements_by_xpath('/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[4]')[0]
  47. sleep(3)
  48. cart.find_elements_by_class_name('buybtn')[0].click()
  49. sleep(3)
  50. self.status = 3
  51. print('已完成跳转')
  52. try:
  53. element = WebDriverWait(self.driver, 3).until(EC.title_is(u'确认订单'))
  54. print("确认定位")
  55. except:
  56. print('###暂无余票###')
  57. #关闭
  58. #self.driver.quit()
  59. time_end = time.time()
  60. print("###经过%d轮奋斗,共耗时%f秒,抢票成功!请确认订单信息###" % (self.num - 1, round(time_end - time_start, 3)))
注意点  find_element_by_link_text似乎只能定位<a></a>里的元素。

最后,只需在提交订单页面,判断一下,然后选择乘客,地址 点击提交订单即可进入到支付宝页面。

  1. def check_order(self):
  2. if self.status in [3, 4, 5]:
  3. print('###开始确认订单###')
  4. self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div/div[2]/div[2]/div[1]/div/label/span[1]/input').click()
  5. print('进入到此阶段')
  6. self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div/div[9]/button').click() # 同意以上协议并提交订单
  7. try:
  8. element = WebDriverWait(self.driver, 5).until(EC.title_contains('支付'))
  9. self.status = 6
  10. print('###成功提交订单,请手动支付###')
  11. sleep(100)
  12. except:
  13. print('###提交订单失败,请查看问题###')
  14. def finish(self):
  15. self.driver.quit()

由于林俊杰已经没票了,所以实验的时候改成张杰即可。。。最后是挨个调用函数。

  1. if __name__ == '__main__':
  2. try:
  3. #在此更改想买的演唱会信息
  4. con = Concert('张杰', [1], [2], '苏州', 1) # 具体如果填写请查看类中的初始化函数
  5. con.enter_concert()
  6. con.choose_ticket()
  7. con.check_order()
  8. except Exception as e:
  9. print(e)
  10. con.finish()
 —————————————小结—————————————————

单次买票时间大约是15.8s,总的来说比较尴尬,我觉得如果要实际使用,还是应该采用 js脚本模拟操作的方式,直接作为插件启用,这样1.不容易被反爬,2.节省了很多的不必要的加载时间   具体的操作以后有机会再更新吧,

原始代码可在csdn的资源里下载,不过没啥必要,太乱了没整理。

今年毕业,下半年忙着干活都没空写,6月份后会将三年论文内容慢慢公布,虽然我作为一个土木专业学生做的都是些半吊子玩意,不过应该是挺好的入门玩具,祝大家新年快乐。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/196613
推荐阅读
相关标签
  

闽ICP备14008679号