重庆大学选课脚本---selenium实现

本文旨在分享选课selenium脚本python代码

2024-6-26更新

脚本介绍:

  1. 基于requests(2.31.0),selenium(4.9.0)与google。
  2. 由于编程水平局限,脚本运行可能会出现bug,并非完美封装.
  3. 脚本完全模拟手动选课,受网络环境影响较大,要想更快可以直接向服务器发送请求,但是这不是本次分享的主题(请求参数容易变化...).

抢课具体的程序:

本人运行环境为:python-3.10 pycharm平台

# -*- coding: utf-8 -*-
# @Time : 2023/12/2 22:55
import os

try:
    from selenium import webdriver
except:
    print("没有找到selenium,尝试下载--")
    os.system(f'pip install selenium')
    from selenium import webdriver

import time
from queue import Queue
from datetime import datetime
from threading import Thread
from selenium.webdriver.common.by import By
from selenium.common import TimeoutException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, ElementNotInteractableException


class GetCourse:
    url = "https://my.cqu.edu.cn"

    def __init__(self, courseList, driver=None):
        self.driver = webdriver.Chrome() if driver is None else driver
        self.courseList = courseList

    def wait(self, sensitivity=1, *element):
        # 隐式等待 - 单元素
        while sensitivity > 0:
            try:
                webWait = WebDriverWait(self.driver, 4)
                wait = webWait.until(EC.presence_of_element_located((element)))
                return wait
            except TimeoutException:
                sensitivity -= 1
                try:
                    ele = self.driver.find_element(*element)
                    self.driver.execute_script("arguments[0].scrollIntoView();", ele)
                except Exception as e:
                    print(e)
                print("retry")
        raise NoSuchElementException

    def waits(self, *element):
        # 隐式等待 - 多元素
        count = 0
        while True:
            try:
                webWait = WebDriverWait(self.driver, 4)
                wait = webWait.until(EC.presence_of_all_elements_located((element)))
                return wait
            except TimeoutException:
                count += 1
                if count % 3 == 0:
                    self.driver.refresh()

    def login(self, kind, text):
        while True:
            input = self.wait(10, By.XPATH, f"//app-login-normal//form/div[{kind}]/nz-input-group/input")
            input.clear()
            input.click()
            time.sleep(0.4)
            input.send_keys(text)
            if input.get_attribute("value") == text:
                break

    def initial_cookie(self):
        # 执行登录
        self.driver.get(self.url)
        self.login(1, Properties.ID)
        self.login(2, Properties.pwd)
        self.driver.find_element(By.XPATH, "//*[@id=\"login-normal\"]/div[2]/form/div[6]/div/button").click()

    def do(self, func):
        # 从主界面进入选课界面
        self.driver.get("https://my.cqu.edu.cn/enroll/Home")
        xpathStr = f"//div[contains(@class,\"select-model\")]//div[contains(@class,\"select-list-model\") or contains(@class,\"selected-course-info\")]//button[.//span[contains(text(), \"{func}\")]]"
        while True:
            try:
                self.wait(2, By.XPATH, xpathStr)
                while datetime.now() <= Properties.begin:
                    pass
                self.driver.find_elements(By.XPATH, xpathStr)[-1].click()
                '''
                list_windows = self.driver.window_handles
                self.driver.switch_to.window(list_windows[-1])
                转换页面, 如果发生页面跳转
                '''
                break
            except ElementNotInteractableException:
                self.driver.find_elements(By.XPATH, xpathStr)[0].click()
                break
            except Exception as e:
                print(e)  # 打印Exception信息
                time.sleep(Properties.DELAY_TIME)
                self.driver.get("https://my.cqu.edu.cn/enroll/Home")

    def close(self):
        try:
            self.driver.find_element(By.XPATH, "//i[@aria-label=\"图标: close\"]").click()
        except Exception:
            pass

    def select(self, name):
        # 执行选课操作
        while True:
            try:
                # 选择课程种类
                self.wait(2, By.XPATH, f"//a[@title=\"{name}\"]").click()
                # 选择课程号
                xpath_CID = f"//tbody[@class=\"ant-table-tbody\"]/tr[.//span[text()=\"{self.courseList[name]}\"]]/td[last()]/span"
                self.wait(2, By.XPATH, xpath_CID)
                try:
                    self.driver.find_element(By.XPATH, f"{xpath_CID}[./label]").click()
                except NoSuchElementException:
                    print("{}课程无法选择".format(name))
                    self.close()
                    return False
                break
            except Exception:
                self.driver.refresh()
        # 初步确认
        try:
            self.driver.find_element(By.XPATH,
                                     f"//div[@class=\"select-class-info-modal\"]//button[.//span[contains(text(),\"选\")]]").click()
            # 进一步确认
            self.driver.find_element(By.XPATH,
                                     f"//div[@class=\"ant-modal-confirm-btns\"]//button[.//span[contains(text(), \"确\")]]").click()
        except Exception as e:
            pass
        finally:
            self.close()
        return True

    def isSelected(self, name):
        # 判断是否成功选到目标课程
        had_Selected = self.waits(By.XPATH, "//div[@id=\"resultList\"]//span[@class=\"resultList-name\"]/span/a")
        for i in had_Selected:
            Properties.had_Selected_items.add(i.get_attribute("innerText").strip())
        print("未选课程{}".format(set(self.courseList.keys() - Properties.had_Selected_items)))
        if name in Properties.had_Selected_items:
            return True
        return False

    def circle(self, courseQueue):
        preCourseList = []
        while courseQueue.empty() is False:
            courseName = courseQueue.get()
            if self.select(courseName):
                preCourseList.append(courseName)
        for course in preCourseList:
            if not self.isSelected(course):
                courseQueue.put(course)
        if courseQueue.empty():
            return True

        time.sleep(1)
        self.driver.refresh()
        time.sleep(10)

        return self.circle(courseQueue)

    def run_rand(self, courseQueue):
        self.driver.maximize_window()
        self.initial_cookie()
        time.sleep(Properties.DELAY_TIME)
        self.do("选")
        print(f"时间已到,开始抢课")
        time.sleep(Properties.DELAY_TIME)
        self.circle(courseQueue)
        print("任务完成")


def run(courseList, thread_num, driver=None):
    # 只开几个线程,
    queue = Queue(len(courseList))
    thread_lis = []
    for courseName, v in courseList.items():
        queue.put(courseName)
    for i in range(0, thread_num):
        thread_lis.append(Thread(target=GetCourse(courseList, driver).run_rand, args=(queue,)))
    for thread in thread_lis:
        thread.start()


class Properties:

    # 全局变量
    ID = ""  # 统一认证账号
    pwd = ".."  # 统一认证密码
    try:
        import version_s as props
        ID = props.ID
        pwd = props.password
    except ImportError:
        pass
    had_Selected_items = set()
    begin = datetime.strptime("2024-06-29 8:50:01", "%Y-%m-%d %H:%M:%S")  # 开始抢课时间

    DELAY_TIME = 0.9  # 延迟时间,防止页面未加载完成,如果程序运行不稳定,可以适当增加延迟时间

    # 自定义课程列表  课程名:课程班号
    courseList = {
        "能源大数据与人工智能": "154020-002",
    }

    google_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe" # 你的谷歌浏览器路径

    def __init__(self):
        # 配置selenium参数
        from selenium.webdriver.chrome.service import Service
        service = Service(r"chromedriver.exe")
        options = Options()
        options.binary_location =  self.google_path
        try:
            driver = webdriver.Chrome(options=options, service=service)
        except Exception:
            print("驱动异常,正在下载--")
            from updateDriver import update_driver
            update_driver()
            print("下载完成--")
            driver = webdriver.Chrome(options=options, service=service)
        self.driver = driver


if __name__ == '__main__':
    driver = Properties().driver  # 配置参数

    run(Properties.courseList, thread_num=1, driver=driver)

程序介绍:

Properties:配置类,自定义相关参数:账号,抢课时间,课程列表,延时时间,驱动配置.

课程字典参数定义:

 

获取浏览器驱动的代码:

# -*- coding: utf-8 -*-
# @Time : 2023/8/29 13:48
import requests
import zipfile
import os
import json

root = "."


def removeDirs(path, dir):
    if os.path.exists(os.path.join(path, dir)):
        path = os.path.join(path, dir)
        if os.path.isdir(path):
            os.chdir(path)
            absPath = os.getcwd()
            for inner in os.listdir('.'):
                if os.path.isdir(inner):
                    removeDirs(os.getcwd(), inner)
                    continue
                os.remove(inner)
            os.chdir(absPath[:absPath.rfind("\\")])
            os.removedirs(dir)
        else:
            os.remove(path)


def update_driver():
    url = 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json'
    resp = json.loads(requests.get(url).text)
    for platforms in resp['channels']['Stable']['downloads']['chromedriver']:
        if platforms['platform'] == "win64":
            zips = platforms['url']
            file = requests.get(zips).content
            with open('./chromedriver-win64.zip', 'wb') as f:
                f.write(file)
            zip_file = zipfile.ZipFile('chromedriver-win64.zip')
            name_ = filter(lambda x: x.endswith(".exe"), zip_file.namelist()).__next__()
            zip_file.extract(name_, root)
            if os.path.exists(f'{root}\chromedriver.exe'):
                os.remove(f'{root}\chromedriver.exe')
            zip_file.close()
            os.rename(os.path.join(root, name_), f'{root}\chromedriver.exe')
            removeDirs(root, 'chromedriver-win64')
            removeDirs(root, 'chromedriver-win64.zip')


update_driver()

最后再附上文件目录:

 将两个程序放到同一个文件夹下就行了,如果安装selenium报错,那么请自行搜寻如何安装selenium.

 希望这个脚本可以帮助到你,如果遇到了bug欢迎向我反馈,我会很感激.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值