Selenium自动化实战:Python实现URP教务系统教学评估与成绩查询 1. 项目缘起从手动点击到自动化评估每到学期末最让人头疼的事情之一就是登录学校的URP教务系统给各门课程的老师完成教学评估然后才能查看自己的期末成绩。这个流程本身不复杂但架不住它步骤繁琐、网络拥堵而且必须在规定时间内完成。手动操作时你得一遍遍刷新登录页面忍受验证码的折磨然后机械地点击几十个“非常满意”的选项最后才能看到那个心心念念的成绩单。这个过程不仅耗时还容易因为网络卡顿或误操作而中断体验极差。于是一个很自然的想法就冒出来了能不能让程序自动完成这一切用脚本模拟人的操作自动登录、自动完成评估、自动获取并保存成绩。这不仅能解放双手还能确保流程的稳定性和可重复性。这就是我们这次要聊的项目利用Selenium这个强大的浏览器自动化工具实现URP教务系统的全流程自动化操作。Selenium的核心价值在于它能像真人一样驱动浏览器点击、输入、下拉、跳转所有网页交互都能被精准控制是处理这类基于Web的、有固定流程的重复性任务的绝佳选择。这个项目适合所有被类似流程困扰的学生也适合对Python自动化、网页爬虫在合规范围内感兴趣的朋友。它不涉及破解或攻击系统只是将你手动操作的步骤用代码复现一遍本质上是一种“机器人流程自动化”RPA。通过这个项目你不仅能学会Selenium的基本操作更能掌握一套解决实际问题的自动化思维如何分析网页结构、如何定位元素、如何处理登录验证、如何应对页面跳转和加载等待。接下来我们就一步步拆解看看如何把这个想法变成现实。2. 核心工具链选型与环境搭建工欲善其事必先利其器。要实现URP系统的自动化我们需要一套稳定、可靠的工具链。核心自然是Selenium但围绕它的一系列配套工具和库的选择直接决定了脚本的健壮性和开发效率。2.1 为什么是Selenium WebDriver市面上自动化工具不少比如Playwright、Puppeteer等后起之秀也很强大。但在这个场景下选择Selenium有几点核心考量生态成熟与社区支持Selenium是历史最悠久、社区最庞大的Web自动化框架之一。这意味着你在遇到任何稀奇古怪的问题时几乎都能在Stack Overflow或中文技术社区找到解决方案。URP系统这类国内高校广泛使用的系统其前端技术栈如古老的jQuery、复杂的表单可能比较“独特”Selenium庞大的用户基数意味着更高的踩坑概率和更丰富的填坑经验。多语言支持Selenium支持Java、Python、C#等多种语言。我们选择Python是因为其语法简洁库生态丰富特别适合快速开发和脚本编写。用PythonSelenium的组合可以让我们更专注于业务逻辑而非语言细节。对真实浏览器的完美模拟Selenium WebDriver通过浏览器厂商提供的原生驱动如ChromeDriver来直接控制浏览器。这意味着脚本运行时就是一个真实的、完整的浏览器实例在运行。这对于需要处理JavaScript渲染、Cookie会话、甚至是一些简单图形验证码后续会讨论策略的场景至关重要。URP系统的登录和评估页面充满了动态加载的元素和表单提交一个无头浏览器或简单的HTTP请求库如requests很难完美模拟。2.2 环境搭建详细步骤这里我们以Windows系统、Chrome浏览器为例给出最稳妥的搭建流程。第一步安装Python确保你的电脑上安装了Python 3.7或更高版本。可以从Python官网下载安装包安装时务必勾选“Add Python to PATH”这样可以在命令行中直接使用python和pip命令。第二步安装Selenium库打开命令行CMD或PowerShell输入以下命令pip install selenium这条命令会从PyPIPython包索引下载并安装最新稳定版的Selenium库。第三步下载与浏览器版本匹配的WebDriver这是最容易出错的一步。Selenium本身只是一个控制指令的发送者它需要对应浏览器的“驱动程序”WebDriver来实际操控浏览器。打开你的Chrome浏览器在地址栏输入chrome://settings/help查看你的Chrome版本号例如版本 128.0.6613.138。访问ChromeDriver的官方下载站点通常搜索“ChromeDriver下载”即可找到。关键点来了你必须下载与你的Chrome浏览器主版本号完全一致的ChromeDriver。例如Chrome是128.x.x.x你就必须下载128.x.x.x系列的ChromeDriver。版本不匹配会导致Selenium无法启动浏览器或运行时崩溃。下载对应版本的chromedriver.exe文件。第四步配置WebDriver路径下载的chromedriver.exe需要让Selenium能找到。有两种常用方法方法A添加到系统PATH将chromedriver.exe文件放在一个固定的目录例如C:\WebDriver\然后将这个目录路径添加到系统的环境变量PATH中。这样Selenium可以在任何位置启动它。方法B在代码中指定路径推荐对于单项目脚本更简单直接的方法是在Python代码中指定驱动文件的绝对路径。这样避免了环境配置的麻烦项目迁移也更方便。注意很多新手在这里会卡住提示“chromedriver executable needs to be in PATH”。如果遇到请严格按照上述步骤检查浏览器版本和驱动版本是否一致以及路径是否正确。第五步验证安装创建一个简单的Python脚本test_selenium.pyfrom selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定你的chromedriver.exe的绝对路径 driver_path rC:\path\to\your\chromedriver.exe service Service(executable_pathdriver_path) # 创建浏览器驱动对象 driver webdriver.Chrome(serviceservice) # 打开百度首页 driver.get(https://www.baidu.com) print(driver.title) # 应该打印出“百度一下你就知道” # 等待几秒查看效果 import time time.sleep(3) # 关闭浏览器 driver.quit()运行这个脚本如果成功弹出一个Chrome浏览器窗口并打开了百度页面控制台打印出标题那么恭喜你Selenium环境搭建成功。3. 逆向工程手动操作步骤的代码化拆解在动手写代码之前我们必须像侦探一样把整个手动操作流程“逆向工程”弄清楚每一个步骤背后对应的网页操作是什么。这是自动化成功与否的关键盲目写代码只会陷入无尽的调试泥潭。3.1 流程步骤映射我们先梳理出从打开浏览器到打印成绩的完整手动步骤并思考其代码实现打开URP系统登录页手动打开浏览器输入网址。代码对应driver.get(“登录页URL”)。输入用户名和密码手动在输入框点击并打字。代码需要先定位到用户名和密码的输入框元素然后使用send_keys()方法输入文本。处理验证码如果存在这是最大的变数和难点。URP系统可能使用数字、字母、算式或滑动验证码。我们需要观察其类型。如果是简单的数字字母图片可以考虑在合规前提下手动识别一次并输入或者研究其验证码接口是否有规律但通常没有。更务实的做法是在脚本中设置暂停等待用户手动输入验证码。这虽然牺牲了部分自动化程度但保证了脚本的100%可行性和安全性避免了触碰复杂的图像识别或逆向工程。点击登录按钮定位登录按钮元素执行click()操作。等待页面跳转并进入评估模块登录成功后页面会跳转到学生主页。我们需要从主页的菜单或链接中找到“教学评估”或类似的入口。这需要分析跳转后的页面HTML结构定位到那个特定的链接并点击。遍历所有待评估课程并完成评估进入评估页面点击评估入口后通常会进入一个列表页列出所有本学期需要评估的课程。逐门课程评估对列表中的每一门课程点击“评估”或“进入评估”按钮。填写评估表单评估页面通常是一个有多项选择题如“非常满意”、“满意”等的表单。我们需要定位到每一个单选按钮通常是input type”radio”并选中对应的选项如第一个选项“非常满意”。这里可能涉及遍历多个问题。提交单门课程评估填写完毕后定位并点击“提交”按钮。提交后页面可能会刷新或弹窗提示需要代码能处理这些反馈。返回课程列表提交一门后通常需要返回课程列表页面继续评估下一门。这里要注意页面跳转的逻辑是开新窗口还是原页面刷新。所有课程评估完成后进入成绩查询页面评估全部完成后才能解锁成绩查询功能。从主导航栏找到“成绩查询”或“学生成绩”入口点击进入。选择查询学期并获取成绩列表在成绩查询页面可能需要选择学期如“2023-2024学年 第2学期”然后点击“查询”。页面会以表格形式展示所有课程的成绩。定位并提取成绩数据这是数据抓取环节。我们需要分析成绩表格的HTML结构使用浏览器的开发者工具定位到table标签然后逐行(tr)逐列(td)地提取课程名称、成绩、学分等信息。格式化并打印/保存成绩将提取到的数据整理成易读的格式如列表、字典然后在控制台打印出来或者保存为CSV、Excel文件方便后续查看和分析。安全退出系统所有操作完成后最好点击“退出登录”链接然后调用driver.quit()关闭浏览器释放资源。3.2 关键难点与应对策略页面加载等待网络速度和服务器响应会导致页面元素加载有延迟。如果代码在元素还没出现时就尝试去点击或输入会抛出NoSuchElementException。必须使用“显式等待”。Selenium提供了WebDriverWait和expected_conditions模块可以让代码智能地等待某个元素出现、可点击或可见后再执行操作而不是用固定的time.sleep()傻等。元素定位这是Selenium的核心技能。我们需要使用浏览器的“开发者工具”F12打开通过“检查”功能来查看目标元素的HTML属性。常用的定位方式有find_element(By.ID, “id值”)通过元素的id属性定位最精确。find_element(By.NAME, “name值”)通过name属性定位常用于表单。find_element(By.CLASS_NAME, “class名”)通过CSS类名定位。find_element(By.XPATH, “xpath表达式”)通过XML路径定位功能最强大也最灵活可以处理没有ID、Name的复杂情况。find_element(By.CSS_SELECTOR, “css选择器”)通过CSS选择器定位语法简洁。对于URP这类可能使用框架的系统XPATH和CSS_SELECTOR往往是主力。iframe处理如果登录框或评估表单被嵌套在一个iframe框架里你必须先使用driver.switch_to.frame(“frame的id或name”)切换到该框架内才能定位到里面的元素。操作完后记得用driver.switch_to.default_content()切换回主页面。弹窗和警报提交评估后可能会有JavaScript弹窗alert。需要使用driver.switch_to.alert来获取弹窗对象然后调用.accept()确定或.dismiss()取消来处理。4. 代码实战从登录到成绩提取的完整实现有了前面的分析我们现在可以着手编写核心代码了。请注意以下代码是一个高度概括的模板你需要根据自己学校URP系统的实际HTML结构来调整定位器如ID、XPath。4.1 项目结构与初始化首先我们创建一个Python文件并导入必要的库进行初始化设置。import time from selenium import webdriver from selenium.webdriver.chrome.service import Service 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 TimeoutException, NoSuchElementException import pandas as pd # 用于后续保存数据到Excel # 1. 初始化浏览器驱动 def init_driver(): # 指定你的ChromeDriver路径 service Service(executable_pathr你的chromedriver.exe绝对路径) # 配置浏览器选项可选 options webdriver.ChromeOptions() # 如果想无头运行不显示浏览器界面取消下面这行的注释 # options.add_argument(--headless) # 防止被一些简单的反爬机制识别可以添加用户代理等非必需 options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) driver webdriver.Chrome(serviceservice, optionsoptions) # 设置全局隐式等待非必须推荐用显式等待 driver.implicitly_wait(10) # 单位秒 return driver # 主函数 def main(): driver init_driver() try: # 后续所有操作写在这里 login_to_urp(driver) # ... 其他函数调用 except Exception as e: print(f程序运行出错: {e}) finally: # 确保最终关闭浏览器 driver.quit() if __name__ __main__: main()4.2 登录函数实现登录函数需要处理页面加载、元素定位、输入和可能的验证码中断。def login_to_urp(driver): login_url https://你的学校urp登录页地址 driver.get(login_url) # 使用显式等待确保页面核心元素加载完成 wait WebDriverWait(driver, 20) # 最多等待20秒 try: # 假设用户名输入框的id是‘username’密码框id是‘password’ username_input wait.until( EC.presence_of_element_located((By.ID, username)) ) password_input driver.find_element(By.ID, password) login_button driver.find_element(By.ID, loginBtn) # 假设登录按钮id # 输入你的账号密码在实际使用中建议从配置文件或安全输入读取不要硬编码 your_username 你的学号 your_password 你的密码 username_input.clear() username_input.send_keys(your_username) password_input.clear() password_input.send_keys(your_password) # **处理验证码关键步骤** # 先尝试查找验证码输入框和图片 try: captcha_input driver.find_element(By.ID, captcha) # 验证码输入框ID captcha_image driver.find_element(By.ID, captchaImg) # 验证码图片ID # 如果找到了验证码元素说明需要输入 print(检测到验证码请在浏览器中查看并手动输入...) # 这里可以增加一个截图功能方便用户查看 # captcha_image.screenshot(captcha.png) # 然后程序暂停等待用户手动在浏览器中输入验证码 input(请在浏览器中输入验证码后回到控制台按回车键继续...) except NoSuchElementException: # 没找到验证码元素说明本次登录不需要验证码直接继续 print(未检测到验证码继续登录...) pass # 点击登录按钮 login_button.click() print(已点击登录按钮等待跳转...) # 等待登录成功后的页面特征元素出现比如显示用户名的区域 # 假设登录成功后首页有一个id为‘userInfo’的元素 wait.until(EC.presence_of_element_located((By.ID, userInfo))) print(登录成功) except TimeoutException: print(登录页面加载超时或元素定位失败请检查网络或页面结构。) raise # 抛出异常让主函数捕获并退出4.3 完成教学评估函数这是最复杂的部分需要处理列表循环、表单填写和页面导航。def complete_evaluation(driver): print(开始进行教学评估...) wait WebDriverWait(driver, 15) # 1. 找到并点击“教学评估”入口 # 入口可能是一个链接文本包含“教学评估”、“评估”等 # 使用XPath通过链接文本定位是一种常见方法 try: eval_link wait.until( EC.element_to_be_clickable((By.XPATH, //a[contains(text(), 教学评估)])) ) eval_link.click() print(已进入教学评估模块。) except TimeoutException: # 如果XPath不行尝试其他方式比如通过菜单的ID或CLASS print(未找到标准评估入口尝试其他定位方式...) # 这里需要你根据实际页面调整 # eval_link driver.find_element(By.ID, “someMenuId”) # eval_link.click() # 2. 等待评估课程列表加载 # 假设课程列表在一个id为‘courseList’的table中 wait.until(EC.presence_of_element_located((By.ID, courseList))) # 3. 获取所有待评估课程的行 # 假设每一门课程是table中的一行tr并且有一个“评估”按钮 course_rows driver.find_elements(By.XPATH, //table[idcourseList]/tbody/tr) total_courses len(course_rows) print(f找到 {total_courses} 门待评估课程。) evaluated_count 0 for index, row in enumerate(course_rows): try: # 重新定位当前行的“评估”按钮因为页面可能刷新 # 按钮可能在当前行的某个td里的一个链接或button eval_button row.find_element(By.XPATH, .//a[contains(text(), 评估)]) course_name row.find_element(By.XPATH, .//td[2]).text # 假设第2列是课程名 print(f正在评估第 {index1}/{total_courses} 门课程: {course_name}) # 点击“评估”按钮进入评估详情页 eval_button.click() # 等待评估表单加载 wait.until(EC.presence_of_element_located((By.ID, evalForm))) # 4. 填写评估表单 # 评估表通常由多个问题组成每个问题是一组同名的radio button # 例如所有“教师授课态度”的选项radio的name都是‘q1’ # 我们的策略是找到所有问题组对每一组选择第一个选项通常是“非常满意” question_groups driver.find_elements(By.XPATH, //div[contains(class, question-group)]) for group in question_groups: # 在每个问题组里找到第一个radio button并点击 first_radio group.find_element(By.XPATH, .//input[typeradio][1]) # 有时候radio被label包裹直接点击radio可能不行需要点击关联的label # 获取radio的id然后找到对应的label radio_id first_radio.get_attribute(id) if radio_id: label group.find_element(By.XPATH, f.//label[for{radio_id}]) label.click() else: # 如果没有id尝试直接点击radio元素 first_radio.click() # 5. 提交评估 submit_button driver.find_element(By.ID, submitEval) submit_button.click() # 处理可能的成功提示弹窗 time.sleep(1) # 给弹窗一点时间弹出 try: alert driver.switch_to.alert alert.accept() # 点击“确定” print( 评估提交成功。) except: # 没有弹窗可能是页面刷新或提示在页面上 pass # 6. 返回课程列表页继续下一门 # 提交后页面可能自动跳回列表也可能需要点击“返回”链接 back_link driver.find_element(By.XPATH, //a[contains(text(), 返回)]) back_link.click() # 等待列表页再次加载 wait.until(EC.presence_of_element_located((By.ID, courseList))) # 重新获取课程行列表因为页面刷新了 course_rows driver.find_elements(By.XPATH, //table[idcourseList]/tbody/tr) evaluated_count 1 except Exception as e: print(f 评估课程 {index1} 时出现错误: {e}) # 出错后尝试刷新页面或返回列表重试 driver.back() wait.until(EC.presence_of_element_located((By.ID, courseList))) course_rows driver.find_elements(By.XPATH, //table[idcourseList]/tbody/tr) continue # 跳过当前课程继续下一个 print(f教学评估完成。成功评估 {evaluated_count}/{total_courses} 门课程。)4.4 查询并打印成绩函数评估完成后就可以去获取成绩了。def fetch_and_print_grades(driver): print(开始查询成绩...) wait WebDriverWait(driver, 15) # 1. 导航到成绩查询页面 try: grade_link wait.until( EC.element_to_be_clickable((By.XPATH, //a[contains(text(), 成绩查询)])) ) grade_link.click() print(已进入成绩查询页面。) except TimeoutException: print(未找到成绩查询入口。) return # 2. 选择学期如果需要 # 假设有一个下拉框选择学期其id为‘termSelect’ try: term_select driver.find_element(By.ID, termSelect) from selenium.webdriver.support.ui import Select select Select(term_select) # 选择第一个选项通常是当前学期或者根据文本选择 select.select_by_index(0) # select.select_by_visible_text(“2023-2024学年 第2学期”) except NoSuchElementException: print(未找到学期选择框可能默认显示当前学期。) # 3. 点击查询按钮 query_button driver.find_element(By.ID, queryBtn) query_button.click() # 4. 等待成绩表格加载 wait.until(EC.presence_of_element_located((By.ID, gradeTable))) # 5. 解析成绩表格 grade_table driver.find_element(By.ID, gradeTable) # 获取所有数据行通常表头是第一个tr数据从第二个开始 rows grade_table.find_elements(By.XPATH, .//tbody/tr) # 或者直接用 rows driver.find_elements(By.XPATH, //table[idgradeTable]/tbody/tr) grades_data [] for row in rows: cols row.find_elements(By.TAG_NAME, td) if len(cols) 4: # 假设至少有4列序号、课程名、学分、成绩 course_info { 序号: cols[0].text, 课程名称: cols[1].text, 学分: cols[2].text, 成绩: cols[3].text, # 可以继续添加其他列如“绩点”、“课程性质”等 } grades_data.append(course_info) # 6. 打印成绩 print(\n 成绩单 ) print(f{序号:5}{课程名称:30}{学分:8}{成绩:6}) print(- * 60) for item in grades_data: print(f{item[序号]:5}{item[课程名称]:30}{item[学分]:8}{item[成绩]:6}) # 7. 可选保存到文件 df pd.DataFrame(grades_data) df.to_excel(本学期成绩.xlsx, indexFalse) print(f\n成绩已保存至 本学期成绩.xlsx) return grades_data4.5 主流程整合最后我们将上述函数整合到主函数中形成一个完整的流程。def main(): driver init_driver() try: # 步骤1登录 login_to_urp(driver) # 登录后可能需要短暂等待页面完全稳定 time.sleep(2) # 步骤2完成教学评估 complete_evaluation(driver) # 步骤3查询并打印成绩 grades fetch_and_print_grades(driver) print(\n所有自动化任务已完成) except Exception as e: print(f主流程执行出错: {e}) # 出错时可以截图保存现场便于调试 driver.save_screenshot(error_screenshot.png) print(错误截图已保存为 error_screenshot.png) finally: # 步骤4退出 input(按回车键退出并关闭浏览器...) driver.quit()5. 避坑指南与实战经验分享写代码的过程很少一帆风顺尤其是在对付像URP这样可能年久失修、前端代码风格不一的系统时。下面分享一些我踩过的坑和总结的经验希望能帮你少走弯路。5.1 元素定位失效动态ID与XPath稳定性URP系统的前端代码可能是多年前的产物或者使用了某些框架自动生成元素ID。你可能会发现今天用By.ID定位成功的按钮明天就跑不通了因为ID是动态变化的例如包含时间戳或随机数。应对策略优先使用相对稳定的属性name、class注意可能是多个class组合通常比动态ID稳定。查看元素时看看有没有固定的name”xxx”。善用XPath的文本和属性匹配当ID和Name都不可靠时XPath是你的王牌。可以通过元素的文本内容、邻近元素的特征来定位。例如定位一个文本是“提交”的按钮//button[text()‘提交’]或//input[value‘提交’]。定位“教学评估”这个链接//a[contains(text(), ‘教学评估’)]。contains函数非常有用可以匹配部分文本。通过兄弟节点、父节点等关系定位比如一个输入框前面有个固定的label标签写着“用户名”那么可以这样定位输入框//label[text()‘用户名’]/following-sibling::input。不要过度依赖浏览器开发者工具直接复制的XPathChrome的“Copy - Copy full XPath”生成的往往是绝对路径像/html/body/div[3]/div[2]/form/div[1]/input。这种路径极其脆弱页面结构稍有变动比如多了一个div就会失效。一定要自己编写相对路径的XPath。5.2 页面加载与异步等待的艺术这是新手最常遇到的问题。代码执行速度远快于网络加载和浏览器渲染速度。绝对不要滥用time.sleep()这是最糟糕的等待方式。设短了元素还没加载出来设长了白白浪费时间。脚本会变得又慢又不可靠。显式等待是唯一推荐的方式使用WebDriverWait配合expected_conditions。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By wait WebDriverWait(driver, 10) # 超时时间10秒 element wait.until(EC.presence_of_element_located((By.ID, “someId”))) # 或者等待元素可点击 button wait.until(EC.element_to_be_clickable((By.XPATH, “//button[text()‘确定’]”)))presence_of_element_located元素出现在DOM中即可不一定可见。visibility_of_element_located元素出现在DOM中并且可见。element_to_be_clickable元素可见且可点击如按钮。根据场景选择对于输入框用presence或visibility都可以对于要点击的按钮一定要用element_to_be_clickable否则可能点击无效。处理AJAX加载有些内容是通过AJAX动态加载的比如点击查询后表格数据才加载。等待表格出现不能只等table标签还要等里面的tr或td有内容。可以这样写wait.until(EC.visibility_of_element_located((By.XPATH, “//table[id‘dataTable’]//td”)))5.3 验证码自动化路上的“拦路虎”如前所述验证码是自动化登录的最大障碍。除了脚本中提到的“手动中断输入法”还有以下思路但请务必在合规和尊重系统安全的前提下考量观察规律有些简单的系统验证码会在页面的某个隐藏字段里或者验证码图片的URL本身就包含了答案虽然这很不安全。用开发者工具的Network面板观察验证码图片的请求看看响应头或URL参数。OCR识别复杂且不稳定对于简单的数字字母验证码可以使用Tesseract等OCR库尝试识别。但需要处理图片噪声、裁剪成功率不高且URP的验证码可能做了干扰线、扭曲等防OCR处理。第三方打码平台不推荐有付费API服务可以识别验证码。但这涉及隐私和安全风险且违反大多数网站服务条款强烈不推荐用于此类个人学习项目。终极方案半自动化我们的示例代码采用的方案是最稳妥的——遇到验证码就暂停让用户手动输入。虽然不够“全自动”但保证了脚本的可用性和安全性。你可以优化一下用户体验比如自动截图验证码区域并保存然后提示用户查看图片并输入。5.4 会话管理与异常恢复脚本运行时间可能较长网络波动或服务器临时问题可能导致操作中断。使用Try-Except包围关键操作就像示例代码中那样对每一门课程的评估操作都用try-except包裹一旦某门课出错可以记录日志然后跳过继续下一门而不是整个脚本崩溃。定期保存状态对于评估这种多步骤任务可以在本地文件或变量中记录已经成功评估的课程ID或名称。如果脚本中途因异常退出重启后可以先读取这个记录跳过已完成的课程。合理使用driver.back()和刷新在页面状态异常或元素找不到时可以尝试返回上一页(driver.back())或刷新当前页(driver.refresh())然后重新定位元素。5.5 浏览器的“反自动化”检测一些现代网站会检测浏览器是否被Selenium等工具控制。虽然URP系统通常没这么先进但了解这一点有备无患。我们的初始化代码中已经添加了一些选项来规避简单的检测options.add_argument(‘--disable-blink-featuresAutomationControlled’) options.add_experimental_option(“excludeSwitches”, [“enable-automation”]) options.add_experimental_option(‘useAutomationExtension’, False)这些设置可以移除Chrome浏览器中一些被自动化工具控制的标志。如果仍然被检测到可能需要更复杂的伪装如修改WebDriver的属性driver.execute_cdp_cmd但这属于更高级的话题且可能随着浏览器版本更新而失效。6. 脚本的优化与扩展方向一个能跑通的脚本只是开始一个健壮、易用、可维护的脚本才是目标。完成基础功能后可以从以下几个方向优化你的项目6.1 配置化与模块化分离配置信息将URL、用户名、密码、XPath定位表达式等易变信息从代码中抽离出来放到配置文件如config.ini、config.yaml或settings.py中。这样当URP系统改版或你需要评估不同学期的课程时只需修改配置文件而不用动核心代码。函数模块化将登录、评估、查询成绩等功能封装成独立的函数或类方法提高代码的可读性和复用性。主流程变得非常清晰。6.2 增强健壮性更完善的日志系统使用Python的logging模块替代简单的print。可以设置不同日志级别INFO, WARNING, ERROR并将日志输出到文件方便事后排查问题。重试机制对于网络请求失败、元素短暂不可见等临时性错误可以实现一个重试装饰器。例如某个点击操作失败后自动重试2-3次。超时时间动态调整根据网络状况和服务器响应动态调整WebDriverWait的超时时间而不是一个固定值。6.3 功能扩展多学期成绩批量导出修改成绩查询函数使其能够遍历选择多个学期并将所有成绩合并到一个Excel文件中每个学期一个工作表。成绩分析与可视化利用pandas和matplotlib对导出的成绩数据进行简单的分析如计算平均绩点、绘制各科成绩分布直方图等。定时任务结合Windows的任务计划程序Windows或cronLinux/Mac让脚本在每学期末的特定时间自动运行实现真正的“无人值守”。图形用户界面GUI使用tkinter或PyQt为脚本制作一个简单的图形界面方便不熟悉命令行的同学使用可以在界面上输入账号密码、选择操作等。通知功能脚本运行完成后可以通过邮件、钉钉机器人、Server酱等方式将成绩结果摘要发送到你的手机实现闭环。6.4 伦理与合规性重申最后也是最重要的一点我们必须反复强调自动化脚本使用的边界目的正当这个脚本仅用于个人学习Python和Selenium自动化技术以及提升个人处理重复性事务的效率。遵守规则使用前请务必阅读你所在学校关于教务系统使用的相关规定。确保你的自动化操作不会对服务器造成异常压力例如避免高频、并发请求。责任自负任何因使用自动化脚本导致的问题如账号异常、评估数据错误等需由使用者自行承担。脚本不应被用于任何干扰系统正常运行、窃取他人信息或谋取不正当利益的用途。尊重版权URP系统的前端代码、设计等属于其开发者或学校本脚本仅模拟用户交互不涉及对任何受版权保护内容的非法获取或复制。自动化是一把双刃剑用好了是提升效率的神器。希望这个详细的指南不仅能帮你搞定URP系统的自动化更能让你掌握一套分析和解决类似Web自动化问题的通用方法论。在实际动手的过程中耐心和细致的观察调试比任何高级技巧都重要。祝你调试顺利早日摆脱手动点击的烦恼。