美文网首页
实验仪器自动预约脚本

实验仪器自动预约脚本

作者: 拔丝圣代 | 来源:发表于2017-05-13 10:51 被阅读0次

    仪器预约

    最近女票总是要每周半夜预约实验仪器,虽然难度并不大,手快一点总能抢到,但是这种简单重复性的劳动岂是一个程序员能忍的?必然要靠脚本解决啊。

    目标


    目标是在周六凌晨零点,根据提前设定好的帐号密码和预约信息,到达零点时自动完成预约,期间不需要人为干预。

    方案选择


    预约需要登录,要用到cookie,因此这里用python自带的urllib和urllib2两个库来实现。基本用法如下:

    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


    要实现定时启动,需要用到apscheduler,基本用法:

    from apscheduler.schedulers.blocking import BlockingScheduler
    
    def job():
        #要定时启动的任务
        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中设定好时间,只要等着时间到,任务就会自动开始执行。

    分析数据


    1. 登录

    接下来从chrome中进行手动操作,从控制台中获取请求的URL和数据

    首先要登录。在chrome中登录

    点击登录后查看登录请求:

    找到了登录的URL以及提交的数据。

    登录数据:

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

    2. 预约

    手动预约一次

    用chrome查看数据:


    找到了POST的URL以及要提交的数据。后面经过测试,currentDate可以不加。

    预约数据

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

    开始编码


    初始化函数

    先设定好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)
    

    登录函数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
    

    用户名和密码填入请求的数据,用urllib.urlencode转化成对应的格式,通过前面定义的opener发送POST请求,并根据是否重定向判断是否登录成功,若登录成功则重定向到首页。
    login_result.geturl()是重定向之后的URL,若与self.login_url相同,则没有重定向,登录失败,否则登录成功。

    预约函数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"]:
                # 预约成功
                print id2instrument[instrumentId] + ' 预约成功! 预约时间:' + reserveDate + ' ' + \
                    reserveStartTime + '-' + reserveEndTime
                return result["reserveRecordId"]
            else:
                print id2instrument[instrumentId] + ' 预约失败…… ' + result['errorCode'].encode('utf-8')
                return None
    

    与登录类似,将预约时间和仪器ID转格式,作为POST数据,发送POST请求,返回结果是JSON格式。根据返回结果中"errorType"字段判断是否预约成功,成功则返回预约记录ID,否则返回None。
    id2instrument是仪器ID到仪器名称的字典,要在前面定义。这样就可以根据仪器ID得到预定的仪器名称,用于打印输出。
    仪器ID号可以在浏览器控制台中看到。

    # 实验仪器ID
    INSTRUMENT_OLD_F20 = '28ad18ae3ebb4f91b1d52553019ca381'
    INSTRUMENT_NEW_F20 = '563e690aae7b41dfb6da1880f291e65b'
    id2instrument = {INSTRUMENT_OLD_F20: '老F20', INSTRUMENT_NEW_F20: '新F20'}
    

    开始预约

    job函数调用login和reserve函数,实现登录成功后预约。

    def job():
        rsv = ReserveTem()
        rsv.login(email, password)
        # 预定
        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是预订信息,需提前定义好,包括预定时间和仪器ID,可以定义多个,一次预约多个仪器多个时间。同样,帐号和密码也要提前定义

    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
        )
    ]
    

    定时任务

    最后就是定时启动了

    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'
    

    为避免到预定时间才发现帐号密码错误,先尝试登录。
    登录成功则利用apscheduler实现定时启动,启动时间start_time需要提前定义,在运行程序前填好

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

    'sat'是周六,上面的时间代表周六的零点。
    定时任务启动,输出程序要开始的时间 如 SAT. 00:00:00

    这样就程序的基本功能就实现了。下面再添加进一步的功能:

    额外功能


    多次尝试

    为了提高成功率,应该尝试多次预约,用while循环。

    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)
    
            # 预定
            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记录预约成功的数量,小于reserve_info的长度代表没有全部预约成功,需要继续尝试。每次尝试间隔一秒sleep(1),每次计数器try_time+1,最多尝试100次。

    添加预约信息以及删除预约

    在浏览器中预约成功后需填写预约信息,程序中也可以实现此功能。和前面一样,分析浏览器中的URL和数据,同样的方法就可以实现,只不过这里需要提供预约ID号和仪器ID号,这在预约的返回值和预约信息中可以得到。删除预约同理,这里不再赘述。

    尝试运行


    代码完成后,复制到我的云服务器上,填写帐号密码、开始时间以及预约信息,配置python环境,开始运行。到达时间后成功预约到仪器。以后终于不用再等着零点预约啦!

    完整代码


    完整的代码可以在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"]:
                # 预约成功
                print id2instrument[instrumentId] + ' 预约成功! 预约时间:' + reserveDate + ' ' + \
                    reserveStartTime + '-' + reserveEndTime
                return result["reserveRecordId"]
            else:
                print id2instrument[instrumentId] + ' 预约失败…… ' + 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)
    
            # 预定
            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'])
                # 填写预订信息
                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'
    

    相关文章

      网友评论

          本文标题:实验仪器自动预约脚本

          本文链接:https://www.haomeiwen.com/subject/nzvtxxtx.html