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

软考成绩自动查询小助手:Python + Selenium 实现定时监控

软考成绩自动查询小助手:Python + Selenium 实现定时监控

  • 软考成绩自动查询小助手:Python + Selenium 实现定时监控
    • 📌 背景与痛点
    • 🛠️ 技术栈与核心依赖
    • 🔐 敏感性处理:如何保护账号密码与本地路径
      • 1. 使用环境变量(推荐)
      • 2. 使用配置文件(如 `.env` 或 `config.ini`)
    • 🧩 功能模块详解
      • 1. 浏览器驱动初始化(`get_driver`)
      • 2. 登录流程(`login`)
      • 3. 成绩查询(`query_score`)
      • 4. 定时任务(`job`)
      • 5. 主程序
    • 🚀 使用步骤
    • ⚠️ 注意事项
    • 📝 脱敏后的完整代码
    • 📢 最后的话

软考成绩自动查询小助手:Python + Selenium 实现定时监控

还在每天手动刷新软考官网看成绩出了没?不如写个脚本替你盯着,一旦发布立刻通知你!
本文将分享一个基于 Python + Selenium 的自动化查询工具,并重点讲解如何对敏感信息进行安全处理。


📌 背景与痛点

每年软考(计算机技术与软件专业技术资格)成绩公布时,考生们总忍不住反复登录官网刷新页面,既耗费精力又容易错过第一时间。
其实,这种重复性工作完全可以用自动化脚本代替——只要我们能让脚本自动登录定时查询,并在检测到成绩时及时提醒即可。

本脚本正是为此而生,它使用Selenium模拟浏览器操作,配合schedule库实现定时循环查询,一旦发现“成绩已发布”或“合格”字样,就会在日志中高亮提示。


🛠️ 技术栈与核心依赖

  • Python 3.7+
  • Selenium– 浏览器自动化驱动
  • Edge WebDriver– 与 Microsoft Edge 浏览器配合(亦可改用 Chrome)
  • schedule– 轻量级定时任务调度
  • logging– 日志记录,方便追踪运行状态

安装命令:

pipinstallselenium schedule

同时请确保下载与本地 Edge 版本匹配的 Edge WebDriver,并将其所在目录加入系统 PATH。


🔐 敏感性处理:如何保护账号密码与本地路径

原代码中直接硬编码了用户名、密码和本地用户数据目录,这存在严重的安全隐患(例如代码泄露或版本控制意外提交)。
改进方案:将敏感信息移至环境变量外部配置文件,并在代码中动态读取。

1. 使用环境变量(推荐)

在系统或虚拟环境中设置:

exportRUANKAO_USERNAME="your_id"exportRUANKAO_PASSWORD="your_password"exportRUANKAO_USER_DATA_DIR="/path/to/profile"

然后在 Python 中通过os.environ.get()获取。

2. 使用配置文件(如.envconfig.ini

若不想设置环境变量,也可用configparser读取本地配置文件(需将配置文件加入.gitignore)。

本文示例将采用环境变量方式,既简单又安全。


🧩 功能模块详解

1. 浏览器驱动初始化(get_driver

  • 加载 Edge 选项,关闭自动化控制特征(防止被网站识别为爬虫)。
  • 启用用户数据目录user-data-dir),用于持久化保存登录会话和 Cookie,避免每次重复登录。
  • 执行 JS 隐藏navigator.webdriver属性,进一步降低检测风险。

2. 登录流程(login

  • 访问登录页,检测当前是否已登录(通过判断 URL 或页面元素)。
  • 若未登录,则:
    1. 点击“切换账号密码登录”(因为默认可能为扫码)。
    2. 输入用户名和密码。
    3. 触发滑动验证码(div.tncode),此时脚本暂停并等待用户手动滑动
    4. 用户完成后按回车继续,点击登录按钮。
  • 登录成功后,后续查询可直接复用会话。

⚠️为何验证码必须手动?
滑动验证码通常包含复杂的行为轨迹分析,自动破解成本高且违反网站条款,故采用“半自动”方式——脚本触发验证,人工完成,既合规又稳定。

3. 成绩查询(query_score

  • 访问成绩查询页。
  • 若页面显示“考区列表”,则自动点击“浙江”(可根据需要修改)进入具体考区。
  • 检查页面源码中是否包含“成绩暂未发布”、“合格”或“分数”等关键词。
  • 若找到成绩信息,则记录日志并返回成功标志;否则返回FalseNone(表示会话过期)。

4. 定时任务(job

  • 每次执行前检查是否处于登录状态,若掉线则自动重登。
  • 调用query_score并根据返回值决定是否重试。

5. 主程序

  • 首次启动时先登录。
  • 立即执行一次查询。
  • 通过schedule.every(1).minutes.do(job, driver)设置固定间隔(本文设为 1 分钟,可调整)。
  • 保持主循环运行,直到用户按Ctrl+C退出。

🚀 使用步骤

  1. 克隆/创建脚本文件,将下方脱敏后的代码保存为query_ruankao.py
  2. 安装依赖pip install selenium schedule
  3. 下载 Edge WebDriver并确保可执行。
  4. 设置环境变量(或直接在代码中临时替换):
    exportRUANKAO_USERNAME="你的证件号"exportRUANKAO_PASSWORD="你的密码"exportRUANKAO_USER_DATA_DIR="./edge_profile"# 任意本地路径
  5. 运行脚本
    python query_ruankao.py
  6. 首次运行会打开浏览器,若未登录,则脚本会停留在滑动验证码处,手动完成滑动后按回车继续
  7. 之后脚本会定时自动查询,日志会输出每次的结果。

⚠️ 注意事项

  • 验证码处理:每次登录都可能需要手动滑动,若会话保持良好(通过user-data-dir),则后续无需重复登录。
  • 页面结构变化:软考官网可能会更新 HTML 结构,届时需调整 CSS 选择器或 XPath。
  • 查询间隔:建议不要过于频繁(至少 1 分钟),避免给服务器造成压力。
  • 运行环境:需有图形界面(Windows / Linux with X11 / macOS),因为 Selenium 需要真实浏览器。
  • 隐私安全:切勿将包含真实账号的代码上传至公开仓库。

📝 脱敏后的完整代码

以下代码已将所有敏感信息替换为环境变量读取,并添加了详细的注释。你可以直接复制使用,只需正确设置环境变量即可。

importosimportloggingimporttimeimportschedulefromseleniumimportwebdriverfromselenium.common.exceptionsimportNoSuchElementException,TimeoutExceptionfromselenium.webdriver.common.byimportByfromselenium.webdriver.edge.optionsimportOptionsfromselenium.webdriver.supportimportexpected_conditionsasECfromselenium.webdriver.support.uiimportWebDriverWait# ---------- 从环境变量读取敏感配置 ----------USERNAME=os.environ.get("RUANKAO_USERNAME")PASSWORD=os.environ.get("RUANKAO_PASSWORD")USER_DATA_DIR=os.environ.get("RUANKAO_USER_DATA_DIR","./edge_profile")# 默认相对路径ifnotUSERNAMEornotPASSWORD:raiseValueError("请设置环境变量 RUANKAO_USERNAME 和 RUANKAO_PASSWORD")LOGIN_URL="https://bm.ruankao.org.cn/sign/in"QUERY_URL="https://bm.ruankao.org.cn/report/query"CHECK_INTERVAL_MINUTES=1# 查询间隔(分钟)logging.basicConfig(level=logging.INFO,format="%(asctime)s - %(levelname)s - %(message)s")defget_driver():"""启动 Edge 浏览器,使用持久化用户目录"""options=Options()options.add_argument("--disable-gpu")options.add_argument("--no-sandbox")options.add_argument("--disable-blink-features=AutomationControlled")options.add_experimental_option("excludeSwitches",["enable-automation"])options.add_experimental_option("useAutomationExtension",False)options.add_argument("--start-maximized")options.add_argument(f"--user-data-dir={USER_DATA_DIR}")driver=webdriver.Edge(options=options)# 隐藏自动化特征driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")returndriverdefis_logged_in(driver):"""检测是否已登录(通过检查当前URL或页面元素)"""current_url=driver.current_urlif"sign/in"incurrent_url:returnFalsetry:# 查找用户头像菜单,如果存在则认为已登录driver.find_element(By.CSS_SELECTOR,".layui-nav-item img.layui-nav-img")returnTrueexceptNoSuchElementException:returnFalsedeflogin(driver):"""执行登录(若已登录则跳过)"""logging.info(f"正在访问登录页:{LOGIN_URL}")driver.get(LOGIN_URL)time.sleep(2)ifnotis_logged_in(driver):logging.info("未登录,开始执行登录流程...")wait=WebDriverWait(driver,10)# 1. 切换到账号密码登录try:change_btn=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,"a.change_login")))driver.execute_script("arguments[0].click();",change_btn)logging.info("已切换到账号密码登录")time.sleep(1.5)exceptTimeoutException:logging.info("未找到切换按钮,可能已经在账号密码模式")# 2. 填写账号和密码try:username_input=wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,".layui-input.username")))password_input=driver.find_element(By.CSS_SELECTOR,".layui-input.password.pwd1")username_input.clear()username_input.send_keys(USERNAME)password_input.clear()password_input.send_keys(PASSWORD)logging.info("账号密码已填写")exceptNoSuchElementExceptionase:logging.error(f"找不到账号或密码输入框:{e}")raise# 3. 处理滑动验证码(需人工干预)try:tncode_div=driver.find_element(By.CSS_SELECTOR,"div.tncode")driver.execute_script("arguments[0].click();",tncode_div)logging.info("已触发滑动验证码,请在浏览器中手动完成滑动")input("完成滑动验证后,在此按回车继续...")time.sleep(1)exceptNoSuchElementException:logging.info("未找到滑动验证码元素,可能无需验证")# 4. 点击登录按钮try:submit_btn=driver.find_element(By.CSS_SELECTOR,".layui-btn.loginBtn")driver.execute_script("arguments[0].click();",submit_btn)logging.info("已点击登录按钮")exceptNoSuchElementExceptionase:logging.error(f"找不到登录按钮:{e}")raisetime.sleep(5)if"sign/in"indriver.current_url:logging.error("登录失败,请检查账号密码或验证码")returnFalseelse:logging.info("登录成功")returnTrueelse:logging.info("检测到已登录,无需重复登录")returnTruedefquery_score(driver):"""访问成绩查询页面,处理考区列表页的跳转"""logging.info(f"正在访问成绩查询页面:{QUERY_URL}")driver.get(QUERY_URL)time.sleep(5)# 检查是否进入考区列表页page_source=driver.page_sourceif"机构名称"inpage_sourceand"报名有效时间"inpage_source:logging.warning("检测到考区列表页,尝试点击'浙江'进入...")try:# 定位“浙江”对应的“进入”按钮zhejiang_entry=driver.find_element(By.XPATH,"//td[contains(text(),'浙江')]/following-sibling::td/a[contains(text(),'进入')]")driver.execute_script("arguments[0].click();",zhejiang_entry)logging.info("已点击'浙江'的'进入'按钮")time.sleep(3)page_source=driver.page_sourceexceptExceptionase:logging.error(f"点击'浙江'进入按钮失败:{e}")returnFalseelse:logging.info("当前页面不是考区列表页,直接检查成绩状态")# 检查成绩状态if"成绩暂未发布"inpage_source:logging.info("⏳ 成绩暂未发布,继续等待...")returnFalseelif"合格"inpage_sourceor"分数"inpage_sourceor"成绩"inpage_source:logging.info("🎉 成绩已发布!")try:score_element=driver.find_element(By.CSS_SELECTOR,".score-result")score_text=score_element.text logging.info(f"成绩详情:{score_text}")except:logging.info("请手动查看浏览器中的成绩页面。")returnTrueelse:if"sign/in"indriver.current_url:logging.warning("会话过期,需要重新登录")returnNoneelse:logging.info("未知状态,可能页面未完全加载或结构已变化")returnFalsedefjob(driver):"""定时任务"""logging.info("========== 执行定时查询 ==========")try:if"sign/in"indriver.current_url:logging.warning("检测到在登录页面,尝试重新登录...")ifnotlogin(driver):logging.error("重新登录失败,停止任务")returnresult=query_score(driver)ifresultisNone:logging.warning("会话过期,尝试重新登录...")iflogin(driver):query_score(driver)else:logging.error("重新登录失败")exceptExceptionase:logging.error(f"查询任务异常:{e}")if__name__=="__main__":driver=get_driver()try:ifnotlogin(driver):logging.error("首次登录失败,退出")driver.quit()exit()query_score(driver)schedule.every(CHECK_INTERVAL_MINUTES).minutes.do(job,driver)logging.info(f"定时任务已启动,每{CHECK_INTERVAL_MINUTES}分钟查询一次")whileTrue:schedule.run_pending()time.sleep(1)exceptKeyboardInterrupt:logging.info("用户中断,关闭浏览器")finally:driver.quit()

📢 最后的话

这个脚本已经在我自己的环境中稳定运行了几天,完美实现了“无人值守,成绩秒知”的目标。当然,它也存在一些局限性(如依赖浏览器界面、验证码需人工),但作为一个辅助工具已经足够实用。

如果你也正在等待软考成绩,不妨一试。记得保护好你的账号信息,用环境变量或配置文件隔离敏感数据。
如果你有更好的改进建议(例如接入邮件/微信通知),欢迎在评论区交流!


Happy Coding & Good Luck!🍀

http://www.jsqmd.com/news/1104257/

相关文章:

  • Python数据分析课程期末考试判断题联系题100题
  • Kiran-shell 性能优化:面板响应速度与内存管理的10个技巧
  • 如何解决区域创新资源分布不清的问题?
  • 65美元Brick设备助摆脱手机成瘾,首周屏幕使用时间降7%!
  • 程序员你觉得是业务重要还是技术重要?
  • 3步开启智能办公:UI-TARS桌面AI助手实战指南
  • 新版《健康与位置数据保护法案》将推出:禁止向数据经纪商出售含 AI 聊天机器人的敏感信息
  • 5分钟搞定:PC版微信QQ防撤回终极方案,让重要消息永不消失
  • 【MATLAB】STM32低功耗控制策略建模与仿真实现
  • 增量式角度编码器:高精度角位移实时采集核心器件
  • 【MATLAB】无人机集群队形缩放控制算法
  • 大模型性能提升40%的真相:五维协同优化与工程落地指南
  • PS PDF 批量导入导出工具 Pro|PDF 一键转 PSD/JPG/PNG 脚本
  • 使用一个json文件来描述我们的战场
  • 终极指南:用Mac Mouse Fix让普通鼠标在macOS上超越触控板体验
  • 新手向 OpenClaw 部署实操,图形化工具完成本地智能体环境搭建(包含安装包)
  • 【AI大模型】代码入门:批量调用API的极简Python脚本
  • 近百万本护照在公共互联网暴露数月,数据安全缺陷引担忧!
  • 2026年房地产动画服务行业选购指南
  • Pikachu靶场从入门到精通(六):不安全文件下载、目录遍历、敏感信息泄露与URL重定向漏洞实战
  • 2026年AI生成文献综述哪家强?PaperRed与笔捷AI、ChatGPT实测对比
  • VDExplainer:让漏洞检测模型“说清楚”,逐语句解释漏洞从何而来
  • 如何精准识别校地之间的创新合作潜力?
  • Python数据分析期末试题及详解
  • 偏振光学在显示技术中的应用综述:原理、进展与挑战——从 iPhone 屏幕演进到悟赫德护景贴观复盾的光学补偿方案
  • 手机屏幕保护膜的光学性能测试方法与标准研究——以悟赫德护景贴观复盾的测试体系为例
  • 想选升降龙门架制造厂家?这些挑选要点你不能错过!
  • 一人公司必备AI工具:5分钟将详情页变废为宝,产出高转化社媒图文
  • 史无前例合作!用Claude享50%折扣,州政府雇员借其辅助日常工作
  • 终极指南:如何让群晖Video Station影视信息更丰富完整