實驗儀器自動預(yù)約腳本

儀器預(yù)約

最近女票總是要每周半夜預(yù)約實驗儀器,雖然難度并不大,手快一點總能搶到愕鼓,但是這種簡單重復(fù)性的勞動豈是一個程序員能忍的钙态?必然要靠腳本解決啊。

目標(biāo)


目標(biāo)是在周六凌晨零點菇晃,根據(jù)提前設(shè)定好的帳號密碼和預(yù)約信息册倒,到達(dá)零點時自動完成預(yù)約,期間不需要人為干預(yù)磺送。

方案選擇


預(yù)約需要登錄驻子,要用到cookie,因此這里用python自帶的urllib和urllib2兩個庫來實現(xiàn)估灿〕绾牵基本用法如下:

cookie = cookielib.CookieJar()
handler = urllib2.HTTPCookieProcessor(self.cookie)
opener = urllib2.build_opener(self.handler)
data = urllib.urlencode(dict(
    username=username,
    password=password
))
result = opener.open(url, data)

urllib2默認(rèn)的urlopen不支持cookie,所以要自定義一個opener


要實現(xiàn)定時啟動馅袁,需要用到apscheduler域慷,基本用法:

from apscheduler.schedulers.blocking import BlockingScheduler

def job():
    #要定時啟動的任務(wù)
    pass

start_time = dict(
    day_of_week='sat',
    hour=0,
    minute=0,
    second=0
)
scheduler = BlockingScheduler()
scheduler.add_job(job, 'cron', **start_time)
scheduler.start()

在start_time中設(shè)定好時間,只要等著時間到汗销,任務(wù)就會自動開始執(zhí)行犹褒。

分析數(shù)據(jù)


1. 登錄

接下來從chrome中進(jìn)行手動操作,從控制臺中獲取請求的URL和數(shù)據(jù)

首先要登錄弛针。在chrome中登錄

點擊登錄后查看登錄請求:

找到了登錄的URL以及提交的數(shù)據(jù)叠骑。

登錄數(shù)據(jù):

login_url = 'http://cem.ylab.cn/doLogin.action'
login_data = urllib.urlencode(dict(
        origUrl='',
        origType='',
        rememberMe='false',
        username=email,
        password=password
))

2. 預(yù)約

手動預(yù)約一次

用chrome查看數(shù)據(jù):


找到了POST的URL以及要提交的數(shù)據(jù)。后面經(jīng)過測試削茁,currentDate可以不加宙枷。

預(yù)約數(shù)據(jù)

reserve_url = 'http://cem.ylab.cn/user/doReserve.action'
reserve_data = urllib.urlencode({
    'reserveDate': reserveDate,
    'instrumentId': instrumentId,
    'reserveStartTime': reserveStartTime,
    'reserveEndTime': reserveEndTime
})

開始編碼


初始化函數(shù)

先設(shè)定好URL,建立opener:

class ReserveTem(object):
    def __init__(self):
        self.login_url = 'http://cem.ylab.cn/doLogin.action'  
        self.reserve_url = 'http://cem.ylab.cn/user/doReserve.action'  

        self.cookie = cookielib.CookieJar()
        self.handler = urllib2.HTTPCookieProcessor(self.cookie)
        self.opener = urllib2.build_opener(self.handler)

登錄函數(shù)login

    def login(self, email, password):
        login_data = urllib.urlencode(dict(
            origUrl='',
            origType='',
            rememberMe='false',
            username=email,
            password=password
        ))
        login_result = self.opener.open(self.login_url, login_data)

        if login_result.geturl() != self.login_url:
            # 重定向則登錄成功
            print '登錄成功茧跋!'
            return True
        else:
            print '登錄失敗……'
            return False

用戶名和密碼填入請求的數(shù)據(jù)慰丛,用urllib.urlencode轉(zhuǎn)化成對應(yīng)的格式,通過前面定義的opener發(fā)送POST請求瘾杭,并根據(jù)是否重定向判斷是否登錄成功璧帝,若登錄成功則重定向到首頁。
login_result.geturl()是重定向之后的URL富寿,若與self.login_url相同,則沒有重定向锣夹,登錄失敗页徐,否則登錄成功。

預(yù)約函數(shù)reserve

    def reserve(self, reserveDate, reserveStartTime, reserveEndTime, instrumentId):
        reserve_data = urllib.urlencode({
            'reserveDate': reserveDate,
            'instrumentId': instrumentId,
            'reserveStartTime': reserveStartTime,
            'reserveEndTime': reserveEndTime
        })
        reserve_result = self.opener.open(self.reserve_url, reserve_data)

        result = json.loads(reserve_result.read())

        if 'success' in result["errorType"] and result["reserveRecordId"]:
            # 預(yù)約成功
            print id2instrument[instrumentId] + ' 預(yù)約成功! 預(yù)約時間:' + reserveDate + ' ' + \
                reserveStartTime + '-' + reserveEndTime
            return result["reserveRecordId"]
        else:
            print id2instrument[instrumentId] + ' 預(yù)約失敗…… ' + result['errorCode'].encode('utf-8')
            return None

與登錄類似银萍,將預(yù)約時間和儀器ID轉(zhuǎn)格式变勇,作為POST數(shù)據(jù),發(fā)送POST請求,返回結(jié)果是JSON格式搀绣。根據(jù)返回結(jié)果中"errorType"字段判斷是否預(yù)約成功飞袋,成功則返回預(yù)約記錄ID,否則返回None链患。
id2instrument是儀器ID到儀器名稱的字典巧鸭,要在前面定義。這樣就可以根據(jù)儀器ID得到預(yù)定的儀器名稱麻捻,用于打印輸出纲仍。
儀器ID號可以在瀏覽器控制臺中看到。

# 實驗儀器ID
INSTRUMENT_OLD_F20 = '28ad18ae3ebb4f91b1d52553019ca381'
INSTRUMENT_NEW_F20 = '563e690aae7b41dfb6da1880f291e65b'
id2instrument = {INSTRUMENT_OLD_F20: '老F20', INSTRUMENT_NEW_F20: '新F20'}

開始預(yù)約

job函數(shù)調(diào)用login和reserve函數(shù)贸毕,實現(xiàn)登錄成功后預(yù)約郑叠。

def job():
    rsv = ReserveTem()
    rsv.login(email, password)
    # 預(yù)定
    for info in reserve_info:
        if info.get('success', False):
            continue
        id = rsv.reserve(reserveDate=info['reserveDate'],
                         reserveStartTime=info['reserveStartTime'],
                         reserveEndTime=info['reserveEndTime'],
                         instrumentId=info['instrumentId'])

reserve_info是預(yù)訂信息,需提前定義好明棍,包括預(yù)定時間和儀器ID乡革,可以定義多個,一次預(yù)約多個儀器多個時間摊腋。同樣沸版,帳號和密碼也要提前定義

email = os.getenv("username")
password = os.getenv("password")
reserve_info = [
    dict(
        reserveDate='',  # '2017年01月01日'
        reserveStartTime='',  # '12:00'
        reserveEndTime='',  # '13:00'
        instrumentId=''  # INSTRUMENT_OLD_F20
    )
]

定時任務(wù)

最后就是定時啟動了

if __name__ == '__main__':
    try_login = ReserveTem()
    if try_login.login(email, password):
        scheduler = BlockingScheduler()
        scheduler.add_job(job, 'cron', **start_time)
        print 'job will start at : ' + start_time['day_of_week'].upper() + \
              '. %02d:%02d:%02d' % (start_time['hour'], start_time['minute'], start_time['second'])
        scheduler.start()
    else:
        print 'invalid username or password'

為避免到預(yù)定時間才發(fā)現(xiàn)帳號密碼錯誤,先嘗試登錄歌豺。
登錄成功則利用apscheduler實現(xiàn)定時啟動推穷,啟動時間start_time需要提前定義,在運行程序前填好

start_time = dict(
    day_of_week='sat',
    hour=0,
    minute=0,
    second=0
)

'sat'是周六类咧,上面的時間代表周六的零點馒铃。
定時任務(wù)啟動,輸出程序要開始的時間 如 SAT. 00:00:00

這樣就程序的基本功能就實現(xiàn)了痕惋。下面再添加進(jìn)一步的功能:

額外功能


多次嘗試

為了提高成功率区宇,應(yīng)該嘗試多次預(yù)約,用while循環(huán)值戳。

def job():
    rsv = ReserveTem()
    success_num = 0
    try_time = 0

    while success_num < len(reserve_info) and try_time < 100:
        try_time += 1
        
        # 登錄
        rsv.login(email, password)

        # 預(yù)定
        for info in reserve_info:
            if info.get('success', False):
                continue
            id = rsv.reserve(reserveDate=info['reserveDate'],
                             reserveStartTime=info['reserveStartTime'],
                             reserveEndTime=info['reserveEndTime'],
                             instrumentId=info['instrumentId'])
        sleep(1)

success_num記錄預(yù)約成功的數(shù)量议谷,小于reserve_info的長度代表沒有全部預(yù)約成功,需要繼續(xù)嘗試堕虹。每次嘗試間隔一秒sleep(1)卧晓,每次計數(shù)器try_time+1,最多嘗試100次赴捞。

添加預(yù)約信息以及刪除預(yù)約

在瀏覽器中預(yù)約成功后需填寫預(yù)約信息逼裆,程序中也可以實現(xiàn)此功能。和前面一樣赦政,分析瀏覽器中的URL和數(shù)據(jù)胜宇,同樣的方法就可以實現(xiàn),只不過這里需要提供預(yù)約ID號和儀器ID號,這在預(yù)約的返回值和預(yù)約信息中可以得到桐愉。刪除預(yù)約同理财破,這里不再贅述。

嘗試運行


代碼完成后从诲,復(fù)制到我的云服務(wù)器上左痢,填寫帳號密碼、開始時間以及預(yù)約信息盏求,配置python環(huán)境抖锥,開始運行。到達(dá)時間后成功預(yù)約到儀器碎罚。以后終于不用再等著零點預(yù)約啦磅废!

完整代碼


完整的代碼可以在github上下載
https://github.com/a188616786a/zju_tem_reserve

# encoding:utf-8
import json
import os
import urllib
import urllib2
import cookielib
from time import sleep

from apscheduler.schedulers.blocking import BlockingScheduler

INSTRUMENT_OLD_F20 = '28ad18ae3ebb4f91b1d52553019ca381'
INSTRUMENT_NEW_F20 = '563e690aae7b41dfb6da1880f291e65b'
id2instrument = {INSTRUMENT_OLD_F20: '老F20', INSTRUMENT_NEW_F20: '新F20'}

# 以下需填寫
# --------------------------------------
email = os.getenv("username")
password = os.getenv("password")
reserve_info = [
    dict(
        reserveDate='',  # '2017年01月01日'
        reserveStartTime='',  # '12:00'
        reserveEndTime='',  # '13:00'
        instrumentId=INSTRUMENT_NEW_F20  # INSTRUMENT_OLD_F20
    )
]
start_time = dict(
    day_of_week='sat',
    hour=0,
    minute=0,
    second=0
)
# --------------------------------------


class ReserveTem(object):
    def __init__(self):
        self.login_url = 'http://cem.ylab.cn/doLogin.action'  # GET or POST
        self.reserve_url = 'http://cem.ylab.cn/user/doReserve.action'  # POST
        self.add_comment_url = 'http://cem.ylab.cn/user/addReserveComment.action'  # POST
        self.delete_reserve_url = 'http://cem.ylab.cn/user/deleteReserve.action'  # GET or POST

        self.cookie = cookielib.CookieJar()
        self.handler = urllib2.HTTPCookieProcessor(self.cookie)
        self.opener = urllib2.build_opener(self.handler)

    def login(self, email, password):
        login_data = urllib.urlencode(dict(
            origUrl='',
            origType='',
            rememberMe='false',
            username=email,
            password=password
        ))
        login_result = self.opener.open(self.login_url, login_data)

        if login_result.geturl() != self.login_url:
            # 重定向則登錄成功
            print '登錄成功矢炼!'
            return True
        else:
            print '登錄失敗……'
            return False

    def reserve(self, reserveDate, reserveStartTime, reserveEndTime, instrumentId):
        reserve_data = urllib.urlencode({
            'reserveDate': reserveDate,
            'instrumentId': instrumentId,
            'reserveStartTime': reserveStartTime,
            'reserveEndTime': reserveEndTime
        })
        reserve_result = self.opener.open(self.reserve_url, reserve_data)

        result = json.loads(reserve_result.read())

        if 'success' in result["errorType"] and result["reserveRecordId"]:
            # 預(yù)約成功
            print id2instrument[instrumentId] + ' 預(yù)約成功! 預(yù)約時間:' + reserveDate + ' ' + \
                reserveStartTime + '-' + reserveEndTime
            return result["reserveRecordId"]
        else:
            print id2instrument[instrumentId] + ' 預(yù)約失敗…… ' + result['errorCode'].encode('utf-8')
            return None

    def add_comment(self, instrumentId, reserveRecordId, msg):
        add_comment_data = urllib.urlencode({
            'instrumentId': instrumentId,
            'hideRest': '1',
            'reserveRecordId': reserveRecordId,
            'commentMandatory': 'true',
            'comment': msg
        })
        self.opener.open(self.add_comment_url, add_comment_data)

    def delete_reserve(self, reserveRecordId):
        delete_data = urllib.urlencode(dict(
            hideRest='1',
            reserveRecordId=reserveRecordId))
        result = self.opener.open(self.delete_reserve_url, delete_data)
        if '操作失敗' in result.read():
            # 失敗
            print '無此記錄缠诅,刪除失敗'
        else:
            print '刪除成功玩荠!'


def job():
    rsv = ReserveTem()
    success_num = 0
    try_time = 0

    while success_num < len(reserve_info) and try_time < 100:
        try_time += 1
        
        # 登錄
        rsv.login(email, password)

        # 預(yù)定
        for info in reserve_info:
            if info.get('success', False):
                continue
            id = rsv.reserve(reserveDate=info['reserveDate'],
                             reserveStartTime=info['reserveStartTime'],
                             reserveEndTime=info['reserveEndTime'],
                             instrumentId=info['instrumentId'])
            # 填寫預(yù)訂信息
            if id:
                info['success'] = True
                success_num += 1
                rsv.add_comment(instrumentId=info['instrumentId'], reserveRecordId=id, msg='f20')
        sleep(1)

if __name__ == '__main__':
    try_login = ReserveTem()
    if try_login.login(email, password):
        scheduler = BlockingScheduler()
        scheduler.add_job(job, 'cron', **start_time)
        print 'job will start at : ' + start_time['day_of_week'].upper() + \
              '. %02d:%02d:%02d' % (start_time['hour'], start_time['minute'], start_time['second'])
        scheduler.start()
    else:
        print 'invalid username or password'
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慕趴,一起剝皮案震驚了整個濱河市委可,隨后出現(xiàn)的幾起案子嚼蚀,更是在濱河造成了極大的恐慌血公,老刑警劉巖荷荤,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玫鸟,死亡現(xiàn)場離奇詭異导绷,居然都是意外死亡,警方通過查閱死者的電腦和手機屎飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門妥曲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钦购,你說我怎么就攤上這事檐盟。” “怎么了押桃?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵葵萎,是天一觀的道長。 經(jīng)常有香客問我唱凯,道長羡忘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任磕昼,我火速辦了婚禮壳坪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掰烟。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布纫骑。 她就那樣靜靜地躺著蝎亚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪先馆。 梳的紋絲不亂的頭發(fā)上发框,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音煤墙,去河邊找鬼梅惯。 笑死,一個胖子當(dāng)著我的面吹牛仿野,可吹牛的內(nèi)容都是我干的铣减。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼脚作,長吁一口氣:“原來是場噩夢啊……” “哼葫哗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起球涛,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤劣针,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后亿扁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捺典,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年从祝,在試婚紗的時候發(fā)現(xiàn)自己被綠了襟己。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡哄褒,死狀恐怖稀蟋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呐赡,我是刑警寧澤退客,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站链嘀,受9級特大地震影響萌狂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怀泊,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一茫藏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霹琼,春花似錦务傲、人聲如沸凉当。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽看杭。三九已至,卻和暖如春挟伙,著一層夾襖步出監(jiān)牢的瞬間楼雹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工尖阔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贮缅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓介却,卻偏偏與公主長得像谴供,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子筷笨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理憔鬼,服務(wù)發(fā)現(xiàn),斷路器胃夏,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 經(jīng)過多次嘗試轴或,模擬登錄淘寶終于成功了,實在是不容易仰禀,淘寶的登錄加密和驗證太復(fù)雜了照雁,煞費苦心,在此寫出來和大家一起分...
    追不到的那縷風(fēng)閱讀 1,687評論 0 3
  • 22年12月更新:個人網(wǎng)站關(guān)停答恶,如果仍舊對舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,160評論 22 257
  • 1 前言 作為一名合格的數(shù)據(jù)分析師饺蚊,其完整的技術(shù)知識體系必須貫穿數(shù)據(jù)獲取、數(shù)據(jù)存儲悬嗓、數(shù)據(jù)提取污呼、數(shù)據(jù)分析、數(shù)據(jù)挖掘包竹、...
    whenif閱讀 18,052評論 45 523
  • 最近在學(xué)習(xí)flask燕酷,用到flask-login,發(fā)現(xiàn)網(wǎng)上只有0.1版本的中文文檔周瞎,看了官方已經(jīng)0.4了苗缩,并且添加...
    ZZES_ZCDC閱讀 5,934評論 3 24