简介:这是一份面向高校学生与Python自动化爱好者的实战项目源码,基于Selenium实现浙江大学自动健康打卡功能,适合作为毕业设计、课程设计或自动化脚本学习案例。压缩包共10个文件,约6.91MB,以py脚本为核心,辅以txt说明、yml工作流配置、chromedriver驱动及md文档,覆盖打卡主逻辑、验证码识别与消息推送等模块,结构清晰便于二次开发。项目为个人高分作品,已通过导师指导与答辩评审,获95分评价,代码经测试可正常运行。已有52人学习下载。读者可从中掌握Selenium元素定位、表单自动填写、定时任务配置及异常处理思路,也可在此基础上修改扩展为其他自动化场景,适合具备一定Python基础、希望提升实战能力的学习者参考借鉴。
1. 从手动填报到脚本代劳:这套浙大健康打卡源码到底解决了什么
如果你在浙大待过一段时间,大概率经历过这样的早晨:闹钟响了先摸手机,打开打卡页面,填体温、选健康状况、勾承诺书、点提交,一套动作下来两三分钟,偶尔网络卡一下还得重来。单次不累,但连续几百天就是折磨。这套基于 Selenium 的自动健康打卡项目,核心价值就是把这套重复动作交给浏览器自动化去跑,你只需要在配置里填一次账号信息和打卡参数,剩下的交给脚本。它适合三类人:一是想省事的在校同学,二是拿它当 Selenium 实战案例的计算机专业学生,三是想在此基础上改造成其他自动化填报任务的开发者。项目里带了完整源码、chromedriver、requirements.txt 和详细文档,拿到手就能跑,不是那种只给几个片段让你自己拼的半成品。
2. 拆开压缩包:目录结构与 Selenium 驱动链路
2.1 文件清单与各模块职责
先把压缩包解开,根目录下能看到这些内容:AutoClock-main是主工程目录,daka.py是打卡主脚本,chaojiying.py负责验证码识别,DingRobot.py是钉钉机器人通知模块,chromedriver是浏览器驱动,requirements.txt锁定依赖,README.md是使用说明,.github/workflows里放了定时任务的配置模板。这个结构说明作者不是随手写了个脚本,而是按工程化思路组织的:主流程、验证码、通知、依赖、文档各管一摊。
daka.py是整个项目的入口,它做的事情可以拆成四步:启动 Chrome、登录、填写表单、提交并判断结果。chaojiying.py的存在说明打卡页面在某些情况下会弹验证码,作者用第三方打码平台来兜底。DingRobot.py则是把打卡结果推送到钉钉群,方便你确认今天到底打没打上。这三个文件构成了「执行—识别—反馈」的闭环。
2.2 Selenium 驱动链路与 chromedriver 版本匹配
Selenium 的工作原理不复杂:你的 Python 代码通过 WebDriver 协议跟 chromedriver 通信,chromedriver 再去操控真实的 Chrome 浏览器。所以链路上任何一环版本对不上,脚本就会在启动浏览器那一步直接翻车。项目里自带了 chromedriver,但你要确认它的版本跟你本机 Chrome 的大版本号一致。比如你本机是 Chrome 120,驱动也得是 120.x,差一个大版本就可能报session not created的错误。
# 查看本机 Chrome 版本(Windows 在地址栏输入 chrome://version) # Linux 下可以用: google-chrome --version # macOS 下: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version # 查看项目自带 chromedriver 版本 ./chromedriver --version上面这几条命令的目的很直接:把浏览器版本和驱动版本都打印出来做比对。如果版本不匹配,去 chromedriver 的官方下载页找对应版本替换掉项目里的那个文件即可。注意 Windows 下驱动文件名是chromedriver.exe,Linux 和 macOS 下没有后缀,替换时别搞混。
2.3 依赖安装与虚拟环境隔离
项目根目录的requirements.txt里列了 Selenium、requests 等包。我一般不建议直接往全局 Python 环境里装,容易跟其他项目的依赖打架。用 venv 建一个独立环境,出问题直接删掉重建,比到处找冲突省事得多。
# 创建虚拟环境 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS / Linux: source venv/bin/activate # 安装依赖 pip install -r requirements.txt # 确认 selenium 版本 pip show seleniumpython -m venv venv会在当前目录下建一个名为 venv 的文件夹,里面是一套独立的 Python 解释器和 pip。激活之后,你后续所有 pip install 都只影响这个环境。pip show selenium用来确认装上的版本号,Selenium 4.x 和 3.x 在 API 上有差异,比如 4.x 里find_element_by_id这类写法已经被移除,改成了find_element(By.ID, ...)。如果你拿到的代码是 3.x 写法但装的是 4.x,运行时会报AttributeError,这时候要么降级 Selenium,要么把代码里的定位方式改过来。
3. 配置与运行:把账号、参数和通知串起来
3.1 账号信息与打卡参数的配置位置
打开daka.py,你会看到脚本开头或单独一个配置区域里放着用户名、密码、打卡表单的默认值。常见做法是把这些敏感信息抽到一个单独的配置文件或者环境变量里,避免直接硬编码在脚本中。如果项目里没有做这层抽离,你可以自己加一个config.py,把账号密码放进去,然后在daka.py里 import 进来。
# config.py 示例 USERNAME = "你的学号" PASSWORD = "你的密码" # 打卡表单里的默认选项,按你实际情况填 DEFAULT_TEMPERATURE = "36.5" DEFAULT_LOCATION = "校内"# daka.py 中引用配置 from config import USERNAME, PASSWORD, DEFAULT_TEMPERATURE # 登录部分大致逻辑 driver.find_element(By.ID, "username").send_keys(USERNAME) driver.find_element(By.ID, "password").send_keys(PASSWORD) driver.find_element(By.ID, "login-btn").click()这里的关键参数是USERNAME和PASSWORD,填错的话脚本会在登录页卡住,后续所有步骤都走不下去。DEFAULT_TEMPERATURE这类表单默认值要根据学校当时的填报要求来设,不同时期字段可能不一样,跑之前先手动打开一次打卡页面,看看当前要填哪些项,再对照着改脚本里的字段定位和默认值。
3.2 表单字段定位与显式等待
打卡页面上的输入框、下拉框、提交按钮,都需要用 Selenium 的定位方法找到。项目里大概率用的是By.ID、By.NAME或By.XPATH。页面加载需要时间,如果脚本跑得比页面渲染快,就会报NoSuchElementException。解决办法是用显式等待,让脚本在找不到元素时先等一会儿再重试。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待提交按钮出现,最多等 10 秒 submit_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, "submit-btn")) ) submit_btn.click()WebDriverWait(driver, 10)里的 10 是超时秒数,意思是最多等 10 秒,期间每隔 0.5 秒检查一次条件是否满足。EC.element_to_be_clickable确保元素不仅出现了,而且是可以点击的状态。这比用time.sleep(5)硬等要靠谱得多,因为硬等要么等不够要么等太久,显式等待是条件满足就立刻继续。
3.3 验证码识别与钉钉通知的接入
chaojiying.py是打码平台的接口封装。如果打卡页面弹了图形验证码,脚本会截图验证码区域,发给打码平台,拿回识别结果再填进去。用这个模块需要你去打码平台注册账号,拿到自己的软件 ID 和密钥,填到chaojiying.py对应的变量里。不打码平台的话,也可以看看验证码是不是简单的算术题或者固定字符,如果是,直接在脚本里写逻辑判断就行,不必额外花钱。
DingRobot.py负责把打卡结果推到钉钉。你需要在钉钉群里添加一个自定义机器人,拿到 Webhook 地址和加签密钥,填到脚本里。打卡成功或失败后,脚本会发一条消息到群里,你打开手机就能看到今天到底打没打上。这个模块不是必须的,但强烈建议配上,否则脚本在后台跑,你根本不知道它有没有正常工作。
# DingRobot.py 中的关键配置 WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=你的token" SECRET = "你的加签密钥" # 发送消息的函数大致长这样 def send_message(content): # 拼接签名、构造请求、发送 ...Webhook 地址和密钥填错的话,消息发不出去,但打卡本身不受影响。所以如果你发现打卡记录有了但钉钉没收到通知,先检查这两个参数,再检查网络能不能访问钉钉的接口。
4. 避坑与排查:那些让你打卡失败的常见问题
4.1 驱动版本不匹配导致浏览器起不来
现象:运行脚本后立刻报错,提示session not created: This version of ChromeDriver only supports Chrome version XX。原因是你本机 Chrome 自动更新了,但项目里的 chromedriver 还是旧版本。解决方法是查看本机 Chrome 版本,去下载对应版本的 chromedriver 替换项目里的文件。如果懒得每次手动换,可以用webdriver-manager这个库,让它自动下载匹配的驱动。
4.2 页面元素定位失效
现象:脚本能打开浏览器、能登录,但到了填表单那一步就报NoSuchElementException。原因是打卡页面的 HTML 结构变了,或者字段的 ID、NAME 跟脚本里写的不一样。解决方法是手动打开打卡页面,按 F12 打开开发者工具,用元素选择器点一下你要填的输入框,看看它的 ID 或 NAME 是什么,然后改脚本里的定位表达式。如果页面用了 iframe,还需要先driver.switch_to.frame()切进去才能找到元素。
4.3 登录态过期与会话保持
现象:昨天还能跑,今天一跑就停在登录页,提示密码错误或者要求重新登录。原因是学校系统可能改了密码策略,或者你的账号在别处登录导致会话失效。解决方法是先手动登录一次确认账号正常,然后检查脚本里的密码是不是最新的。如果系统有异地登录检测,可能还需要处理额外的验证步骤。
4.4 定时任务环境下浏览器无法启动
现象:在本地跑得好好的,放到服务器上用 cron 定时执行就失败。原因是服务器上没有图形界面,Chrome 需要以 headless 模式运行。解决方法是在启动 Chrome 的代码里加上options.add_argument('--headless')和options.add_argument('--no-sandbox')。另外 cron 环境下的 PATH 可能跟你的 shell 不一样,脚本里最好用绝对路径来指定 chromedriver 的位置。
from selenium.webdriver.chrome.options import Options options = Options() options.add_argument('--headless') # 无界面模式 options.add_argument('--no-sandbox') # 服务器上常需要 options.add_argument('--disable-dev-shm-usage') # 避免共享内存不足 driver = webdriver.Chrome(options=options)这三个参数是无界面环境下的标配。--headless让 Chrome 不弹窗口,--no-sandbox解决权限问题,--disable-dev-shm-usage避免容器里共享内存太小导致浏览器崩溃。加上之后,脚本在服务器上就能安静地跑完。
4.5 打卡成功但通知没发出来
现象:去系统里查记录发现打卡成功了,但钉钉群没收到消息。原因是DingRobot.py里的 Webhook 或密钥配错了,或者服务器网络访问不了钉钉接口。解决方法是先手动调用一次发送函数看报什么错,再检查 Webhook 地址是否完整、加签密钥是否跟机器人设置里的一致。如果服务器在内网,可能还需要配置网络出口。
5. 进阶改造:把打卡脚本变成通用自动化填报框架
这套代码跑通之后,你会发现它的骨架其实适用于任何「登录—填表—提交」的网页操作。我后来把它改造成了实验室的日报填报工具,思路是一样的:换掉登录部分的定位表达式,换掉表单字段的填写逻辑,通知模块原封不动复用。具体做法是抽出一个BaseFormFiller类,把打开页面、登录、填字段、提交、通知这几个步骤定义成方法,每个具体任务只需要继承这个类并覆盖字段映射就行。
class BaseFormFiller: def __init__(self, url, username, password): self.url = url self.username = username self.password = password self.driver = self._init_driver() def _init_driver(self): options = Options() options.add_argument('--headless') options.add_argument('--no-sandbox') return webdriver.Chrome(options=options) def login(self): raise NotImplementedError def fill_form(self): raise NotImplementedError def submit(self): raise NotImplementedError def notify(self, result): # 复用钉钉通知逻辑 ... def run(self): try: self.driver.get(self.url) self.login() self.fill_form() self.submit() self.notify("打卡成功") except Exception as e: self.notify(f"打卡失败:{e}") finally: self.driver.quit()这个基类把流程固定下来,子类只需要实现login、fill_form、submit三个方法。run方法里用 try-except-finally 保证无论成功失败都会发通知、都会关浏览器。改造的时候,你只需要打开目标网页,用开发者工具找到各个元素的定位方式,填到子类里就行。
验证改造是否成功,我一般会分两步走:先在本地用有界面模式跑一遍,肉眼确认每一步都点对了地方;再切到 headless 模式跑一遍,确认无界面下也没问题。两步都过了,再放到定时任务里。从那以后我每次改完定位表达式,都会强制走一遍「有界面→无界面→定时任务」这个流程,避免直接上服务器然后对着日志猜哪里出了问题。希望这套思路能帮到你。
本文还有配套的精品资源,点击获取