当前位置: 首页 > Linux

懒人法宝:定期订票详解

时间:2023-04-06 20:11:01 Linux

前言暑假无所事事。我想每天早上去游泳,减肚子,练耐力。正好我们住的游泳池早上提供免费门票。但是需要从前一天早上7:00开始预定第二天早上的免费游泳票。往年暑假,我每天早上6点55分准时起床,半睁着眼睛等待7点10分的到来,立马抢票!抢完之后,他满脸如释重负的倒在床上继续睡觉。这只是一种折磨。我在学校从来没有起这么早过。这个暑假,我真的不想再早起了。考虑到订票网站的订票流程很简单,能不能写个脚本代替我每天早上完成订票任务呢?答案是肯定的。最后,虽然我实际使用的方法很简单,但既然是生活中很少遇到的实际问题,我也分享一下。之前没有任何刷票和爬虫的经验。(我专注于数据挖掘)科技改变生活。本博客的目的只是分享和记录使用互联网方法解决生活中懒人的实际问题。后台购票网站:韵律株洲游泳中心购票网站购票规则:用户可预约当日7:00-22:00第二日免费游泳券领取资格,每位用户每次只能预订一张。当天(如果人多也可以订当天的票)。游泳池概况:(嘿嘿,我在株洲厉害)注:本脚本只实现简单的订票功能,因为网站不需要验证码(很多外行,虽然我也是外行,求助然后去12306抢票...)功能目标自动登录功能(无需验证码!)自动选择预定的场地、时间等信息,并提交表单支持多账号同时执行刷票任务定时任务邮件提醒抢票结果工具模块pythonsplintershellcrontaborplist流程分析直接进入游泳池预约界面(还有很多其他的项目可以预约,羽毛球,室内足球……好想给株洲政府点个赞)点击右上角登录按钮进入登录页面,输入手机账号和密码,点击登录按钮进入登录状态,此时页面会跳转到预约界面,选择预约的日期和时间,点击确认预约按钮确认预约确认对话框,点击Confirm完成所有的预订流程(不是预订时间或者预订结束所以显示“undefined”)以上就是整个预订流程,非常简单!就是这么简单让我萌生了花时间写剧本代替我订票的邪念!功能实现Splinter环境配置下载安装splinter下载安装chromeweb驱动pythonsplinter参考教程访问游泳池预订界面fromsplinter.browserimportBrowserfromtimeimportsleepimportdatetimeimportmailimportsysurl="http://www.wentiyun.cn/venue-722.html"#配置自己的chrome驱动路径executable_path={'executable_path':'/usr/local/Cellar/chromedriver/2.31/bin/chromedriver'}defvisitWeb(url):#访问网站b=Browser('chrome',**executable_path)b.visit(url)returnb进入登录页面,使用账号密码登录deflogin(b,username,passwd):try:lf=b.find_link_by_text(u"login")#登录按钮是一个链接Formsleep(0.1)b.execute_script("window.scrollBy(300,0)")#向下滑动滚轮将输入框和确认按钮移动到视野内lf.click()b.fill("username",username)#在username部分输入自己的账号b.fill("password",passwd)#在passwd部分输入账号密码button=b.find_by_name("subButton")button.click()exceptException,e:print"Loginfailed,pleasechecktheloginrelated:",esys.exit(1)继续刷票策略用户进入预约界面后,需要根据时间地点信息要求及确认考虑到某次预订很可能因提前预订或其他情况导致预订失败,仅预订一次是不够的,需要多次预订直至预订成功。因此,预订策略为:重复预订,退出条件:预订后一分钟,即七点过一分钟后退出,或者预订成功后退出完成预订后退出(满足退出条件1),保险起见重启chrome,继续预订操作,十次操作后,退出预订程序。时间选择:获取明天的日期,选择预定明天的游泳票defgetBookTime():#今天预定明天,时间逻辑date=datetime.datetime.now()+datetime.timedelta(days=1)dateStr=date.strftime('%Y-%m-%d')年月日=dateStr.split('-')date='/'.join([month,day])returndatedeftimeCondition(h=7.0,m=1.0,s=0.0):#退出时间判断now=datetime.datetime.now()dateStr=now.strftime('%H-%M-%S')时分秒=dateStr.split('-')t1=h*60.0+m+s/60.0t2=float(hour)*60.0+float(minute)+float(second)/60.0ift1>=t2:returnTruereturnFalsedefbook(b):#Repeatthe预订行为,直到满足时间条件或预订成功退出while(True):start=datetime.datetime.now()startStr=start.strftime('%Y-%m-%d%H:%M:%S')print"**********%s*********"%startStrtry:#selectdatedate=getBookTime()b.find_link_by_text(date).click()#将按钮移动到视野b.execute_script("window.scrollBy(0,100)")#css显示确认按钮js="vari=document.getElementsByClassName(\"btn_box\");i[0].style=\"display:true;\""b.execute_script(js)#点击确认b.find_by_name('btn_submit').click()sleep(0.1)b.find_by_id('popup_ok').click()sleep(0.1)#testpopupbox#test(b)#sleep(0.1)result=b.evaluate_script("document.getElementById(\"popup_message\").innerText")b.find_by_id('popup_ok').click()sleep(0.1)printresultend=datetime.datetime.now()print"在预订页面花费的时间:%s秒"%(end-start).secondsifresult=="预订成功!".decode("utf-8"):returnTrueelifnottimeCondition():returnFalseb.reload()exceptException,e:print'预约页面刷票失败,原因:',eend=datetime.datetime.now()print"总耗时:%s秒"%(end-start).seconds#解释当前时间如果是75分钟后,如果不是timeCondition():returnFalseb.reload()deftryBook(username,passwd):#连续刷票10次后,退出程序r=Falseforiinxrange(10):try:start=datetime.datetime.now()startStr=start.strftime('%Y-%m-%d%H:%M:%S')打印"==========%次尝试,开始时间%s========"%(i,startStr)b=visitWeb(url)login(b,username,passwd)r=book(b)ifr:print“书完了!”b.quit()breakelse:print"再试%s,已经7:1了,抢票快结束了"%ib.quit()end=datetime.datetime.now()print"==========%s尝试结束,总共花费了%s秒========"%(i,(end-start).seconds)exceptException,e:print'尝试%s失败,原因:%s'%(i,e)end=datetime.datetime.now()print"=========尝试%s结束,总消耗时间%s秒========"%(i,(end-start).seconds)returnFalsereturnrMail服务参考廖雪峰老师的实现,程序其实并不麻烦,主要是邮箱的SMTP服务!需要开通邮箱的SMTP代理服务。如果你的qq账号是很久以前注册的,那么我不建议使用qq邮箱。一系列的秘密保护会让你崩溃。推荐使用新浪邮箱。发送过程如下:mail.pyimportsmtplibimporttracebackfromemail.mime.textimportMIMETextfromemail.mime.multipartimportMIMEMultipartfromemail.headerimportHeaderfromemail.utilsimportparseaddr,formataddr'''to_addr="844582201@qq.com"password="*****"from_addr="m13072163887@163.com"msg=MIMEText('你好,用Python发送...','plain','utf-8')server=smtplib.SMTP("smtp.163.com")#SMTP协议默认端口为25server.login(from_addr,password)server.sendmail(from_addr,[to_addr],msg.as_string())server.quit()''''''@subject:Emailsubject@msg:Emailcontent@toaddrs:收件人Email地址@fromaddr:发件人Email地址@smtpaddr:Smtp服务地址,可以在邮箱中查看,例如163邮箱是smtp.163.com@password:发件人邮箱密码'''def_format_addr(s):name,addr=parseaddr(s)returnformataddr((Header(name,'utf-8').encode(),addr))defsendmail(subject,msg,toaddrs,fromaddr,smtpaddr,password):mail_msg=MIMEMultipart()如果不是isinstance(subject,unicode):subject=unicode(subject,'utf-8')mail_msg['Subject']=subjectmail_msg['From']=_format_addr('Python-auto<%s>'%fromaddr)mail_msg['To']=','.join(toaddrs)mail_msg.attach(MIMEText(msg,'plain','utf-8'))try:s=smtplib.SMTP()s.set_debuglevel(1)s.connect(smtpaddr,25)#连接到smtp服务器s.login(fromaddr,password)#登录邮箱s.sendmail(fromaddr,toaddrs,mail_msg.as_string())#发送邮件s.quit()exceptException,e:print"Error:unabletosendemail",eprinttraceback.format_exc()defsend(msg):fromaddr="mynameislps@sina.com"smtpaddr="smtp.sina.com"password="*****"subject="这是邮件的主题"toaddrs=["844582201@qq.com"]sendmail(subject,msg,toaddrs,fromaddr,smtpaddr,password)定时任务攻略每天七点开始抢票。为保险起见,考虑到上面构造的抢票策略,我们可以在6:59开始操作(考虑到预订页面、登录页面、登录操作也要访问,以防有一定的延迟)。所以我们每天早上6点59分配任务。定时任务的工具有两种,一种是使用Linux自带的定时工具crontab,一种是使用Mac自带的比较优雅的定时工具plist。这两个工具都非常简单实用,这里就不多做介绍了。多账户同时订票操作策略这需要使用强大的shell脚本。我们在shell中配置需要预定的账号密码信息,shell根据这些账号信息启动不同的进程,同时完成订票任务。#!/bin/bashmy_array=("130****3887""****"\"187****4631""****")#要操作的用户数len=${#my_array[@]}len=`expr$len/2`i=0while(($i<$len))doecho"The($i)thuseris:${my_array[2*i]}"logname="/Users/lps/work/program/ticketReservation/log/${my_array[2*i]}.log"nohup/Users/lps/anaconda/bin/python/Users/lps/work/program/ticketReservation/book.py${my_array[2*i]}${my_array[2*i+1]}>${logname}2>&1&i=`expr$i+1`done一个服务良好、健壮的程序需要一个集合ofcomparisons一个完整的日志系统,这个方案的日志服务在上面的方案中体现出来了,当然不一定是最好的。仅供参考。这样可以方便我们定位错误或故障发生的位置!完整的项目在Github上:https://github.com/lps683/tic...一些烦人的问题需要按钮/链接显示在视野内才能点击。上述程序中的b.execute_script("window.scrollBy(300,0)")等操作是上下调整页面位置,显示视野内的按钮;如果有些按钮是不可见的,那么我们可以修改JS中控件的属性来显示按钮。如上面程序中的#css所示,确认按钮js="vari=document.getElementsByClassName(\"btn_box\");i[0].style=\"display:true;\""b.execute_script(js)弹框定位问题:预约成功会弹出确认框:这个对话框不好搞定。我试过alert=browser.get_alert()alert.textalert.accept()alert.dismiss()之类的东西但没有成功。最后在这个对话框上右击,找到它的源代码,根据ID信息找到这个对话框即可解决!总结从技术上讲,本文没有亮点。如果要对付12306等一系列网站,还有很多麻烦的东西要研究。但是,如果可以用科技来解决生活中的实际问题,何乐而不为呢!其实这个定时订票程序是一个很流程化的东西。事实上,该程序正在模拟人类的各种行为。因此,在打码前,一定要测试网站订票流程,掌握订票规则。如果你和你的同学交流,如果你能抓住预定的消息格式,那岂不是更容易!嗯,我觉得有道理,不过我没试过。我也对真正的刷票软件很感兴趣,但是暂时还没有时间研究,欢迎大家指教!