当前位置: 首页 > news >正文

上海应用大学网课自动化学习脚本(基于Python selenium)代码重构为GUI界面 —— 技术笔记

在之前原有功能的基础上,新增了基于 PyQt5 的图形化用户界面(GUI),提升了操作体验和可视化程度,主要功能包括:

1. 日志显示区

界面上方新增一个 日志窗口(QTextEdit)。

程序运行过程中产生的所有提示、执行进度、错误信息都会实时输出到日志区。

日志会自动滚动到最新位置,方便用户查看。

2. 控制区(三个主要组件)

输入框(QLineEdit)

用于输入或修改 Entercourse 的 XPath。

这个 XPath 是进入具体课程的关键定位符,用户可根据需要手动修改。

“开始自动执行”按钮(QPushButton)

点击后会自动启动 Edge 浏览器并打开指定学习网站。

程序自动完成以下步骤:

切换到密码登录页面

自动输入账号密码并登录

点击“查看全部”进入课程列表

使用输入框中的 XPath 定位并进入指定课程

开始播放课程视频,并进入自动刷课逻辑

“关闭浏览器”按钮(QPushButton)

点击后会停止自动执行线程,并关闭当前 Edge 浏览器。

保证不会因为强制关闭而导致残留的后台浏览器进程。

3. 备注说明区

在控制区按钮下方新增了一行 说明性文本(QLabel):

“备注: Entercourse XPath 为应刷课程的 XPath”

主要是帮助用户理解输入框的用途,避免误操作。

重构后代码如下:

import sys
import time
import threading
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QLineEdit, QLabel
from PyQt5.QtCore import pyqtSignal, QObject
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import StaleElementReferenceException#***********************************GUI开发版:上海应用大学网课自动刷视频***********************************# ---------------- 日志信号 ----------------
class Logger(QObject):log_signal = pyqtSignal(str)def __init__(self):super().__init__()def log(self, msg):self.log_signal.emit(msg)# ---------------- Selenium 自动执行线程 ----------------
class AutoPlayThread(threading.Thread):def __init__(self, driver, entercourse_xpath, logger):super().__init__()self.driver = driverself.entercourse_xpath = entercourse_xpathself.running = Trueself.logger = loggerdef log(self, msg):self.logger.log(msg)# 安全点击def safe_click(self, xpath):for _ in range(3):if not self.running:return Falsetry:element = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, xpath)))element.click()return Trueexcept StaleElementReferenceException:time.sleep(1)return False# 安全获取文本def safe_get_text(self, xpath):for _ in range(3):if not self.running:return ""try:element = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, xpath)))return element.text.strip()except StaleElementReferenceException:time.sleep(1)return ""def time_to_seconds(self, time_str):try:h, m, s = map(int, time_str.split(":"))return h*3600 + m*60 + sexcept:return 0# ---------------- 自动化功能 ----------------def jumpPasswd(self):if self.safe_click('//*[@id="app"]/div/div/div/div[1]/div[2]/div/div[1]/div[1]/div/label[2]'):self.log("✅ 密码登录跳转成功")else:self.log("❌ 密码登录跳转失败")def login(self):try:username_input = self.driver.find_element(By.XPATH,'//*[@id="app"]/div/div/div/div[1]/div[2]/div/div[1]/div[2]/div/form/div[1]/div/div/input')password_input = self.driver.find_element(By.XPATH,'//*[@id="app"]/div/div/div/div[1]/div[2]/div/div[1]/div[2]/div/form/div[2]/div/div[1]/input')login_button = self.driver.find_element(By.XPATH,'//*[@id="app"]/div/div/div/div[1]/div[2]/div/div[1]/div[2]/div/div/div/button')time.sleep(1)username_input.send_keys("15346111559")password_input.send_keys("Xzl19980216")login_button.click()time.sleep(3)self.log("✅ 登录成功")except Exception as e:self.log(f"❌ 登录失败: {e}")def Viewall(self):if self.safe_click('//*[@id="studentHomeStep4"]/div/div[1]/div/a'):self.log("✅ 进入查看全部成功")time.sleep(2)else:self.log("❌ 进入查看全部失败")def Entercourse(self):if self.safe_click(self.entercourse_xpath):self.log("✅ 进入课程成功")time.sleep(2)else:self.log("❌ 进入课程失败")def ClicklearnVideo(self):try:self.driver.switch_to.window(self.driver.window_handles[-1])if self.safe_click('//*[@id="app"]/div/div[1]/div[2]/div[1]/div/div[2]/div/div[2]/div/div/div[2]/div/div/div[2]/div/div[1]/div[2]/div[2]/div[1]/button'):self.log("🎬 视频开始播放")time.sleep(2)else:self.log("❌ 播放视频失败")except Exception as e:self.log(f"❌ 播放视频失败: {e}")def click_next_chapter(self):if self.safe_click('//*[@id="app"]/div/div[1]/div[1]/div[3]/div[3]/button'):self.log("➡️ 跳转到下一章节")time.sleep(5)self.ClicklearnVideo()else:self.log("❌ 无法点击“下一章节”")def auto_play_videos(self):while self.running:try:time.sleep(5)Btime_str = self.safe_get_text('//*[@id="app"]/div/div[1]/div[2]/div[1]/div/div[2]/div/div[2]/div/div/div[3]/div[2]/span[2]')Etime_str = self.safe_get_text('//*[@id="app"]/div/div[1]/div[2]/div[1]/div/div[2]/div/div[2]/div/div/div[3]/div[2]/span[3]')if not Btime_str or not Etime_str:self.log("⚠️ 未检测到时间,跳转下一章节")self.click_next_chapter()continueBtime_seconds = self.time_to_seconds(Btime_str) / 0.7Etime_seconds = self.time_to_seconds(Etime_str)self.log(f"🎞 观看时长应达到: {Btime_seconds}秒")self.log(f"🎞 当前已观看: {Etime_seconds}秒")if Etime_seconds >= Btime_seconds:self.log("✅ 视频播放完成,准备跳转下一章")self.click_next_chapter()except Exception as e:self.log(f"❌ 播放出错: {e}")self.driver.refresh()time.sleep(3)self.ClicklearnVideo()def stop(self):self.running = Falsetry:self.driver.quit()self.log("🛑 浏览器已关闭")except:pass# ---------------- GUI 界面 ----------------
class LearnInGUI(QWidget):def __init__(self):super().__init__()self.setWindowTitle("上海应用大学网课自动刷视频")self.setGeometry(100, 100, 800, 600)self.thread = Noneself.driver = Noneself.logger = Logger()self.logger.log_signal.connect(self.log)layout = QVBoxLayout()self.setLayout(layout)# 日志显示self.log_text = QTextEdit()self.log_text.setReadOnly(True)layout.addWidget(self.log_text)# 控制区域control_layout = QHBoxLayout()control_layout.addWidget(QLabel("Entercourse XPath:"))self.entercourse_input = QLineEdit('//*[@id="app"]/div/div[2]/div[1]/div[2]/div[1]/div[1]/div[2]/div[2]/a')control_layout.addWidget(self.entercourse_input)self.start_btn = QPushButton("开始自动执行")self.start_btn.clicked.connect(self.start_auto)control_layout.addWidget(self.start_btn)self.stop_btn = QPushButton("关闭浏览器")self.stop_btn.clicked.connect(self.stop_browser)control_layout.addWidget(self.stop_btn)layout.addLayout(control_layout)# **新增一行文本备注**self.xpath_note = QLabel("备注: Entercourse XPath为应刷课程的 XPath")layout.addWidget(self.xpath_note)def log(self, msg):self.log_text.append(msg)self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())def start_auto(self):if self.thread is None:edge_options = webdriver.EdgeOptions()edge_options.use_chromium = Trueself.driver = webdriver.Edge(options=edge_options)self.driver.get("https://www.learnin.com.cn/#/login")time.sleep(5)entercourse_xpath = self.entercourse_input.text().strip()self.thread = AutoPlayThread(self.driver, entercourse_xpath, self.logger)# 按顺序执行初始化任务self.thread.jumpPasswd()self.thread.login()self.thread.Viewall()self.thread.Entercourse()self.thread.ClicklearnVideo()# 启动自动播放线程threading.Thread(target=self.thread.auto_play_videos, daemon=True).start()self.log("🚀 自动执行开始")def stop_browser(self):if self.thread:self.thread.stop()self.thread = Noneself.log("🛑 自动执行已停止")# ---------------- 主程序 ----------------
if __name__ == "__main__":app = QApplication(sys.argv)gui = LearnInGUI()gui.show()sys.exit(app.exec_())
http://www.hskmm.com/?act=detail&tid=15466

相关文章:

  • 在K8S中,Deployment⽀持扩容吗?它与HPA有什么区别?
  • 开源语音识别FunASR入门详解
  • AT_abc201_f [ABC201F] Insertion Sort 题解
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • c语言动态内存分配
  • 2025.9.24——1橙
  • AT_arc172_d [ARC172D] Distance Ranking
  • Python爬虫实现大乐透历史数据抓取
  • 【读书笔记】《深入理解计算机系统(原书第三版)》第一章 计算机系统漫游
  • 如何将PPT每一页批量导出为高清JPG图片?一文讲清处理流程
  • 实用指南:计算机视觉:基于YOLOv11 实例分割与OpenCV 在 Java 中的实现图像实例分割
  • Java实现双色球历史是否中奖查询
  • ABC424 游记(VP)
  • Java实现大乐透历史是否中奖查询
  • 阿德勒的课题分离是很好用的东西
  • 别再混淆 PHP8.1 中纤程 Fibers 和协程 Coroutines 了 一文搞懂它们的区别
  • 主要测试的测试用例
  • 详细介绍Seata的AT模式分布式事务
  • VMware VeloCloud 漏洞分析:未授权远程代码执行全链条攻破
  • 【GitHub每日速递 250924】18 个 AI 投资大师齐上阵!这个开源对冲基金让你看透市场底牌
  • HJ9 提取不重复的整数
  • 2025年国家科技奖初评公布(科技进步奖)
  • 2025年国家科技奖初评公布(科技发明奖)
  • 12
  • 2025年国家科技奖初评公布(自然科学奖)
  • 近端策略优化算法PPO的核心概念和PyTorch实现详解
  • JAX快速上手:从NumPy到GPU加速的Python高性能计算库入门教程
  • Memento:基于记忆无需微调即可让大语言模型智能体持续学习的框架
  • 记录一次附加属性失效全过程
  • Java 与物联网(IoT):边缘计算与智能终端应用