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

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

文章目录

对爬虫、逆向感兴趣的同学可以查看文章,一对一小班教学(系统理论和实战教程)、提供接单兼职渠道: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()
相关推荐
一只专注api接口开发的技术猿7 小时前
Open‑Claw 实战|告别页面逆向,快速搭建电商商品监控与数据分析完整方案
大数据·数据库·python·数据挖掘·数据分析
忆~遂愿8 小时前
免密连接+快捷键+虚拟鼠标:ToDesk远程操作AI效率拉满
人工智能·python·深度学习·神经网络·自然语言处理·计算机外设·安全架构
ComPDFKit8 小时前
用 ComPDF DocSlight 提取 PDF 表格:三种样本实测与 Python 接入
开发语言·python·pdf·pdf表格提取
92year8 小时前
我用运筹学穷举了麦当劳全菜单:30 块预算,怎么吃到全局最优?
python·agent·mcp
用户7624117567058 小时前
第三章 流程控制语句
python
东莞市云毅网络有限公司8 小时前
结构化标注缺失怎么查:JSON-LD抽取与每日巡检脚本实现
python·sqlite·自动化运维·geo·数据监测
2601_962966648 小时前
数学与应用数学专业准备风险管理岗,2027届秋招金融知识和工具技能怎么补?
python·sql
码流子9 小时前
02-易迁信创兼容引擎
大数据·数据库·python
kimnoic9 小时前
Python常见模块及其用法示例详解
开发语言·python
databook9 小时前
Scikit-Learn实战:5步搞定PCA降维
python·机器学习·scikit-learn