自从我在知乎分享了代码后,很多人关注了我的代码,自己也很荣幸,哈哈。但是好多人运行我的代码却出现了这样那样的问题,我刚开始也很纳闷,明明自己当初试了好几个演唱会都可以,为什么现在有这么多问题,上段时间自己正好有事自己也没太关注这个,所以我也只是站在原来研究的基础上进行回答,很多回答可能并没有点到关键上。最近正好有人QQ找我,我好好看了下,发现原来是大麦网的页面源码变了,变了很多很多,所以我原来的代码差不多失效大半了。我的方法太过于依赖页面源码的元素ID、xpath、class_name等等标签,所以应付不了这种变化。em,更新代码这件事,我最近先放放。不过大致思路是对的,大家在我的代码基础上修改下又是可以用的啦~

完整代码

大麦网上,像林俊杰这类歌星的演唱会的门票,往往时间一到就瞬间售完,网速慢了或者手慢了都只能对着屏幕叹息了。所以有时候你愿意花这钱也不一定买得到看的机会。但是其实这个问题并不难解决,手速慢?那可以让代码模拟操作。网速慢?那可以把程序放到阿里服务器上运行。而这其中的关键就是编写一个抢票程序!

要说抢票,网上关于12306的抢票程序多如繁星,但是关于大麦网的还真不多,但是核心要义都是一样的:模拟。其实这种程序一定程度上类似于爬虫,所以马上可以想到Python的urllib、BeautifulSoup、selenium、splinter等模块。最初为了方便,偷懒使用了splinter,封装得相当简单,但是功能不多,文档不全,网上资料少,写到后来,感觉主用这个模块解决不了自动登录问题,所以放弃转成了selenium。接下来,我将从账号登录、选择演唱会、购票、确认订单四个方面依次讲解思路。

1.账号登录

既然是抢票,我希望给定演唱会信息后一运行程序就能够马上进行,不需要其他的人工干预。这就要求我们的账号能够再抢票前自动登录绑定。其实要实现自动登录,由两种方式:①Cookie ②模拟登录。使用Cookie(原理:如果服务器端的$_COOKIE函数中记录了你的Cookie,那就可以直接调用登录,如果没有就需要人工登录了,登录了之后,二次访问界面就能把自己的Cookie保存到$_COOKIE函数)这种方式其实很方便,保存在本地,要用了调用一下,相当于带了一块出入皇宫的令牌,不用每次进入都验身啥的。但是Cookie涉及过期问题,同时是存在较大安全隐患。所以我决定玩玩另一个方式——模拟登录。

大麦网的登录界面如下图:

我们需要做的就是填充账号密码,填完了之后点击登录,其实人工登录到这里就完成了,但是用代码填充完内容后点击登录,惊喜就出现了,一个滑块突然出现在按钮上方,并红字提示需要将滑块滑到最右边才能点击登录。所以接下来我们除了要完成内容填充、按钮点击外,还要完成滑块的滑动。(我试过模拟人工填写信息来躲避这个滑块,但是奈何骗不过去,只好直面问题了)

说实话,因为经验不足,内容填充花了我好几个小时,原因就是这个登录框其实是一个网页,封装在iframe标签中,作为外部网页的子界面,不管我用哪个工具包,用id、class、xpath等哪个方式,都无法定位到账号框和密码框。当我注意到iframe这个坑,才搞明白要用下面这句话来定位到子页面。

self.driver.switch_to_frame('alibaba-login-box')#里面这个是iframe的id

接下来就简单了,定位到两个框和一个按钮,然后点击一下按钮,保证滑块乖乖地出现。

self.driver.find_element_by_id('fm-login-id').send_keys(self.uid)
self.driver.find_element_by_id('fm-login-password').send_keys(self.upw)
self.driver.find_element_by_tag_name("button").click()

然后,想想自己是怎么滑动滑块的,在滑块处按下左键不动,移动鼠标向右,到最右边,然后松手。要模拟这个过程就需要用到ActionChains了。

ActionChains(self.driver).click_and_hold(self.driver.find_element_by_id('nc_1_n1z')).perform()#按住滑块不动
ActionChains(self.driver).move_by_offset(xoffset=250, yoffset=0).perform()#直接到终点,可能速度太快,会被系统判错误操作(这也是我不用drag_and_drop这个函数的原因),快到终点时停下
for i in range(2):ActionChains(self.driver).move_by_offset(xoffset=10, yoffset=0).perform()#再慢慢滑两步sleep(0.1)
sleep(0.5)#滑完了之后稍等下,让系统判断完毕
ActionChains(self.driver).release().perform()#松开点击
self.driver.find_element_by_tag_name("button").click()#点击登录

结束了之后,记得加上下面这句话,从iframe切换出去。

self.driver.switch_to_default_content()

但是,过了两天,我发现这个代码在我的电脑上失效了(别人那里好像是可以的)。。。不知道什么原因,selenium打开浏览器之后,无论是代码滑动滑块还是我手动滑动滑块,都是被判无效的,但是我自己打开浏览器操作是可以的。我觉得我可能是被针对了(以前也碰到过两次),感觉没有办法解决之后,我开始着手用其他方式解决自动登录——Cookie。其实吧,这个是套路,直接贴代码就行了,有一个地方需要注意注意注意,重要的事情说三遍,因为网上的教程在这个地方很多都是错的,从而导致我当时代码调得差点自闭了。

    def get_cookie(self):self.driver.get(damai_url)print("###请点击登录###")while self.driver.title.find('大麦网-全球演出赛事官方购票平台')!=-1:sleep(1)print("###请扫码登录###")while self.driver.title=='中文登录':sleep(1)print("###扫码成功###")pickle.dump(self.driver.get_cookies(), open("cookies.pkl", "wb")) print("###Cookie保存成功###")def set_cookie(self):try:cookies = pickle.load(open("cookies.pkl", "rb"))#载入cookiefor 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('###载入Cookie###')except Exception as e:print(e)

之后只要如此调用就可以了。

  if not os.path.exists('cookies.pkl'):#如果不存在cookie.pkl,就获取一下self.get_cookie()else:self.driver.get(damai_url)self.set_cookie()

2.选择演唱会

接下来就很直接了。

self.driver.find_elements_by_xpath('/html/body/div[1]/div/div[4]/input')[0].send_keys(self.name)#找到搜索栏,填入演唱会歌星的名字
self.driver.find_elements_by_xpath('/html/body/div[1]/div/div[4]/div[1]')[0].click()#点击旁边的搜索按钮

kinds=self.driver.find_element_by_id('category_filter_id').find_elements_by_tag_name('li')#选择演唱会类别
for k in kinds:if k.text=='演唱会':k.click()break
lists=self.driver.find_elements_by_id('content_list')[0].find_elements_by_tag_name('li')#获取所有可能演唱会
titles=[]
links=[]
self.choose_result=0
for li in lists:word_link=li.find_element_by_tag_name('h3')titles.append(word_link.text)temp_s=word_link.get_attribute('innerHTML').find('href')+6temp_e=word_link.get_attribute('innerHTML').find('target')-2links.append(word_link.get_attribute('innerHTML')[temp_s:temp_e])if li.find_element_by_tag_name('h3').text.find(self.place)!=-1:#选择地点正确的演唱会self.choose_result=len(titles)break
self.url="https:"+links[self.choose_result-1]
self.driver.get(self.url)#载入至购买界面

3.购票

datelist=self.driver.find_element_by_id("performList").find_elements_by_tag_name('li')#根据优先级选择一个可行日期
for i in self.date:j=datelist[i-1].get_attribute('class')if j=='itm':datelist[i-1].click()breakelif j=='itm itm-sel':breakelif j=='itm itm-oos':continue
sleep(1)
pricelist=self.driver.find_element_by_id("priceList").find_elements_by_tag_name('li')#根据优先级选择一个可行票价
for i in self.price:j=pricelist[i-1].get_attribute('class')if j=='itm':pricelist[i-1].click()breakelif j=='itm itm-sel':breakelif j=='itm itm-oos':continue
sleep(1.5)
cart=self.driver.find_element_by_id('cartList')
try:#各种按钮的点击try:cart.find_element_by_class_name('ops').find_element_by_link_text("立即预定").click()self.status=3except:cart.find_element_by_class_name('ops').find_element_by_link_text("立即购买").click()self.status=4
except:cart.find_element_by_class_name('ops').find_element_by_link_text("选座购买").click()self.status=5
self.num+=1
sleep(0.5)

接下来就是要点击确定了,但是这个确定按钮有三类:立即预定、立即购买、选座购买,我们需要分别处理。如果出错,就刷新界面

cart=self.driver.find_element_by_id('cartList')
try:#各种按钮的点击try:cart.find_element_by_class_name('ops').find_element_by_link_text("立即预定").click()self.status=3except:cart.find_element_by_class_name('ops').find_element_by_link_text("立即购买").click()self.status=4
except:cart.find_element_by_class_name('ops').find_element_by_link_text("选座购买").click()self.status=5

如果没有成功跳转,我没抢票的经验,不知道接下来会出现什么,所以也不知道要怎么具体应对,所以就刷新页面,重复上述操作,只能暂时这么粗暴地来了。

4.确认订单

点击选座购买,如果跳转成功,接下来就是选座了,这个有点小麻烦,暂时没写代码应对,开始交给人工处理。

点击立即预定和立即购买后如果正常跳转,之后就是确认订单信息与结算了,这时会有好多类型的界面,但是其中两类界面占了大多数,所以我就针对这两类进行编程。

因为这个时候的界面涉及很多隐私信息,我就不放出来了,虽然可以打码,但是我就是不想放。大致的操作其实和前面是类似的:找到目标,点击。

print('###开始确认订单###')
print('###默认购票人信息###')
rn_button=self.driver.find_elements_by_xpath('/html/body/div[3]/div[3]/div[2]/div[2]/div/a')
if len(rn_button)==1:#如果要求实名制
print('###选择实名制信息###')
rn_button[0].click()
#选择实名信息
tb=self.driver.find_element_by_xpath('/html/body/div[3]/div[3]/div[12]/div')
lb=tb.find_elements_by_tag_name('label')[self.real_name]#选择第self.real_name个实名者
lb.find_elements_by_tag_name('td')[0].click()
tb.find_element_by_class_name('one-btn').click()
print('###默认选择付款方式###')
print('###确认商品清单###')
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]')
if len(rn_button)==1:#如果要求实名制
print('###选择购票人信息###')
rn_button[0].click()
#选择实名信息
tb=self.driver.find_element_by_xpath('/html/body/div[3]/div[3]/div[13]/div')
lb=tb.find_elements_by_tag_name('label')[self.real_name]#选择第self.real_name个实名者
lb.find_elements_by_tag_name('td')[0].click()
tb.find_element_by_class_name('one-btn').click()
print('###不选择订单优惠###')
print('###请在付款完成后下载大麦APP进入订单详情页申请开具###')
self.driver.find_element_by_id('orderConfirmSubmit').click()#同意以上协议并提交订单
sleep(8)
if self.driver.title.find('支付')!=-1:
self.status=6
print('###成功提交订单,请手动支付###')
else:
print('###提交订单失败,请查看问题###')

我试了几个,基本都是可以抢票成功的,但是这是没有一窝蜂地去抢的情况下。

下次真正抢票了之后,我会结合实际经验进行修改优化!

有问题请在建议区反映!!!

——————————————我是一条分界线————————————————

2019/1/25 补充:(想法来源于潘同学)

①定时开启任务

比如15:30开启抢票通道,我们可以让程序在15:28开始运行准备起来了,这个定时可以由代码完成也可以由操作系统完成,我觉得由操作系统来解决是最简单的,windows具体操作可以参考这里,linux和mac-os也有各自的方法,很容易实现的。千万不能用延时条件判断来解决,那样太浪费CPU资源了。

②将部分强制等待(time.sleep)替换为显式等待

比如:

try:locator = (By.XPATH, "/html/body/div[1]/div/div[3]/div[1]/a[2]/div")element = WebDriverWait(self.driver, 3).until(EC.text_to_be_present_in_element(locator,self.usr_name))self.status=1print("###登录成功###")
except:self.status=0print("###登录失败###")

速度感觉有点提升,而且解决了部分定位不当的问题。方法参考这里

selenium中文文档_基于selenium的大麦网演唱会抢票程序相关推荐

  1. python演唱会抢票脚本_基于selenium的大麦网演唱会抢票程序

    自从我在知乎分享了代码后,很多人关注了我的代码,自己也很荣幸,哈哈.但是好多人运行我的代码却出现了这样那样的问题,我刚开始也很纳闷,明明自己当初试了好几个演唱会都可以,为什么现在有这么多问题,上段时间 ...

  2. python pptx库中文文档_基于python-pptx库中文文档及使用详解

    个人使用样例及部分翻译自官方文档,并详细介绍chart的使用 一:基础应用 1.创建pptx文档类并插入一页幻灯片 from pptx import Presentation prs = Presen ...

  3. pyppeteer有java版本吗_Pyppeteer中文文档_序言_安装_基本使用及注意事项

    Pyppeteer中文文档_序言_安装_基本使用及注意事项 Pyppeteer是Puppeteer Javascript(无头) chrome/chromium 浏览器自动化库的Python非官方端口 ...

  4. springboot中文文档_登顶 Github 的 Spring Boot 仓库!艿艿写的最肝系列

    源码精品专栏 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 My ...

  5. aspose excel中文文档_除了VBA,还有哪些编程语言可以操作Excel文件?

    Excel(Microsoft office)是现在最常用的办公软件,主要涉及电子表格制作.数据处理.报表输出展示以及更高端的还有金融建模等:我们知道,在需要批处理多个Excel工作表以及工作簿的时候 ...

  6. flask中文文档_「Flask系列」 初识Flask

    引子 作者有多年的编程打杂经验,之前一直参与基于Java的各种项目以及产品规划与设计,后因自己创业维持一家小公司,有些项目与产品,想降低开发成本,故在公司内部推行基于Python Flask的后端开发 ...

  7. vuetify中文文档_我们为什么选择Vuetify作为前端框架

    尝试了很多不同的前端框架,最终我们选择Vuetify(https://vuetifyjs.com)前端框架. 从Bootstrap开始,到iview,Buefy,elementUI,我们都是不断的尝试 ...

  8. springboot 中文文档_比Swagger还好用的自动生成接口文档工具

    JApiDocs是一个无需额外注解.开箱即用的SpringBoot接口文档生成工具. 编写和维护API文档这个事情,对于后端程序员来说,是一件恼人但又不得不做的事情,我们都不喜欢写文档,但除非项目前后 ...

  9. springboot 中文文档_还在用 Swagger生成接口文档?我推荐你试试它

    JApiDocs是一个无需额外注解.开箱即用的SpringBoot接口文档生成工具. 编写和维护API文档这个事情,对于后端程序员来说,是一件恼人但又不得不做的事情,我们都不喜欢写文档,但除非项目前后 ...

  10. keras中文文档_【DL项目实战02】图像识别分类——Keras框架+卷积神经网络CNN(使用VGGNet)

    版权声明:小博主水平有限,希望大家多多指导. 目录: [使用传统DNN] BG大龍:[DL项目实战02]图像分类--Keras框架+使用传统神经网络DNN​zhuanlan.zhihu.com [使用 ...

最新文章

  1. 后浪优秀!21 岁小伙兼职程序员养家,大三存款达 6 位数
  2. 大数据量表中,增加一个NOT NULL的新列
  3. 联想p720装系统_分享联想ThinkPad X1 Carbon笔记本最稳的重装WIN10系统方法
  4. RobHess的SIFT代码解析之RANSAC
  5. Abp + Grpc 如何实现用户会话状态传递
  6. hashset去重原理_基于simhash的文本去重原理
  7. jitpack让使用第三方依赖库更简单
  8. php多人访问抽奖倒计时一致,Javascript和PHP倒计时器为每个人显示相同的计时器...
  9. 在linux下解压jdk时出现的问题
  10. qt checkbox 选中事件_Qt基础知识学习
  11. keil教程之创建基础软件工程
  12. 按键双击和单击c语言,【按键】[独立按键] - 单击,双击,三击以及N击
  13. bzoj 1709: [Usaco2007 Oct]Super Paintball超级弹珠
  14. iOS 在线下载字体
  15. 【HDU 4699】Editor【栈】
  16. STM32 BOR/POR/PDR介绍
  17. You can find the Nike LeBron Soldier 11 now at kd10sale.com
  18. send_nsca passive monitor setup notes
  19. gcc 编译时中-L -l -Wall的含义
  20. 1072: 青蛙爬井 C语言

热门文章

  1. 美国华盛顿警察局被敲诈勒索,怎样抵御新型勒索攻击?
  2. 2023湖南师范大学计算机考研信息汇总
  3. 微型计算机硬件系统包括什么,微型计算机硬件系统由什么组成(6个基本组成部件)...
  4. 服务器系统测试,服务器系统整合测试
  5. VMware Workstation Player的vmnet8没启动,虚拟机没法NAT的问题分析与解决
  6. 乐理:十二平均律与大小调
  7. H.265流媒体播放器EasyPlayer.js的属性、方法及事件的调用汇总
  8. Excel技能培训之十 选择性粘贴,单元格公式转换为数值,对每个单元格进行运算,行列转换,只粘贴非空值
  9. Jieba中文分词 (二) ——词性标注与关键词提取
  10. 中印程序员对比:是什么让我们觉得印度程序员很厉害?