香港理工大学实验室定时预约

香港理工大学实验室定时预约

文章目录

对爬虫、逆向感兴趣的同学可以查看文章,一对一小班教学(系统理论和实战教程)、提供接单兼职渠道:https://blog.csdn.net/weixin_35770067/article/details/142514698

简介

香港理工大学实验室预约项目,由于平时预约人数较多,因此定制定时预约软件,学校网页非常简单,一个简单的post请求直接搞定。

接单价格

有第三方抽佣,个人到手660,供大家参考。

软件界面

网站预约界面

代码

python 复制代码
# -*- coding: utf-8 -*-
'''
@Time    : 2025-06-23 16:24
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple bs4
'''
import time
import warnings
from bs4 import BeautifulSoup
import requests
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import threading
from datetime import datetime, timedelta
import ctypes
import sys
warnings.filterwarnings('ignore')
def hide_console():
    """隐藏控制台窗口"""
    if sys.platform.startswith('win'):
        handle = ctypes.windll.kernel32.GetConsoleWindow()
        ctypes.windll.user32.ShowWindow(handle, 0)

hide_console()

def create_order(instrument, cookies, auth_token, reservation_details):
    """
    Sends a POST request to create a reservation for the selected instrument.
    """
    # Dynamically set the URL and Referer based on the instrument choice
    if instrument == "fNIRS1":
        target_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_1/single_reservations"
        referer_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_1/single_reservations/new"
    else:  # Default to fNIRS2
        target_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_2/single_reservations"
        referer_url = "https://urfms.polyu.edu.hk/facilities/ubsn/instruments/NIRSport_2_2/single_reservations/new"

    headers = {
        'Host': 'urfms.polyu.edu.hk',
        'Cache-Control': 'max-age=0',
        'Origin': 'https://urfms.polyu.edu.hk',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'Sec-Fetch-Site': 'same-origin',
        'Sec-Fetch-Mode': 'navigate',
        'Referer': referer_url,  # Use the dynamic Referer
        'Cookie': cookies
    }
    data = {
        'authenticity_token': auth_token,
        'order_account': '2204',
        'send_notification': '1',
        'commit': 'Create',
    }
    data.update(reservation_details)

    try:
        res = requests.post(target_url, headers=headers, data=data, verify=False, timeout=5)  # Use the dynamic URL
        res.raise_for_status()
        return res.text
    except requests.exceptions.RequestException:
        return ""


class ReservationApp:
    def __init__(self, root):
        self.root = root

        # --- Variables ---
        self.instrument_var = tk.StringVar(value="fNIRS2")  # Default selection
        self.start_date_var = tk.StringVar(value="13 Jul 2025")
        self.start_hour_var = tk.StringVar(value="9")
        self.start_min_var = tk.StringVar(value="00")
        self.start_meridian_var = tk.StringVar(value="AM")
        self.end_date_var = tk.StringVar(value="13 Jul 2025")
        self.end_hour_var = tk.StringVar(value="10")
        self.end_min_var = tk.StringVar(value="00")
        self.end_meridian_var = tk.StringVar(value="AM")
        self.duration_display_var = tk.StringVar(value="1:00")
        self.cookie_var = tk.StringVar()
        self.auth_token_var = tk.StringVar()
        self.schedule_time_var = tk.StringVar(value=f"{(datetime.now() + timedelta(minutes=1)).strftime('%H:%M:%S')}")
        self.status_var = tk.StringVar(value="Status: Idle")

        self._update_title()  # Set initial title
        self.root.geometry("850x420")  # Adjusted height for the new frame

        self.create_widgets()
        self.create_log_window()

    def _update_title(self):
        """Updates the main window title based on the instrument selection."""
        selected_instrument = self.instrument_var.get()
        self.root.title(f"Reservation Scheduler {selected_instrument}")

    def create_log_window(self):
        self.log_window = tk.Toplevel(self.root)
        self.log_window.title("Live HTML Log")
        self.log_window.geometry("800x600")
        self.log_text = scrolledtext.ScrolledText(self.log_window, wrap=tk.WORD, state=tk.DISABLED)
        self.log_text.pack(expand=True, fill='both')
        self.log_window.protocol("WM_DELETE_WINDOW", self.log_window.withdraw)
        self.log_window.withdraw()

    def update_status(self, message):
        self.status_var.set(message)

    def log_to_gui(self, message):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, message)
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)

    def schedule_reservation(self):
        # Get all required values from GUI
        instrument = self.instrument_var.get()
        cookies = self.cookie_var.get()
        auth_token = self.auth_token_var.get()
        schedule_time_str = self.schedule_time_var.get()

        if not cookies or not auth_token:
            messagebox.showerror("Invalid Input", "Cookie and Authenticity Token fields cannot be empty.")
            return

        try:
            target_time = datetime.strptime(schedule_time_str, '%H:%M:%S').time()
            schedule_dt = datetime.combine(datetime.now().date(), target_time)
            if schedule_dt < datetime.now(): schedule_dt += timedelta(days=1)
            start_dt = schedule_dt - timedelta(seconds=5)
            delay = (start_dt - datetime.now()).total_seconds()
            if delay < 0:
                messagebox.showerror("Invalid Time", "Scheduled time is in the past.")
                return
        except ValueError:
            messagebox.showerror("Invalid Format", "Schedule time must be in HH:MM:SS format.")
            return

        try:
            duration_str = self.duration_display_var.get()
            h, m = map(int, duration_str.split(':')) if ':' in duration_str else (0, int(duration_str))
            total_minutes = h * 60 + m
        except ValueError:
            messagebox.showerror("Invalid Input", "Duration format is incorrect.")
            return

        reservation_details = {
            'reservation[reserve_start_date]': self.start_date_var.get(),
            'reservation[reserve_start_hour]': self.start_hour_var.get(),
            'reservation[reserve_start_min]': self.start_min_var.get(),
            'reservation[reserve_start_meridian]': self.start_meridian_var.get(),
            'reservation[reserve_end_date]': self.end_date_var.get(),
            'reservation[reserve_end_hour]': self.end_hour_var.get(),
            'reservation[reserve_end_min]': self.end_min_var.get(),
            'reservation[reserve_end_meridian]': self.end_meridian_var.get(),
            'reservation[duration_mins]_display': self.duration_display_var.get(),
            'reservation[duration_mins]': str(total_minutes),
        }

        self.schedule_button.config(state=tk.DISABLED)
        self.update_status(f"Status: Scheduled for {schedule_dt.strftime('%Y-%m-%d %H:%M:%S')}. Waiting...")

        task_thread = threading.Thread(
            target=self._execute_task,
            args=(delay, instrument, cookies, auth_token, reservation_details), daemon=True
        )
        task_thread.start()

    def _execute_task(self, delay, instrument, cookies, auth_token, reservation_details):
        time.sleep(delay)

        self.root.after(0, self.update_status, f"Status: Executing for {instrument}... Check log.")
        self.root.after(0, self.log_window.deiconify)
        self.root.after(0, lambda: self.log_to_gui(f"--- Starting Reservation Attempts for {instrument} ---\n"))

        start_time = time.time()
        success = False
        while time.time() - start_time < 60:
            response_html = create_order(instrument, cookies, auth_token, reservation_details)

            timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
            log_entry = f"\n{'=' * 80}\nATTEMPT TIMESTAMP: {timestamp}\n{'=' * 80}\n"

            if response_html:
                soup = BeautifulSoup(response_html, 'html.parser')
                log_entry += soup.prettify()
                if not soup.find('div', class_='errorExplanation222222'):
                    self.root.after(0, self.update_status, "Status: Success! Reservation created.")
                    self.root.after(0, lambda: messagebox.showinfo("Success", "Reservation created successfully!"))
                    self.root.after(0, lambda: self.log_to_gui("\n\n--- RESERVATION SUCCESSFUL ---"))
                    success = True
            else:
                log_entry += "--- [INFO] No response from server or network error ---"

            self.root.after(0, self.log_to_gui, log_entry)
            if success: break
            time.sleep(0.2)

        if not success:
            self.root.after(0, self.update_status, "Status: Finished. 60-second window passed.")
            self.root.after(0, lambda: self.log_to_gui("\n\n--- 60-SECOND EXECUTION WINDOW FINISHED ---"))

        self.root.after(0, lambda: self.schedule_button.config(state=tk.NORMAL))

    def create_widgets(self):
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # --- Instrument Selection Frame ---
        inst_frame = ttk.LabelFrame(main_frame, text="Select Instrument", padding=(10, 5))
        inst_frame.grid(row=0, column=0, columnspan=7, sticky=(tk.W, tk.E), pady=(0, 10))
        ttk.Radiobutton(inst_frame, text="fNIRS1", variable=self.instrument_var, value="fNIRS1",
                        command=self._update_title).pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(inst_frame, text="fNIRS2", variable=self.instrument_var, value="fNIRS2",
                        command=self._update_title).pack(side=tk.LEFT, padx=5)

        # --- Main content frame ---
        content_frame = ttk.Frame(main_frame)
        content_frame.grid(row=1, column=0, columnspan=7)

        ttk.Label(content_frame, text="Start Time", font=('Helvetica', 10, 'bold')).grid(row=0, column=0, sticky=tk.W,
                                                                                         pady=2)
        ttk.Entry(content_frame, textvariable=self.start_date_var, width=15).grid(row=0, column=1)
        ttk.Combobox(content_frame, textvariable=self.start_hour_var, values=list(range(1, 13)), width=4).grid(row=0,
                                                                                                               column=2,
                                                                                                               padx=5)
        ttk.Combobox(content_frame, textvariable=self.start_min_var, values=[f"{i:02d}" for i in range(0, 60, 15)],
                     width=4).grid(row=0, column=3)
        ttk.Combobox(content_frame, textvariable=self.start_meridian_var, values=["AM", "PM"], width=4).grid(row=0,
                                                                                                             column=4,
                                                                                                             padx=5)

        ttk.Label(content_frame, text="End Time", font=('Helvetica', 10, 'bold')).grid(row=1, column=0, sticky=tk.W,
                                                                                       pady=2)
        ttk.Entry(content_frame, textvariable=self.end_date_var, width=15).grid(row=1, column=1)
        ttk.Combobox(content_frame, textvariable=self.end_hour_var, values=list(range(1, 13)), width=4).grid(row=1,
                                                                                                             column=2,
                                                                                                             padx=5)
        ttk.Combobox(content_frame, textvariable=self.end_min_var, values=[f"{i:02d}" for i in range(0, 60, 15)],
                     width=4).grid(row=1, column=3)
        ttk.Combobox(content_frame, textvariable=self.end_meridian_var, values=["AM", "PM"], width=4).grid(row=1,
                                                                                                           column=4,
                                                                                                           padx=5)

        ttk.Label(content_frame, text="Duration", font=('Helvetica', 10, 'bold')).grid(row=0, column=5, padx=(20, 0),
                                                                                       sticky=tk.S)
        ttk.Entry(content_frame, textvariable=self.duration_display_var, width=12).grid(row=0, column=6, padx=5,
                                                                                        sticky=tk.S)
        ttk.Label(content_frame, text='Enter "total minutes" or "hours:minutes"').grid(row=1, column=5, columnspan=2,
                                                                                       padx=(20, 0), sticky=tk.N,
                                                                                       pady=(0, 10))

        ttk.Label(content_frame, text="Cookie", font=('Helvetica', 10, 'bold')).grid(row=2, column=0, sticky=tk.W,
                                                                                     pady=5)
        ttk.Entry(content_frame, textvariable=self.cookie_var, width=90).grid(row=2, column=1, columnspan=6,
                                                                              sticky=tk.W, padx=5)

        ttk.Label(content_frame, text="Authenticity Token", font=('Helvetica', 10, 'bold')).grid(row=3, column=0,
                                                                                                 sticky=tk.W, pady=5)
        ttk.Entry(content_frame, textvariable=self.auth_token_var, width=90).grid(row=3, column=1, columnspan=6,
                                                                                  sticky=tk.W, padx=5)

        ttk.Label(content_frame, text="Schedule Time (HH:MM:SS)", font=('Helvetica', 10, 'bold')).grid(row=4, column=0,
                                                                                                       sticky=tk.W,
                                                                                                       pady=10)
        ttk.Entry(content_frame, textvariable=self.schedule_time_var, width=15).grid(row=4, column=1, sticky=tk.W)

        self.schedule_button = ttk.Button(content_frame, text="Schedule Reservation", command=self.schedule_reservation)
        self.schedule_button.grid(row=5, column=5, columnspan=2, sticky=tk.E, pady=10)

        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E))


if __name__ == '__main__':
    root = tk.Tk()
    app = ReservationApp(root)
    root.mainloop()

sticky=tk.E, pady=10)

        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E))


if __name__ == '__main__':
    root = tk.Tk()
    app = ReservationApp(root)
    root.mainloop()
相关推荐
Sirius.z3 小时前
第R6周:LSTM实现糖尿病探索与预测
python
名字还没想好☜3 小时前
Python f-string 进阶:数字格式化、对齐填充、调试 = 号与嵌套表达式
开发语言·数据库·python·字符串格式化·f-string
·薯条大王3 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
Dxy12393102164 小时前
Python 如何使用 MySQL 的事务
python·mysql
今儿敲了吗8 小时前
Python ——第三方包
笔记·python
麒麟水手8 小时前
国产银河麒麟系统开发实录:跑通Streamlit,我踩过的6个坑
python
麒麟水手9 小时前
麒麟系统部署检查清单:上线前30分钟,逐项自查这4类问题
python
用户03321266636710 小时前
使用 Python 在 PDF 中添加或删除数字签名
python
代码方舟11 小时前
零信任架构实战:基于天远柠檬查出险-登记证API构建自动化车辆履约评估网关
人工智能·python·架构·自动化
估值探索者11 小时前
【Python实时盯盘与预警 #08】成交额突然放大2倍?Python窗口比较抓异动
java·开发语言·python