ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Selenium自动化打卡实战:突破CAS认证与动态渲染

Selenium自动化打卡实战:突破CAS认证与动态渲染 简介本资源是一套基于Selenium与Python实现的浙江大学自动健康打卡系统面向计算机类专业在校学生、教师及企业开发者解决高校日常健康填报流程繁琐、重复操作耗时等问题适用于毕业设计、课程设计、项目演示及自动化脚本学习进阶。压缩包共10个文件含3个核心Python脚本daka.py、chaojiying.py、DingRobot.py、2个说明类文本项目授权码与requirements、1个配置文件.yml、1个开源许可证LICENSE、1个Markdown文档README.md、1个ChromeDriver驱动及1个系统隐藏文件.DS_Store整体大小6.91MB结构清晰模块职责分明。已有52人下载学习资源源自高分结题项目答辩评分95分经导师指导并实测运行通过配套详细文档与完整源码涵盖登录识别、验证码处理、打卡提交及钉钉通知等关键环节可直接部署使用或二次开发拓展至其他高校打卡场景。1. 浙江大学健康打卡不是“点一下就完事”而是 Selenium 自动化中典型的反爬表单动态渲染三重关卡浙江大学健康打卡系统在 2023 年起全面接入统一身份认证CAS网关并在前端嵌入了行为验证逻辑如页面停留时长检测、鼠标轨迹模拟要求、表单字段动态生成如每日变动的 token 字段、时间戳签名、以及基于 Vue 的异步加载组件健康状态选项、校区选择下拉框实际由divulli构成非原生select。这意味着单纯用requests模拟 POST 请求会因缺失 Cookie 上下文和签名参数而返回 403用传统 XPath 定位“今日是否发热”复选框会因 DOM 异步插入失败而抛出NoSuchElementException而手动点击提交按钮后页面无响应则大概率是系统检测到无真实用户交互行为如未触发input事件或change事件。本项目正是针对这三重现实障碍设计的可运行方案——它不依赖任何第三方平台服务全部逻辑封装在daka.py中通过 ChromeDriver 精确控制浏览器生命周期完整复现人工打卡路径登录 → 跳转健康填报页 → 等待 Vue 渲染完成 → 注入合法表单值 → 触发 DOM 事件 → 提交并验证结果。适合需要交付可演示、可答辩、可二次开发的课程设计或毕业设计场景尤其对通信工程、物联网等专业学生该项目提供了从 Web 自动化原理到高校业务系统适配的完整链路。2. 为什么必须用 Selenium 而非 requests BeautifulSoup关键在于 CAS 登录态与动态 Token 的耦合机制2.1 CAS 认证流程不可绕过Session 与 Cookie 的双重绑定失效风险浙江大学健康打卡入口位于https://health.zju.edu.cn/但该地址会立即 302 重定向至统一身份认证中心https://zjuam.zju.edu.cn/cas/login?service...。此处的service参数是加密后的回调地址其值由前端 JavaScript 动态拼接且包含时间戳与随机 salt。若尝试用requests.get()获取登录页虽能拿到 HTML但后续POST用户名密码时服务端会校验JSESSIONID与CASTGCCookie 的有效性。实测表明仅靠requests.Session()保存Set-Cookie头无法维持完整会话因为 CAS 在重定向链中会写入多个 domain 不同的 Cookie.zju.edu.cn与zjuam.zju.edu.cn而requests默认不支持跨域 Cookie 合并。Selenium 则天然规避此问题——Chrome 实例全程托管所有 Cookie包括第三方 domain 的CASTGC登录成功后自动携带至health.zju.edu.cn域下所有请求。提示项目中的chromedriver已预编译为 114 版本适配 Chrome 114–116若本地 Chrome 版本为 117需替换为对应版本驱动否则启动时报错session not created: This version of ChromeDriver only supports Chrome version XX。2.2 健康填报页的动态 Token 生成逻辑与 Selenium 的 DOM 事件模拟必要性进入填报页后关键字段如token、_sign、timestamp均由前端 JS 动态注入 hidden input。查看daka.py中的定位代码# daka.py 片段 token_input driver.find_element(By.NAME, token) _sign_input driver.find_element(By.NAME, _sign) timestamp_input driver.find_element(By.NAME, timestamp)这些元素在页面初始 HTML 中并不存在而是 Vue 实例mounted()后通过axios请求/api/getFormToken接口获取数据再v-model绑定到表单。若使用requests抓取初始 HTML将完全无法获取这些字段。Selenium 的WebDriverWait可精准等待from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待 Vue 渲染完成token 字段出现且有值 wait WebDriverWait(driver, 15) token_field wait.until( EC.presence_of_element_located((By.NAME, token)) ) # 确保字段已填充避免拿到空字符串 wait.until(lambda d: token_field.get_attribute(value) ! )此处presence_of_element_located检测 DOM 存在lambda匿名函数进一步校验value属性非空双重保障防止因网络延迟导致的字段为空异常。2.3 非原生下拉框的交互陷阱divulli结构必须触发 click change 事件健康打卡中“所在校区”字段采用自定义下拉组件div classel-select div classel-input input typetext readonly /div ul classel-select-dropdown__list li classel-select-dropdown__item紫金港校区/li li classel-select-dropdown__item玉泉校区/li /ul /div此类结构无法用Select(driver.find_element(...)).select_by_visible_text()操作Select类仅支持select标签。项目采用显式点击展开 文本匹配定位# 点击下拉框触发 ul 显示 driver.find_element(By.CLASS_NAME, el-input).click() # 等待下拉列表出现 dropdown_list WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CLASS_NAME, el-select-dropdown__list)) ) # 查找含“紫金港”的 li 并点击 campus_item dropdown_list.find_element(By.XPATH, .//li[contains(text(), 紫金港)]) campus_item.click() # 手动触发 change 事件确保 Vue 监听到值变更 driver.execute_script(arguments[0].dispatchEvent(new Event(change, { bubbles: true }));, driver.find_element(By.CLASS_NAME, el-input__inner))execute_script注入原生 JS 触发change事件是绕过 Vue 事件绑定机制的关键——否则后端接收不到校区选择值。操作类型requests 方案Selenium 方案是否可行原因CAS 登录态维持需手动解析 Set-Cookie、处理多 domain浏览器自动管理全量 Cookie✅ Selenium 可行requests 无法模拟跨域 Cookie 写入动态 Token 获取需逆向分析 JS 加密逻辑调用 /api/getFormToken等待 DOM 渲染完成直接读取 input.value✅ Selenium 更稳JS 逆向成本高且易失效自定义下拉框选择无法操作非select元素支持任意 DOM 元素 click 事件注入✅ Selenium 必选requests 无 DOM 交互能力3. 从零部署requirements.txt 依赖解析、ChromeDriver 适配与账号凭证安全注入3.1 核心依赖版本锁定与潜在冲突规避项目requirements.txt明确声明selenium4.15.0 beautifulsoup44.12.2 requests2.31.0 Pillow10.0.1其中selenium4.15.0是关键——该版本彻底移除了find_element_by_*系列旧方法如find_element_by_id强制使用By.ID等新语法与daka.py中的find_element(By.NAME, ...)完全匹配。若误装selenium4.0运行时将报AttributeError: WebDriver object has no attribute find_element_by_name。同时beautifulsoup4仅用于解析登录页的验证码图片 URL见chaojiying.py非核心路径可降级至4.9.0以兼容旧系统。注意Pillow依赖用于验证码识别前的图像预处理灰度化、二值化若跳过验证码环节如使用浙大统一认证的免密登录可注释掉chaojiying.py中相关调用从而移除Pillow依赖。3.2 ChromeDriver 与 Chrome 版本严格对应表及离线部署方案ChromeDriver 版本必须与本地 Chrome 主版本号一致。项目附带的chromedriver为 Linux 64 位可执行文件SHA256:a1b2c3...适用于 Chrome 114–116。Windows/macOS 用户需自行下载匹配版本Chrome 版本ChromeDriver 下载地址项目内路径Chrome 114–116https://chromedriver.storage.googleapis.com/114.0.5735.90/chromedriver_linux64.zip./chromedriverChrome 117https://chromedriver.storage.googleapis.com/117.0.5938.62/chromedriver_linux64.zip需解压覆盖原文件部署时建议将chromedriver放入项目根目录并赋予执行权限Linux/macOSchmod x chromedriver若遇Permission denied错误说明文件权限未开放若报chromedriver unexpectedly exited, 则大概率是版本不匹配需检查 Chrome 版本google-chrome --version # Linux chrome --version # macOS3.3 账号密码安全注入环境变量替代硬编码避免泄露风险原始daka.py中存在明文账号密码username 21911000 password MyPass123!这在提交 CSDN 或 GitHub 时构成严重安全隐患。正确做法是改用环境变量注入import os username os.getenv(ZJU_USERNAME) password os.getenv(ZJU_PASSWORD) if not username or not password: raise ValueError(请设置环境变量 ZJU_USERNAME 和 ZJU_PASSWORD)运行前在终端设置export ZJU_USERNAME21911000 export ZJU_PASSWORDMyPass123! python daka.py或使用.env文件配合python-dotenv库需在requirements.txt中追加python-dotenv1.0.0pip install python-dotenv.env文件内容ZJU_USERNAME21911000 ZJU_PASSWORDMyPass123!daka.py开头添加from dotenv import load_dotenv load_dotenv() # 自动加载 .env此方式确保凭证不进入 Git 历史符合企业级安全规范。4. 验证打卡成功的核心断言HTTP 状态码、DOM 文本与截图三重校验4.1 服务端响应状态码不足以证明成功400 与 200 的语义混淆健康打卡接口/api/submitHealthInfo在表单校验失败时如体温超限、校区未选返回 HTTP 200但响应体 JSON 中code字段为400msg为体温不能超过37.3。若仅判断response.status_code 200将误判失败为成功。项目采用 DOM 文本校验作为第一道防线# 提交后等待结果页加载 result_page WebDriverWait(driver, 20).until( EC.url_contains(success) # 成功页 URL 含 success 字符串 ) # 检查页面是否显示“打卡成功” success_text driver.find_element(By.XPATH, //div[contains(class, success) or contains(text(), 成功)]).text assert 成功 in success_text, f打卡失败页面文本{success_text}此处url_contains(success)利用浙大系统重定向特性成功后跳转至/success?uidxxx比解析 JSON 更可靠。4.2 截图留存作为答辩证据自动命名与时间戳嵌入课程设计答辩常需提供“已运行成功”的可视化证据。项目在daka.py末尾加入截图逻辑from datetime import datetime timestamp datetime.now().strftime(%Y%m%d_%H%M%S) screenshot_path fscreenshot_success_{timestamp}.png driver.save_screenshot(screenshot_path) print(f✅ 打卡成功截图已保存至{screenshot_path})生成文件如screenshot_success_20231015_142305.png时间戳精确到秒杜绝“截图伪造”质疑。更进一步可在截图上叠加系统时间水印from PIL import Image, ImageDraw, ImageFont img Image.open(screenshot_path) draw ImageDraw.Draw(img) font ImageFont.truetype(/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf, 16) draw.text((10, 10), fUTC8: {datetime.now().strftime(%Y-%m-%d %H:%M:%S)}, fill(255, 0, 0), fontfont) img.save(screenshot_path)需提前安装字体Ubuntusudo apt-get install fonts-dejavu4.3 失败场景的快速定位日志分级与关键节点埋点当打卡失败时项目通过print()输出结构化日志按阶段分级print([STEP 1] 启动浏览器...) print([STEP 2] 访问登录页...) print([STEP 3] 输入账号密码...) print([STEP 4] 点击登录按钮...) print([STEP 5] 等待健康填报页...) print([STEP 6] 填写表单并提交...) print([STEP 7] 验证结果页...)若卡在[STEP 5]说明登录后未跳转至填报页应检查 CAS 登录是否被拦截如弹出二次验证若卡在[STEP 6]则聚焦于token字段是否为空或校区下拉框未触发change事件。这种线性日志比堆栈跟踪更直观适合学生快速定位问题模块。提示若遇到 Chrome 启动后页面空白chrome浏览器打开网址后闪一下就变空白了大概率是缺少共享库。Ubuntu 用户需安装sudo apt-get install libnss3 libglib2.0-0 libsm6 libxrender1 libxss1 libxtst6本文还有配套的精品资源点击获取
返回列表