阿里云:
服务器购买地址
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.部署教程
3.代码如下
import requests
import yaml
import random
from datetime import datetime, timedelta
import json
import os
import logging
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
import 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, 10, 0.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')
这个脚本的是座位自动预约脚本。
读取用户的预约偏好与配置。
登录高校图书馆预约系统(通过 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;
登录 → 获取用户信息 → 发起预约;
结束后关闭浏览器并退出。
适用场景
自动在每天固定时间帮你抢"后天"的图书馆座位;
可配置座位区域与时间段;
登录模拟真实操作,无需手动登录网页;
支持失败重试机制,稳定性好;
可选推送到微信,实时提醒预约结果。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论