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

儀器預(yù)約

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

目標(biāo)


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

方案選擇


預(yù)約需要登錄,要用到cookie,因此這里用python自帶的urllib和urllib2兩個庫來實現(xiàn)?;居梅ㄈ缦拢?/p>

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默認的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中進行手動操作,從控制臺中獲取請求的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)了。下面再添加進一步的功能:

額外功能


多次嘗試

為了提高成功率,應(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)境,開始運行。到達時間后成功預(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容