亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

懶人法寶:定時(shí)訂票詳解

kid143 / 3156人閱讀

摘要:背景訂票網(wǎng)站韻動(dòng)株洲游泳館訂票網(wǎng)站訂票規(guī)則用戶當(dāng)天,預(yù)約第二日免費(fèi)游泳公益券領(lǐng)取資格,每位用戶每天只能預(yù)訂一張如有余票當(dāng)天也可預(yù)訂。

前言
暑假閑來(lái)無(wú)事,每天上午的寶貴時(shí)間想去游泳,減減肚子,練練耐力,正好我們那個(gè)地方游泳館上午提供免費(fèi)的票,但是,需要前一天早上七點(diǎn)開(kāi)始預(yù)定第二天上午的免費(fèi)游泳票。往年暑假,我是每天早上六點(diǎn)五十五準(zhǔn)時(shí)起床,眼睛半睜不睜的等著七點(diǎn)一到,立馬搶票!搶完一臉解脫地癱倒在床上繼續(xù)睡覺(jué)。簡(jiǎn)直就是煎熬啊,我在學(xué)校都沒(méi)起這么早過(guò)。
今年暑假,我實(shí)在是不想再早起了,考慮到訂票網(wǎng)站的訂票流程非常簡(jiǎn)易,是否能寫(xiě)一個(gè)腳本代替我每天早上完成訂票任務(wù)呢。答案是肯定的。最后我大概雖然其實(shí)用到的方法很簡(jiǎn)單,但是既然是在生活中難得遇到的實(shí)際問(wèn)題,我也做一個(gè)分享。之前我是沒(méi)有任何刷票、爬蟲(chóng)經(jīng)歷的。(本人專注數(shù)據(jù)挖掘)
技術(shù)改變生活,本篇博客的目的僅僅是分享并記錄一下用互聯(lián)網(wǎng)方法解決懶人在生活中的實(shí)際問(wèn)題。
背景

訂票網(wǎng)站:韻動(dòng)株洲游泳館訂票網(wǎng)站
訂票規(guī)則:用戶當(dāng)天7:00—22:00,預(yù)約第二日免費(fèi)游泳公益券領(lǐng)取資格,每位用戶每天只能預(yù)訂一張(如有余票當(dāng)天也可預(yù)訂)。
游泳館概況:(嘿嘿,我大株洲就是厲害)


注意:本腳本只實(shí)現(xiàn)簡(jiǎn)單的訂票功能,因?yàn)樵摼W(wǎng)站無(wú)需驗(yàn)證碼(很多外行的朋友,雖然我也是外行,都問(wèn)我能不能幫忙去12306搶票。。。)

功能目標(biāo)

自動(dòng)登錄功能(無(wú)驗(yàn)證碼!

自動(dòng)選擇預(yù)定場(chǎng)地、時(shí)間等信息,并提交表單

支持多賬號(hào)同時(shí)進(jìn)行刷票任務(wù)

定時(shí)任務(wù)

郵件提醒搶票結(jié)果

工具模塊

python

splinter

shell

crontab或plist

流程分析

直接進(jìn)入游泳館預(yù)訂界面(還有很多其他的運(yùn)動(dòng)項(xiàng)目可以預(yù)約哦,羽毛球、室內(nèi)足球...真想給株洲政府點(diǎn)個(gè)贊)

點(diǎn)擊右上角登錄按鈕進(jìn)入登錄頁(yè)面

輸入手機(jī)賬號(hào)和密碼,點(diǎn)擊登錄按鈕進(jìn)入登錄狀態(tài),此時(shí)頁(yè)面會(huì)跳轉(zhuǎn)到預(yù)訂界面

選擇好預(yù)定日期、預(yù)定時(shí)間,點(diǎn)擊確認(rèn)預(yù)訂按鈕確認(rèn)預(yù)訂

確認(rèn)對(duì)話框點(diǎn)擊確認(rèn),完成所有預(yù)訂過(guò)程(非預(yù)訂時(shí)間或者預(yù)定完了所以這里顯示"undefined")
以上就是整個(gè)預(yù)定流程,很簡(jiǎn)單吧!正是這么簡(jiǎn)單,讓我萌生了花點(diǎn)時(shí)間寫(xiě)個(gè)腳本來(lái)代替我訂票的邪惡想法!

功能實(shí)現(xiàn) Splinter環(huán)境配置

下載并安裝splinter

下載并安裝chrome Web驅(qū)動(dòng)

python splinter參考教程

訪問(wèn)游泳館預(yù)定界面
from splinter.browser import Browser
from time import sleep
import datetime
import mail
import sys
url = "http://www.wentiyun.cn/venue-722.html"
#配置自己的chrome驅(qū)動(dòng)路徑
executable_path = {"executable_path":"/usr/local/Cellar/chromedriver/2.31/bin/chromedriver"}

def visitWeb(url):
    #訪問(wèn)網(wǎng)站
    b = Browser("chrome", **executable_path)
    b.visit(url)
    return b
進(jìn)入登錄頁(yè)面并賬號(hào)密碼登錄
def login(b, username, passwd):
    try:
        lf = b.find_link_by_text(u"登錄")#登錄按鈕是鏈接的形式
        sleep(0.1)
        b.execute_script("window.scrollBy(300,0)")#下滑滾輪,將輸入框和確認(rèn)按鈕移動(dòng)至視野范圍內(nèi)
        lf.click()
        b.fill("username",username) # username部分輸入自己的賬號(hào)
        b.fill("password",passwd) # passwd部分輸入賬號(hào)密碼
        button = b.find_by_name("subButton")
        button.click()
    except Exception, e:
        print "登錄失敗,請(qǐng)檢查登陸相關(guān):", e
        sys.exit(1)
持續(xù)刷票策略

一旦以用戶的身份進(jìn)入到預(yù)訂界面,就需要按時(shí)間、場(chǎng)地信息要求進(jìn)行選擇,并確認(rèn)??紤]到很可能提前預(yù)約或其他情況導(dǎo)致某次訂票失敗,所以,僅僅一次訂票行為是不行的,需要反復(fù)訂票行為,直到訂票成功,于是,訂票策略如下:

反復(fù)訂票行為,退出條件:訂票一分鐘,即到七點(diǎn)過(guò)一分后退出,或預(yù)訂成功后退出

一次完整的訂票退出后(滿足1退出條件),為了保險(xiǎn),重啟chrome,繼續(xù)預(yù)訂操作,十次操作后,退出預(yù)訂程序

時(shí)間選擇:獲取明天日期,選擇預(yù)訂明天的游泳票

def getBookTime():
    #今天訂明天,時(shí)間邏輯
    date = datetime.datetime.now() + datetime.timedelta(days=1)
    dateStr = date.strftime("%Y-%m-%d")
    year, month, day = dateStr.split("-")
    date = "/".join([month, day])
    return date
def timeCondition(h=7.0,m=1.0,s=0.0):
    #退出時(shí)間判斷
    now = datetime.datetime.now()
    dateStr = now.strftime("%H-%M-%S")
    hour, minute, second = dateStr.split("-")
    t1 = h*60.0 + m + s/60.0
    t2 = float(hour)*60.0 + float(minute) + float(second)/60.0
    if t1 >= t2:
        return True
    return False
def book(b):
    #反復(fù)訂票行為,直到時(shí)間條件達(dá)到或預(yù)訂成功退出
    while(True):
        start = datetime.datetime.now()
        startStr = start.strftime("%Y-%m-%d %H:%M:%S")
        print "********** %s ********" % startStr
        try:
            #選擇日期
            date = getBookTime()
            b.find_link_by_text(date).click()
            #按鈕移到視野范圍內(nèi)
            b.execute_script("window.scrollBy(0,100)")
            #css顯示確認(rèn)按鈕
            js = "var i=document.getElementsByClassName("btn_box");i[0].style="display:true;""
            b.execute_script(js)
            #點(diǎn)擊確認(rèn)
            b.find_by_name("btn_submit").click()
            sleep(0.1)
            b.find_by_id("popup_ok").click()
            sleep(0.1)
            #測(cè)試彈出框
            #test(b)
            #sleep(0.1)
            result = b.evaluate_script("document.getElementById("popup_message").innerText")
            b.find_by_id("popup_ok").click()
            sleep(0.1)
            print result
            end = datetime.datetime.now()
            print "預(yù)訂頁(yè)面刷票耗時(shí):%s秒" % (end-start).seconds
            if result == "預(yù)訂成功!".decode("utf-8"):
                return True
            elif not timeCondition():
                return False
            b.reload()
        except Exception, e:
            print "預(yù)訂頁(yè)面刷票失敗,原因:", e
            end = datetime.datetime.now()
            print "共耗時(shí):%s秒" % (end-start).seconds
            #判讀當(dāng)前時(shí)間如果是7點(diǎn)過(guò)5分了,放棄訂票
            if not timeCondition():
                return False
            b.reload()
def tryBook(username, passwd):
    #持續(xù)刷票10次后,退出程序
    r = False
    for i in xrange(10):
        try:
            start = datetime.datetime.now()
            startStr = start.strftime("%Y-%m-%d %H:%M:%S")
            print "========== 第%s次嘗試,開(kāi)始時(shí)間%s ========" % (i, startStr)
            b = visitWeb(url)
            login(b, username, passwd)
            r = book(b)
            if r:
                print "book finish!"
                b.quit()
                break
            else:
                print "try %s again, 已經(jīng)七點(diǎn)1分,搶票進(jìn)入尾聲" % i
                b.quit()
            end = datetime.datetime.now()
            print "========== 第%s次嘗試結(jié)束,共耗時(shí)%s秒 ========" % (i, (end-start).seconds)
        except Exception, e:
            print "第%s次嘗試失敗,原因:%s" % (i, e)
            end = datetime.datetime.now()
            print "========== 第%s次嘗試結(jié)束,共耗時(shí)%s秒 ========" % (i, (end-start).seconds)
            return False
    return r
郵件服務(wù)

參考廖雪峰老師的實(shí)現(xiàn)哦,程序其實(shí)不麻煩,主要是郵箱的SMTP服務(wù)!

需要郵箱開(kāi)通SMTP代理服務(wù),如果你qq號(hào)是很久之前注冊(cè)的了,那我不推薦使用qq郵箱,一系列的密保會(huì)讓你崩潰。推薦使用新浪郵箱。

發(fā)送程序如下mail.py

import smtplib  
import traceback  
from email.mime.text import MIMEText  
from email.mime.multipart import MIMEMultipart  
from email.header import Header
from email.utils import parseaddr, formataddr
"""
to_addr = "844582201@qq.com"  
password = "*****"  
from_addr = "m13072163887@163.com"  
msg = MIMEText("hello, send by Python...", "plain", "utf-8")
server = smtplib.SMTP("smtp.163.com") # SMTP協(xié)議默認(rèn)端口是25
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()
"""
"""
    @subject:郵件主題 
    @msg:郵件內(nèi)容 
    @toaddrs:收信人的郵箱地址 
    @fromaddr:發(fā)信人的郵箱地址 
    @smtpaddr:smtp服務(wù)地址,可以在郵箱看,比如163郵箱為smtp.163.com 
    @password:發(fā)信人的郵箱密碼 
""" 
def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, "utf-8").encode(), addr))
    
def sendmail(subject,msg,toaddrs,fromaddr,smtpaddr,password):  
    mail_msg = MIMEMultipart()  
    if not isinstance(subject,unicode):  
        subject = unicode(subject, "utf-8")  
    mail_msg["Subject"] = subject  
    mail_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服務(wù)器  
        s.login(fromaddr,password)  #登錄郵箱  
        s.sendmail(fromaddr, toaddrs, mail_msg.as_string()) #發(fā)送郵件  
        s.quit()  
    except Exception,e:  
       print "Error: unable to send email", e  
       print traceback.format_exc()  

def send(msg):
    fromaddr = "mynameislps@sina.com"  
    smtpaddr = "smtp.sina.com"
    password = "*****"  
    subject = "這是郵件的主題"
    toaddrs = ["844582201@qq.com"]
    sendmail(subject,msg,toaddrs,fromaddr,smtpaddr,password)
定時(shí)任務(wù)策略

每天七點(diǎn),搶票開(kāi)始。為了保險(xiǎn)并且考慮到上文所構(gòu)建的搶票策略,我們可以六點(diǎn)五十九分開(kāi)始操作(考慮到還要訪問(wèn)預(yù)訂頁(yè)面、登錄頁(yè)面以及登錄操作等,萬(wàn)一有一定的延時(shí))。于是我們將任務(wù)布置在每天早上的六點(diǎn)五十九分。
定時(shí)任務(wù)的工具有兩種,一種是使用Linux自帶的定時(shí)工具crontab,一種是使用比較優(yōu)雅的Mac自帶的定時(shí)工具plist。這兩種工具非常簡(jiǎn)單實(shí)用,這里也不做太多介紹。

多賬號(hào)同時(shí)訂票操作策略

這就需要借助強(qiáng)大的shell腳本,我們把需要訂票的帳號(hào)密碼信息配置在shell內(nèi),同時(shí)shell根據(jù)這些帳號(hào)信息啟動(dòng)不同的進(jìn)程來(lái)同時(shí)完成訂票任務(wù)。

#!/bin/bash
my_array=("130****3887" "****"
        "187****4631" "****")
#待操作用戶個(gè)數(shù)
len=${#my_array[@]}
len=`expr $len / 2`
i=0
while (($i < $len))
do 
    echo "第($i)個(gè)用戶為: ${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
日志服務(wù)

良好、健壯的程序需要一套比較完備的日志系統(tǒng),本程序的日志服務(wù)都在上文中的程序中反映了,當(dāng)然不見(jiàn)得是最好的。僅供參考。這方便我們定位錯(cuò)誤或失敗的發(fā)生位置!

完整的工程在Github上:https://github.com/lps683/tic...

某些蛋疼的問(wèn)題

需要將按鈕/鏈接顯示在視野范圍內(nèi)才能進(jìn)行點(diǎn)擊操作。上文程序中諸如b.execute_script("window.scrollBy(300,0)")等操作都是上下調(diào)整頁(yè)面位置,將按鈕顯示在視野范圍內(nèi);如果某些按鈕是invisible的,那么我們可以通過(guò)修改JS中控件的屬性來(lái)顯示按鈕。如上文程序中的

#css顯示確認(rèn)按鈕
js = "var i=document.getElementsByClassName("btn_box");i[0].style="display:true;""
b.execute_script(js)

彈出框定位問(wèn)題:最后預(yù)定成功會(huì)彈出一個(gè)確認(rèn)框:

那要獲得這個(gè)對(duì)話框并不容易。我嘗試過(guò)諸如alert = browser.get_alert() alert.text alert.accept() alert.dismiss()之類的辦法都沒(méi)有成功。最后右鍵這個(gè)對(duì)話框,找到它的源碼,根據(jù)ID信息找到這個(gè)對(duì)話框才解決的!

總結(jié)

技術(shù)上來(lái)說(shuō),本文并沒(méi)有什么亮點(diǎn),如果要應(yīng)付12306等一系列的網(wǎng)站,那還有很多很麻煩的東西要研究。但是,能用技術(shù)來(lái)解決生活中的實(shí)際問(wèn)題,何樂(lè)而不為呢!

其實(shí)這個(gè)定時(shí)訂票程序是一個(gè)很流程化的東西,實(shí)際上就是程序在模擬人的各種行為,所以在coding前一定要好好測(cè)試網(wǎng)站訂票流程,把握訂票的規(guī)律。

有和同學(xué)交流,如果能catch到預(yù)定的消息格式,那豈不是更加簡(jiǎn)便了!嗯,我覺(jué)得很有道理,不過(guò)沒(méi)有作嘗試,我對(duì)真正的那些刷票軟件也非常感興趣,但是現(xiàn)在還沒(méi)有時(shí)間去研究,也歡迎大牛指點(diǎn)!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/40807.html

相關(guān)文章

  • 公排模式詳解

    摘要:小公排小公排是每一個(gè)人都是一個(gè)網(wǎng)體,叫多網(wǎng)體公排膜式。排軌方式選擇公排排軌方式自然排序大公排,或按推薦關(guān)系排序小公排入軌條件可自由選擇會(huì)員入軌條件消費(fèi)滿一定金額,或者購(gòu)買(mǎi)指定產(chǎn)品??偨Y(jié)公排模式掀起新的一輪熱潮。 公排系統(tǒng)是近年來(lái)非常受歡迎的商業(yè)模式,因其模式操作簡(jiǎn)單易懂,賺錢(qián)容易。在發(fā)展迅速的移動(dòng)互聯(lián)網(wǎng)時(shí)代,快人一步才是制勝的法寶。公排制模式并不是單一的模式,按照制度、模式、獎(jiǎng)金等不同...

    KunMinX 評(píng)論0 收藏0
  • 華為云三大法寶,讓云服務(wù)器比可靠更可靠

    摘要:數(shù)據(jù)掌握著企業(yè)的命脈,而云服務(wù)器承載著企業(yè)的數(shù)據(jù)和業(yè)務(wù)。只有超高可靠的云服務(wù)器,才能夠?yàn)槠髽I(yè)的業(yè)務(wù)保駕護(hù)航。到底這三大法寶為何物,請(qǐng)見(jiàn)下圖詳解作為華為自辦的面向產(chǎn)業(yè)的全球性年度旗艦大會(huì),將于年月日日在上海隆重舉行。數(shù)據(jù)掌握著企業(yè)的命脈,而云服務(wù)器承載著企業(yè)的數(shù)據(jù)和業(yè)務(wù)。只有超高可靠的云服務(wù)器,才能夠?yàn)槠髽I(yè)的業(yè)務(wù)保駕護(hù)航。華為云提供超高可靠云服務(wù)器套餐,成為企業(yè)上云,實(shí)現(xiàn)數(shù)據(jù)高效運(yùn)營(yíng)的三大法寶...

    ormsf 評(píng)論0 收藏0
  • ACE認(rèn)證考試—阿里云產(chǎn)品概念深化學(xué)習(xí)

    摘要:第一課阿里云相關(guān)概念深化學(xué)習(xí)云服務(wù)器,簡(jiǎn)稱是一種簡(jiǎn)單高效處理能力可彈性伸縮的計(jì)算服務(wù),幫助您快速構(gòu)建更穩(wěn)定安全的應(yīng)用,提升運(yùn)維效率,降低成本,使您更專注于核心業(yè)務(wù)創(chuàng)新。第一課:阿里云相關(guān)概念深化學(xué)習(xí) ECS 云服務(wù)器(Elastic Compute Service,簡(jiǎn)稱 ECS)是一種簡(jiǎn)單高效、處理能力可彈性伸縮的計(jì)算服務(wù),幫助您快速構(gòu)建更穩(wěn)定、安全的應(yīng)用,提升運(yùn)維效率,降低 IT 成本,使...

    zhichangterry 評(píng)論0 收藏0
  • AngularJS中使用百度地圖

    摘要:尤其,對(duì)于組件化起了非常大的作用。今天就簡(jiǎn)單介紹一下我的一個(gè)懶人組件百度地圖。后面詳細(xì)介紹該對(duì)象參數(shù)字符串,是你在百度開(kāi)放平臺(tái)申請(qǐng)的,沒(méi)有這個(gè),你的地圖顯示不出來(lái)的表達(dá)式,用來(lái)控制離線后的友好支持,后面詳細(xì)介紹各參數(shù)。 前言 AngularJS作為一個(gè)成功的框架,營(yíng)造出了完備的生態(tài)系統(tǒng)。尤其Directive,對(duì)于組件化起了非常大的作用。很多時(shí)候,如我這般懶人,網(wǎng)上搜一搜,就找到一個(gè)合...

    沈建明 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<