2025年4月13日星期日

座位自动预约脚本

1.购买服务器

阿里云:

服务器购买地址

https://t.aliyun.com/U/qqlxBb

若失效,可用地址

https://www.aliyun.com/activity/wuying/dj?source=5176.29345612&userCode=49hts92d

腾讯云:

https://curl.qcloud.com/wJpWmSfU

若失效,可用地址

https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=ad201ee2ef3b771157f72ee5464b1fea&from=console

华为云

https://activity.huaweicloud.com/cps.html?fromacct=64b5cf7cc11b4840bb4ed2ea0b2f4468&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905

2.部署教程

2024年最新青龙面板跑脚本教程(一)持续更新中

3.代码如下

import requestsimport yamlimport randomfrom datetime import datetime, timedeltaimport jsonimport osimport logging
from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.chrome.service import Servicefrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitimport time

logging.basicConfig(                    format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',                    datefmt='%H:%M:%S',                    level=logging.DEBUG)
time_zone = 8  # 时区
# 两天后日期
def get_seats_with_config(user_config, date_config, seat_config):    # 二楼东/二楼西/四楼/三楼大厅/守正书院/求新书院/自定义    seat_name = date_config['name']    if seat_name == "自定义":        return user_config['自定义']    return list(range(seat_config[seat_name]['begin'], seat_config[seat_name]['end']))

class SeatAutoBooker:    def __init__(self, booker_config):        self.json = None        self.resp = None        self.user_data = None
        logging.info('Creating SeatAutoBooker object')
        self.un = os.environ["SCHOOL_ID"].strip()  # 学号        print("使用用户:{}".format(self.un))        self.pd = os.environ["PASSWORD"].strip()  # 密码        self.SCKey = None        try:            self.SCKey = os.environ["SCKEY"]        except KeyError:            print("没有Server酱的key,将不会推送消息")
        chrome_options = Options()        chrome_options.add_argument('--headless')        chrome_options.add_argument('--no-sandbox')        chrome_options.add_argument('--disable-dev-shm-usage')        self.driver = webdriver.Chrome(service=Service('/usr/local/bin/chromedriver'), options=chrome_options)        self.wait = WebDriverWait(self.driver, 100.5)        self.cookie = None
        self.cfg = booker_config
    def book_favorite_seat(self, user_config, seat_config):        #判断是否到了预约时间        # 阅览室晚上9点开始预约,自习室晚上8点半开始预约        the_day_after_tomorrow = ['周一''周二''周三''周四''周五''周六''周日'][(datetime.now().weekday() + 2) % 7]        seat_type = seat_config[user_config[the_day_after_tomorrow]['name']]["type"]        if seat_type == "自习室":            start_time = datetime.now().replace(hour=20-time_zone, minute=0, second=0, microsecond=0)            end_time = datetime.now().replace(hour=20-time_zone, minute=15, second=0, microsecond=0)        else:            start_time = datetime.now().replace(hour=21-time_zone, minute=0, second=0, microsecond=0)            end_time = datetime.now().replace(hour=21-time_zone, minute=15, second=0, microsecond=0)        start_time = start_time - timedelta(minutes=self.cfg["cron-delta-minutes"])        if datetime.now() < start_time or datetime.now() > end_time:            return -1"未到预约时间"        logging.info('Booking favorite seat')        retry_sleep_time = timedelta(minutes=self.cfg["cron-delta-minutes"]).seconds*2/(self.cfg["max-retry"]-2) - 10        for tried_times in range(self.cfg["max-retry"]):            try:                return self._book_favorite_seat(user_config, seat_config, tried_times)            except Exception as e:                logging.exception(e)                print(e.__class__, "尝试第{}次".format(tried_times))                time.sleep(retry_sleep_time)
    def _book_favorite_seat(self, user_config, seat_config, tried_times=0):        logging.info('Entering _book_favorite_seat method')        the_day_after_tomorrow = ['周一''周二''周三''周四''周五''周六''周日'][(datetime.now().weekday() + 2) % 7]        date_config = user_config[the_day_after_tomorrow]        seats = get_seats_with_config(user_config, date_config, seat_config)        today_0_clock = datetime.strptime(datetime.now().strftime("%Y-%m-%d 00:00:00"), "%Y-%m-%d %H:%M:%S")        book_time = today_0_clock + timedelta(days=2) + timedelta(hours=date_config['开始时间'])        delta = book_time - self.cfg["start-time"]        total_seconds = delta.days * 24 * 3600 + delta.seconds        if date_config['name'] == '自定义' and tried_times<self.cfg["max-retry"]/3*2:            seat = seats[0]        else:            seat = random.choice(seats)        data = f"beginTime={total_seconds}&duration={3600 * date_config['持续小时数']}&&seats[0]={seat}&seatBookers[0]={self.user_data['uid']}"
        headers = self.cfg["headers"]        headers['Cookie'] = self.cookie        print(data)        self.resp = requests.post(self.cfg["target"], data=data, headers=headers)        self.json = json.loads(self.resp.text)        return self.json["CODE"], self.json["MESSAGE"] + " 座位:{}".format(seat)
    def login(self):        logging.info('Login in')
        pwd_path_selector = """//*[@id="react-root"]/div/div/div[1]/div[2]/div/div[1]/div[2]/div/div/div/div/div[1]/div[2]/div/div[3]/div/div[2]/input"""        button_path_selector = """//*[@id="react-root"]/div/div/div[1]/div[2]/div/div[1]/div[2]/div/div/div/div/div[1]/div[3]"""
        try:            logging.info('开始登陆...')
            self.driver.get("https://网站.com/")            logging.debug('打开网站.')
            self.wait.until(EC.presence_of_element_located((By.NAME, "login_name")))            logging.debug('找到用户名输入框.')
            self.wait.until(EC.presence_of_element_located((By.XPATH, pwd_path_selector)))            logging.debug('找到密码输入框.')
            self.wait.until(EC.presence_of_element_located((By.XPATH, button_path_selector)))            logging.debug('找到登录按钮.')
            self.driver.find_element(By.NAME, 'login_name').clear()            self.driver.find_element(By.NAME, 'login_name').send_keys(self.un)  # 传送帐号            logging.info('输入用户名')
            self.driver.find_element(By.XPATH, pwd_path_selector).clear()            self.driver.find_element(By.XPATH, pwd_path_selector).send_keys(self.pd)  # 输入密码            logging.info('输入密码')            logging.info('点击登录按钮')            self.driver.find_element(By.XPATH, button_path_selector).click()            time.sleep(5)            cookie_list = self.driver.get_cookies()            self.cookie = ";".join([item["name"] + "=" + item["value"] + "" for item in cookie_list])            self.cfg["headers"]['Cookie'] = self.cookie
            logging.info("登录成功!")        except Exception as e:            logging.error(f"登录失败:{e}")            return -1        return 0
    def get_user_info(self):        logging.info('Getting user info')
        headers = self.cfg["headers"]        headers['Cookie'] = self.cookie        try:            resp = requests.get("https://网址/Seat/Index/searchSeats?LAB_JSON=1",                                headers=headers)            self.user_data = resp.json()['DATA']            _ = self.user_data['uid']        except Exception as e:            logging.exception(e)            print(self.user_data)            print(e.__class__.__name__ + ",获取用户数据失败")            return -1        print("获取用户数据成功")        return 0
    def wechatNotice(self, message, desp=None):        logging.info('Sending WeChat notice')
        if self.SCKey != '':            url = 'https://sc.fq.com/{0}.send'.format(self.SCKey)            data = {                'title': message,                desp: desp,            }            try:                r = requests.post(url, data=data)                if r.json()["data"]["error"] == 'SUCCESS':                    print("Server酱通知成功")                else:                    print("Server酱通知失败")            except Exception as e:                logging.exception(e)                print(e.__class__, "推送服务配置错误")
def is_booking_enable(date_cfg):    if date_cfg['启用']:        return True    return False
if __name__ == "__main__":    logging.info('Start of the program')    with open("user_config.yml"'r'as f_obj:        user_config = yaml.safe_load(f_obj)    with open("config/basic_config.yml"'r'as f_obj:        basic_config = yaml.safe_load(f_obj)    with open("config/seat_config.yml"'r'as f_obj:        seat_config = yaml.safe_load(f_obj)
    the_day_after_tomorrow = ['周一''周二''周三''周四''周五''周六''周日'][(datetime.now().weekday() + 2) % 7]    if not is_booking_enable(user_config[the_day_after_tomorrow]):        logging.info('预约未启用')        print("预约未启用")        exit(0)
    s = SeatAutoBooker(basic_config["SeatAutoBooker"])    if not s.login() == 0:        s.driver.quit()        logging.info('Login unsuccessful')        exit(-1)    if not s.get_user_info() == 0:        s.driver.quit()        logging.info('Getting user info unsuccessful')        exit(-1)    s.book_favorite_seat(user_config=user_config, seat_config=seat_config)    s.driver.quit()    logging.info('End of the program')
解析

这个脚本的座位自动预约脚本。

  1. 读取用户的预约偏好与配置。

  2. 登录高校图书馆预约系统(通过 Selenium 控制 Chrome)。

  3. 判断是否到了开放预约的时间。

  4. 获取用户 ID。

  5. 向预约系统发送 POST 请求提交预约。

  6. 可选通过 Server酱发送微信推送。

主要功能

get_seats_with_config

根据用户配置和日期配置,返回该日期可选的座位编号列表。

类 SeatAutoBooker

核心逻辑

__init__
  • 初始化用户信息(学号、密码);

  • 配置浏览器为无头模式;

  • 准备预约用到的配置参数;

  • 创建 ChromeDriver 实例。

login
  • 打开图书馆预约网站;

  • 使用 Selenium 自动输入账号密码并登录;

  • 提取登录后的 Cookie 保存,用于后续预约请求。

get_user_info
  • 通过 GET 请求获取用户的唯一 ID(uid),用于预约参数中传递。

book_favorite_seat
  • 判断是否到了可以预约的时间段;

  • 若在时间范围内则尝试预约;

  • 可重试多次,每次失败后等待一段时间。

_book_favorite_seat
  • 核心预约方法;

  • 构造预约时间(2天后目标时间);

  • 随机选择或固定选定某个座位编号;

  • 构造表单数据并通过 POST 发起预约请求;

  • 返回预约结果(成功/失败及信息)。

wechatNotice
  • 使用 Server酱推送预约结果到微信(可选);

is_booking_enable

  • 判断用户设置中是否启用了该天的预约。

主程序逻辑(if __name__ == "__main__"

  1. 读取 YAML 配置(用户偏好、基础设置、座位配置);

  2. 判断是否启用了预约;

  3. 实例化 SeatAutoBooker;

  4. 登录 → 获取用户信息 → 发起预约;

  5. 结束后关闭浏览器并退出。

适用场景

  • 自动在每天固定时间帮你抢"后天"的图书馆座位;

  • 可配置座位区域与时间段;

  • 登录模拟真实操作,无需手动登录网页;

  • 支持失败重试机制,稳定性好;

  • 可选推送到微信,实时提醒预约结果。


注意

本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。



没有评论:

发表评论

产品经理如何搞副业?不妨试试学编程做网站出海赚美元

我发现能够拿到结果的,大多数是有编程能力的程序员,或者是学习了编程的产品经理。 大家好,我是哥飞。 过去一两年,我观察了很多想要做AI产品的个人,发现能够拿到结果的,大多数是 有编程能力的程序员 ,或者是 学习了编程的产品经理 。 相反,如果你是一个单纯的产品经理,你自己不会...