阿里云:
服务器购买地址
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=2019052.部署教程
3.代码如下
import requestsimport yamlimport randomfrom datetime import datetime, timedeltaimport jsonimport osimport loggingfrom 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 timelogging.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 = Noneself.resp = Noneself.user_data = Nonelogging.info('Creating SeatAutoBooker object')self.un = os.environ["SCHOOL_ID"].strip() # 学号print("使用用户:{}".format(self.un))self.pd = os.environ["PASSWORD"].strip() # 密码self.SCKey = Nonetry: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, 10, 0.5)self.cookie = Noneself.cfg = booker_configdef 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) - 10for 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.secondsif 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.cookieprint(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.cookielogging.info("登录成功!")except Exception as e:logging.error(f"登录失败:{e}")return -1return 0def get_user_info(self):logging.info('Getting user info')headers = self.cfg["headers"]headers['Cookie'] = self.cookietry: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 -1print("获取用户数据成功")return 0def 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 Truereturn Falseif __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')
这个脚本的是座位自动预约脚本。
读取用户的预约偏好与配置。
登录高校图书馆预约系统(通过 Selenium 控制 Chrome)。
判断是否到了开放预约的时间。
获取用户 ID。
向预约系统发送 POST 请求提交预约。
可选通过 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__")
读取 YAML 配置(用户偏好、基础设置、座位配置);
判断是否启用了预约;
实例化 SeatAutoBooker;
登录 → 获取用户信息 → 发起预约;
结束后关闭浏览器并退出。
适用场景
自动在每天固定时间帮你抢"后天"的图书馆座位;
可配置座位区域与时间段;
登录模拟真实操作,无需手动登录网页;
支持失败重试机制,稳定性好;
可选推送到微信,实时提醒预约结果。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论