公司动态
Python Selenium自动化教务系统评教脚本开发实战指南
1. 项目概述与核心需求解析教务系统自动评教这个想法估计在不少大学生和技术爱好者的脑子里都闪过。每到学期末面对那几十门课程、上百个评价项重复点击、机械打分一坐就是大半天确实是个枯燥的体力活。手动评教不仅耗时还容易因为疲劳导致评价不准确甚至漏评。更重要的是这种重复性劳动正是自动化脚本大显身手的地方。这个项目的核心就是利用Python编程语言结合Selenium等自动化测试工具模拟真人操作浏览器自动登录教务系统遍历待评课程并按照预设的逻辑完成所有评价项的填写与提交。它解决的痛点非常明确解放人力提升效率确保评教任务按时、无遗漏地完成。无论是为了节省自己的时间还是出于技术探索的兴趣这个项目都极具实践价值。它适合有一定Python基础对网页自动化感兴趣的同学。你不需要是爬虫或测试专家只要会安装Python库、能看懂基本的HTML结构就能跟着一步步实现。整个过程你会接触到浏览器自动化、网页元素定位、模拟用户交互等核心技能这些技能在数据采集、UI自动化测试等领域都是相通的。2. 技术选型与工具准备要实现自动评教我们需要一套能“操纵”浏览器的工具。这里Selenium是当之无愧的首选。它是一个强大的浏览器自动化框架支持多种浏览器Chrome, Firefox, Edge等可以模拟几乎所有的用户操作点击、输入、下拉选择、滚动等。相比于直接使用requests库模拟HTTP请求Selenium操作的是真实的浏览器环境能更好地处理JavaScript动态加载的页面、验证码虽然本项目不涉及复杂验证码破解但框架支持以及基于Cookie/Session的登录状态保持这对于需要登录且交互复杂的教务系统来说更加可靠和稳定。我们的技术栈非常简单清晰编程语言Python 3.7。Python语法简洁库生态丰富是自动化任务的绝佳选择。核心库Selenium。通过pip install selenium即可安装。浏览器驱动WebDriver。这是Selenium控制浏览器的桥梁。你需要根据你使用的浏览器版本下载对应的驱动如ChromeDriver for Chrome, geckodriver for Firefox, msedgedriver for Edge。驱动版本必须与浏览器版本匹配这是新手最容易踩的坑之一。集成开发环境IDE推荐使用VSCode或PyCharm。它们能提供代码提示、调试等功能提升开发效率。对于纯新手从IDLE或简单的文本编辑器开始也完全可以。注意下载WebDriver后需要将其所在目录添加到系统的PATH环境变量中或者将驱动文件直接放在Python的安装目录下与python.exe同级这样Selenium才能找到它。一个更稳妥的做法是在代码中指定驱动的绝对路径。除了这些你还需要准备好你的教务系统登录账号和密码。强烈建议在项目初期使用测试账号或者确保你的脚本逻辑足够稳健避免因脚本错误导致账号被锁或提交异常数据。3. 环境搭建与Selenium配置实战光说不练假把式我们立刻开始动手搭建环境。假设我们选择最流行的Chrome浏览器作为自动化载体。3.1 安装Python与Selenium库首先确保你的电脑上安装了Python。打开命令行CMD或Terminal输入python --version检查。如果没有安装去Python官网下载安装包安装时务必勾选“Add Python to PATH”。安装Selenium库只需一行命令pip install selenium如果下载速度慢可以使用国内镜像源例如pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple3.2 下载与配置ChromeDriver查看Chrome版本打开Chrome浏览器点击右上角三个点 - 帮助 - 关于Google Chrome记下版本号例如124.0.6367.91。下载对应驱动访问ChromeDriver的官方下载站点或国内镜像站。找到与你的Chrome浏览器主版本号例如124完全一致的ChromeDriver版本进行下载。配置驱动方法一推荐灵活将下载的chromedriver.exe文件放在一个固定的、你容易记住的目录比如D:\WebDriver\。然后在Python代码中使用绝对路径初始化。方法二简单将chromedriver.exe直接复制到Python的安装目录下例如C:\Users\你的用户名\AppData\Local\Programs\Python\Python39\。3.3 编写第一个自动化测试脚本让我们写一个简单的脚本来验证环境是否配置成功。这个脚本会打开百度搜索一个关键词。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 指定ChromeDriver的路径如果已加入PATH则不需要此句 driver_path r‘D:\WebDriver\chromedriver.exe‘ # 请修改为你的实际路径 # 创建Chrome浏览器驱动实例 driver webdriver.Chrome(executable_pathdriver_path) # 如果驱动在PATH中直接 webdriver.Chrome() try: # 打开百度首页 driver.get(‘https://www.baidu.com‘) print(‘当前页面标题‘, driver.title) # 定位搜索框输入关键词 search_box driver.find_element(By.ID, ‘kw‘) # 百度搜索框的id是‘kw‘ search_box.send_keys(‘Selenium自动化‘) search_box.send_keys(Keys.RETURN) # 模拟按下回车键 # 等待2秒查看结果 time.sleep(2) print(‘搜索后页面标题‘, driver.title) finally: # 等待5秒后关闭浏览器 time.sleep(5) driver.quit()运行这个脚本如果能看到Chrome浏览器自动打开、访问百度、输入文字并搜索最后自动关闭那么恭喜你Selenium环境配置成功实操心得在开发调试阶段可以在driver.quit()前加上time.sleep(30)之类的长等待方便你观察页面状态检查元素定位是否正确。正式脚本中应使用更智能的等待方式后面会讲。4. 教务系统页面分析与元素定位策略环境搭好了接下来是最关键的一步分析你的教务系统页面。不同的学校可能使用不同的系统如强智、清元优软、金智等但评教流程大同小异。你需要手动走一遍评教流程并用浏览器的开发者工具F12打开仔细观察。4.1 登录页面分析打开教务系统登录页分析登录表单。找到用户名和密码输入框右键点击输入框选择“检查”。在Elements面板中查看该输入框的HTML属性。常见的定位依据有id最理想的定位方式如id“username”。name如name“userAccount”。class name但class可能不唯一需谨慎。XPath或CSS Selector当元素没有明显唯一标识时使用。找到登录按钮同样检查其属性。注意验证码如果系统有验证码本项目暂不处理复杂图像识别。你可以考虑手动输入验证码通过time.sleep(30)给脚本预留手动操作时间。研究验证码接口但难度较大且可能违反系统规定。4.2 评教主页面与课程列表分析登录成功后找到“教学评价”或“学生评教”入口并进入。你会看到一个待评价课程列表。课程列表结构通常是一个table或一系列div/li。你需要找到包裹每个课程项的HTML元素。“评价”或“进入”按钮每个课程旁会有一个按钮。检查这个按钮的定位信息。通常可以通过课程名称的相邻关系或列表索引来定位。4.3 评价表单页面分析点击进入某门课程的评价页面。这是最复杂的部分。评价项通常是多个评分题如“教学态度”、“教学内容”形式可能是单选按钮input type“radio”、下拉列表select或滑块。你需要找出每个评分题对应的所有选项。选项定位对于单选按钮它们通常有相同的name属性代表同一道题不同的value属性代表不同分值。例如name“q1” value“A”对应优秀name“q1” value“B”对应良好。文本域可能还有主观评价的文本框textarea。提交按钮最后是“提交”或“保存”按钮。4.4 Selenium元素定位方法详解Selenium提供了多种定位元素的方法对应By类的不同属性定位方式示例代码适用场景优点缺点By.IDfind_element(By.ID, “username”)元素有唯一id属性时定位最快、最准确依赖id存在且唯一By.NAMEfind_element(By.NAME, “password”)表单元素常用较为常见和稳定可能不唯一By.CLASS_NAMEfind_element(By.CLASS_NAME, “btn-submit”)通过CSS类定位可直接使用样式类名类名常不唯一需组合使用By.TAG_NAMEfind_element(By.TAG_NAME, “input”)定位特定标签类型简单通常返回多个元素需进一步筛选By.LINK_TEXTfind_element(By.LINK_TEXT, “教学评价”)精确匹配超链接文本对链接定位直接文本必须完全匹配By.PARTIAL_LINK_TEXTfind_element(By.PARTIAL_LINK_TEXT, “评价”)模糊匹配链接文本容错性稍好可能匹配到多个By.XPATHfind_element(By.XPATH, ‘//*[id“courseList”]/div[1]/button’)功能最强大的定位方式非常灵活可定位几乎任何元素语法相对复杂路径可能因页面微小改动而失效By.CSS_SELECTORfind_element(By.CSS_SELECTOR, “#courseList .btn-evaluate”)类似CSS选择器语法简洁效率通常比XPATH高需要了解CSS选择器语法核心技巧优先使用ID和NAME因为它们通常最稳定。如果不行尝试组合CSS_SELECTOR例如“input[name‘q1’][value‘A’]”可以精准定位到名为q1且值为A的单选按钮。XPATH是最后的杀手锏但尽量使用相对路径和属性结合避免使用绝对路径如/html/body/div[5]/div[2]/...因为绝对路径极其脆弱。5. 自动评教脚本核心逻辑与代码实现掌握了定位方法我们就可以开始构建完整的自动评教脚本了。脚本的逻辑流程如下初始化浏览器驱动打开登录页。定位并填写用户名、密码点击登录。等待页面跳转定位并点击“教学评价”入口。在评价列表页面获取所有待评价课程的元素列表。循环遍历每个课程 a. 定位并点击该课程的“进入评价”按钮。 b. 在新打开的标签页或页面中定位所有评价项。 c. 根据预设评分逻辑如全部选“优秀”或随机选择对每个评价项进行操作点击单选按钮、选择下拉选项等。 d. 定位并点击“提交”按钮。 e. 处理提交后的确认对话框如果有。 f. 关闭当前评价标签页或返回列表页。所有课程评价完毕后关闭浏览器。下面是一个高度简化的代码框架展示了核心步骤。请注意其中的选择器ID、XPath等需要你根据自己学校的教务系统实际情况进行替换。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 import time # 初始化浏览器 driver webdriver.Chrome() # 假设驱动已在PATH中 driver.implicitly_wait(10) # 设置隐式等待10秒 wait WebDriverWait(driver, 15) # 设置显式等待对象最长等15秒 # 1. 打开登录页 login_url ‘你的教务系统登录页地址‘ driver.get(login_url) # 2. 登录 username_input driver.find_element(By.ID, ‘usernameInputId‘) # 替换为实际ID password_input driver.find_element(By.ID, ‘passwordInputId‘) # 替换为实际ID login_button driver.find_element(By.ID, ‘loginButtonId‘) # 替换为实际ID username_input.send_keys(‘你的学号‘) password_input.send_keys(‘你的密码‘) login_button.click() # 3. 进入评教模块 (示例通过链接文本点击) try: eval_link wait.until(EC.element_to_be_clickable((By.LINK_TEXT, ‘教学评价‘))) eval_link.click() except Exception as e: print(“未找到‘教学评价’链接尝试其他方式...“) # 可以尝试通过XPath或CSS定位 # driver.find_element(By.XPATH, ‘//a[contains(href, “evaluate”)]‘).click() # 4. 获取待评课程列表 (示例假设每个课程项是一个class为‘course-item’的div) time.sleep(3) # 等待列表加载实际应用应使用显式等待 course_items driver.find_elements(By.CLASS_NAME, ‘course-item‘) # 替换为实际选择器 print(f“找到 {len(course_items)} 门待评价课程“) # 5. 遍历课程进行评价 for index, item in enumerate(course_items): print(f“正在处理第 {index1} 门课程...“) # 在列表页点击“进入评价”按钮 (假设按钮在course-item内部) try: # 重新定位当前课程项的按钮防止元素过时 eval_btn item.find_element(By.CLASS_NAME, ‘btn-evaluate‘) # 替换为实际选择器 eval_btn.click() except Exception as e: print(f“第 {index1} 门课程找不到评价按钮跳过。错误{e}“) continue # 等待新窗口/页面打开并切换到新窗口 time.sleep(2) original_window driver.current_window_handle for window_handle in driver.window_handles: if window_handle ! original_window: driver.switch_to.window(window_handle) break # 现在在新页面评价表单页 try: # 示例评价项1 - 教学态度选择‘优秀’ (value‘A’) q1_radio wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, “input[name‘q1‘][value‘A‘]“))) # 如果单选按钮不可见可能需要先滚动到元素位置 driver.execute_script(“arguments[0].scrollIntoView(true);“, q1_radio) time.sleep(0.5) q1_radio.click() # 示例评价项2 - 教学内容选择‘良好’ (value‘B’) q2_radio driver.find_element(By.CSS_SELECTOR, “input[name‘q2‘][value‘B‘]“) q2_radio.click() # 示例主观评价文本框如果有 try: comment_box driver.find_element(By.NAME, ‘suggestions‘) comment_box.send_keys(‘老师讲课认真内容充实。‘) # 预设评语 except: print(“未找到主观评价框跳过。“) # 提交评价 submit_btn driver.find_element(By.ID, ‘submitButtonId‘) # 替换为实际ID submit_btn.click() # 处理可能的确认弹窗 time.sleep(1) try: alert driver.switch_to.alert alert.accept() # 点击确认 print(“已处理提交确认弹窗。“) except: pass # 没有弹窗则继续 print(f“第 {index1} 门课程评价提交成功“) except Exception as e: print(f“第 {index1} 门课程评价过程中出错{e}“) finally: # 关闭当前评价页切换回主窗口 driver.close() driver.switch_to.window(original_window) # 等待列表页稳定 time.sleep(2) # 6. 所有课程评价完成 print(“\n所有课程评价已完成“) time.sleep(3) driver.quit()6. 高级技巧与稳定性优化上面的基础脚本能跑起来但要在各种网络波动、页面加载速度不同的真实环境中稳定运行还需要加入一些“润滑剂”和“安全气囊”。6.1 智能等待策略time.sleep()是固定等待效率低下且不可靠。Selenium提供了更优的等待方式隐式等待 (Implicit Wait)driver.implicitly_wait(10)设置一个全局超时时间。在查找任何元素时如果元素没有立即出现WebDriver会轮询DOM最多10秒。注意设置一次即可对整个driver生命周期生效但和显式等待混用可能导致超时时间叠加。显式等待 (Explicit Wait)针对某个特定条件进行等待更加灵活精准。这是推荐的主要等待方式。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 15) # 最长等待15秒 # 等待元素可点击 button wait.until(EC.element_to_be_clickable((By.ID, “myButton“))) # 等待元素出现在DOM中 element wait.until(EC.presence_of_element_located((By.NAME, “myElement“))) # 等待元素可见 element wait.until(EC.visibility_of_element_located((By.CLASS_NAME, “myClass“))) button.click()6.2 处理动态加载与iframe动态加载内容有些列表是滚动到底部才加载更多懒加载。此时简单的find_elements可能只能获取到初始的一部分。你需要模拟滚动操作driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);“) time.sleep(2) # 等待新内容加载 # 再次查找元素iframe/框架页如果评价表单嵌在iframe里你必须先切换到该iframe内才能操作其中的元素。# 通过ID或索引切换 driver.switch_to.frame(‘iframe_id‘) # 或者 driver.switch_to.frame(0) # 切换到第一个iframe # 在iframe内操作... # 操作完毕后切回主文档 driver.switch_to.default_content()6.3 评分逻辑的多样化与人性化全部打满分优秀可能过于明显。可以设计更“人性化”的评分逻辑随机评分在“优秀”、“良好”之间随机选择模拟真实评价的波动。import random options [‘A‘, ‘B‘, ‘C‘] # 对应优秀、良好、中等 selected_value random.choice(options) driver.find_element(By.CSS_SELECTOR, f“input[name‘q1‘][value‘{selected_value}‘]“).click()权重评分让“优秀”的概率更高但偶尔出现“良好”。selected_value random.choices([‘A‘, ‘B‘], weights[0.8, 0.2])[0]主观评语库准备一个评语列表每次随机选取一条填入文本框避免千篇一律。6.4 异常处理与日志记录健壮的程序必须能处理异常并记录下发生了什么。import logging logging.basicConfig(levellogging.INFO, format‘%(asctime)s - %(levelname)s - %(message)s‘) for index, item in enumerate(course_items): try: # ... 评价操作 ... logging.info(f“第 {index1} 门课程评价成功。“) except NoSuchElementException as e: logging.error(f“第 {index1} 门课程找不到页面元素。错误信息{e}“) # 可以截图保存便于事后分析 driver.save_screenshot(f‘error_course_{index1}.png‘) continue # 跳过当前课程继续下一个 except TimeoutException as e: logging.warning(f“第 {index1} 门课程页面加载超时重试中...“) # 可以加入重试逻辑 continue except Exception as e: logging.critical(f“第 {index1} 门课程发生未知错误{e}“) # 严重错误可能考虑中断脚本 break7. 常见问题排查与实战避坑指南在实际操作中你几乎一定会遇到下面这些问题。这里我把踩过的坑和解决方案整理出来。7.1 元素定位失败NoSuchElementException这是最常见的问题。原因1页面尚未加载完成。解决在find_element操作前增加等待。务必使用显式等待WebDriverWait代替固定的time.sleep。原因2定位器Selector写错了或已过时。解决使用浏览器开发者工具的Console面板测试你的定位器。例如在Console中输入$$(“input[name‘q1‘]”)对应CSS或$x(“//input[name‘q1‘]”)对应XPath看是否能正确选中元素。确保定位器能唯一标识目标元素。原因3元素在iframe或shadow DOM内。解决检查元素是否在iframe标签内如果是先用driver.switch_to.frame()切换进去。原因4元素是动态生成的DOM结构发生了变化。解决尝试使用更稳定的定位方式例如通过父元素的稳定ID或属性结合相对路径来定位子元素。避免使用绝对索引如div[3]。7.2 点击或交互无效ElementNotInteractableException原因1元素被遮挡例如被弹窗、其他元素覆盖。解决等待遮挡物消失或者使用JavaScript直接点击driver.execute_script(“arguments[0].click();“, element)。原因2元素不可见例如在视窗外或style“display: none;”。解决先滚动到元素所在位置driver.execute_script(“arguments[0].scrollIntoView(true);“, element)然后再操作。对于display:none的元素通常无法直接交互需要检查是否有其他触发其显示的按钮。原因3页面有多个相同定位的元素你操作的不是目标。解决find_element只返回第一个匹配项。使用find_elements获取列表通过索引或遍历找到正确的那个。7.3 浏览器被检测为自动化工具一些网站会检测浏览器是否被Selenium等工具控制。现象无法登录或登录后立即跳转/退出。解决使用undetected-chromedriver库这是一个修改版的ChromeDriver可以规避一些常见的检测。安装pip install undetected-chromedriver使用时导入import undetected_chromedriver as uc然后用driver uc.Chrome()启动。添加实验性选项在启动Chrome时添加一些参数隐藏自动化特征。from selenium.webdriver.chrome.options import Options options Options() options.add_argument(‘--disable-blink-featuresAutomationControlled‘) options.add_experimental_option(“excludeSwitches“, [“enable-automation“]) options.add_experimental_option(‘useAutomationExtension‘, False) driver webdriver.Chrome(optionsoptions) # 同时执行一个JavaScript来覆盖navigator.webdriver属性 driver.execute_script(“Object.defineProperty(navigator, ‘webdriver‘, {get: () undefined})“)7.4 验证码处理如果登录或提交时有验证码会极大增加自动化难度。简单数字/字母验证码可以考虑使用OCR库如pytesseract进行识别但识别率受图片复杂度影响很大且需要额外处理图像二值化、去噪等。复杂验证码滑块、点选、语序基本超出了简单自动化脚本的范畴可能需要借助第三方打码平台涉及费用和稳定性或者考虑半自动化方案。半自动化方案推荐在脚本运行到验证码步骤时暂停并提示用户手动输入。可以通过input()函数在控制台等待输入或者用time.sleep(30)留出足够时间手动操作浏览器。7.5 网络不稳定或页面超时解决合理设置WebDriver的超时时间并加入重试机制。from selenium.common.exceptions import TimeoutException import time max_retries 3 for attempt in range(max_retries): try: driver.get(url) # ... 后续操作 ... break # 成功则跳出重试循环 except TimeoutException: print(f“第{attempt1}次尝试超时{‘重试中...‘ if attempt max_retries-1 else ‘已超过最大重试次数。‘}“) if attempt max_retries - 1: raise # 重试次数用尽抛出异常 time.sleep(2) # 等待一下再重试8. 脚本的封装、调度与安全建议一个完整的项目不仅仅是写一段脚本还要考虑如何方便地使用和维护它。8.1 配置与代码分离不要把账号密码、网址等敏感信息硬编码在脚本里。创建一个config.py或config.ini文件来管理配置。# config.py USERNAME ‘你的学号‘ PASSWORD ‘你的密码‘ LOGIN_URL ‘https://jwxt.yourschool.edu.cn‘在主脚本中导入配置import config username_input.send_keys(config.USERNAME)8.2 使用函数和类进行封装将不同功能模块化使代码更清晰、易维护。class JwxtAutoEvaluator: def __init__(self, username, password): self.driver None self.username username self.password password self.wait None def setup_driver(self): # 初始化浏览器和等待对象 options webdriver.ChromeOptions() # ... 添加选项 ... self.driver webdriver.Chrome(optionsoptions) self.driver.implicitly_wait(10) self.wait WebDriverWait(self.driver, 15) def login(self): # 登录逻辑 pass def get_course_list(self): # 获取课程列表逻辑 pass def evaluate_single_course(self, course_element): # 评价单门课程逻辑 pass def run(self): # 主运行逻辑 self.setup_driver() try: self.login() courses self.get_course_list() for course in courses: self.evaluate_single_course(course) finally: self.driver.quit() if __name__ ‘__main__‘: evaluator JwxtAutoEvaluator(config.USERNAME, config.PASSWORD) evaluator.run()8.3 定时任务与自动化调度如果你希望脚本在特定时间如评教期开始后自动运行可以使用系统级的任务调度。Windows使用“任务计划程序”。macOS/Linux使用cron。 你需要创建一个批处理文件.bat或Shell脚本.sh来调用你的Python脚本然后在任务计划中设置它。8.4 重要的安全与合规提醒这是最重要的一部分请务必仔细阅读遵守校规在运行任何自动化脚本前务必仔细阅读你所在学校关于教务系统使用的规定。明确是否允许自动化操作。本教程仅供技术学习交流请勿用于任何可能违反规定或干扰系统正常运行的用途。尊重服务器负载在脚本中加入随机延迟time.sleep(random.uniform(1, 3))模拟真人操作间隔避免高频请求对服务器造成压力。保护个人隐私妥善保管包含账号密码的配置文件不要上传到公开的代码仓库如GitHub。使用.gitignore文件忽略配置文件。责任自负自动化操作存在风险如因脚本逻辑错误导致评价数据异常如全部零分、重复提交等后果需自行承担。请在运行前充分测试可以先对一两门不重要的课程进行试运行。最后技术是工具如何使用它取决于你。通过这个项目你不仅学会了Selenium自动化更重要的是掌握了分析问题、分解流程、编写稳健代码的思维方式。在实际操作中耐心和细致是成功的关键多使用print语句输出调试信息多利用浏览器的开发者工具进行观察和分析遇到问题就回到这里来对照排查。