恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
用Python+Selenium实现OA自动打卡:从零到部署的完整实战指南
首页
资讯中心
/
用Python+Selenium实现OA自动打卡:从零到部署的完整实战指南
用Python+Selenium实现OA自动打卡:从零到部署的完整实战指南
发布时间:2026/10/17 10:09:55
先交代一下背景。我在一家不大不小的公司上班每天早晚两次要在OA系统里打卡。听起来就是花几分钟的事可实际情况远没那么从容早上赶着通勤到了工位第一件事是开电脑、看消息、泡杯茶等想起来打卡已经在九点以后了晚上下班脑子里全是待办事项签退这事基本靠手机闹钟提醒可闹钟一响手头活没干完顺手就划掉了。要是再赶上系统更新、页面跳转卡半天打卡失败就得走补卡流程找主管签字别提多尴尬。所以我就想写一个Python脚本把“OA自动打卡”这件事彻底自动化。这也是我在博客上记录的第一篇自己动手做的项目所以标题叫【Python首记】。这个脚本做的事情很直接每天在设定好的时间打开OA系统自动登录找到打卡按钮完成点击再把结果写进日志万一失败了还能发消息通知我。别看功能这么简单做完之后我不但告别了“忘打卡”还把Selenium、元素定位、定时任务这些技能一次性练熟了。这篇文章适合被考勤困扰的上班族、刚学Python想找小项目练手的朋友也适合所有想了解浏览器自动化是怎么落地的人。1. 项目立项为什么我决定写一个自动打卡脚本1.1 打卡这件小事其实是典型的“低价值高成本”重复劳动上班打卡这种事技术上没有任何难度但它有个特点频率高、价值低、一旦漏了代价极高。你想想登录OA系统平均要花三十秒找到考勤菜单再等页面刷新又要二十秒找到打卡按钮点一下十秒一次打卡差不多一分钟。早晚各一次一天两分钟一个月按二十二个工作日算就是四十四分钟。这四十四分钟拿来写代码、看文档、摸会儿鱼不都比机械地点按钮强更要命的是漏打卡的成本。一次忘记补卡要去OA里填补卡申请写明时间、原因、证明人然后等主管审批。运气好几分钟搞定运气不好来来回回折腾半小时还要被人问“怎么又忘打卡了”。所以对我来说写自动打卡脚本不是偷懒是一笔非常划算的账我一次性花几个小时把它搞定之后每天省两分钟一个月省四十四分钟一年下来能省出一整天。更何况这件事本身还能让我练手Python自动化属于典型的“一次投入、长期回报”。当然在动手之前我也给自己定了规矩脚本只负责“准时打上卡”不去改打卡记录更不做虚假出勤。公司的考勤制度是底线能自动化的是“操作流程”而不是“考勤事实”。1.2 技术方案选型为什么我优先选Selenium而不是直接调接口动手前我想过三条路分别聊一下各自的情况。第一条路是用requests直接模拟接口调用。理论上最优雅速度快、资源占用低只要找到OA系统打卡接口的URL、请求参数、加密逻辑再把登录后的Cookie或Token维护好就能像发消息一样把打卡请求发出去。但对一个企业内部系统来说这条路有几个坎首先是接口没有文档得靠抓包一点点逆向工作量不小其次登录凭证往往有有效期Token怎么刷新、过期了怎么处理都是坑再就是某些系统会校验浏览器指纹、加验证码或者做风控纯接口方案很容易触发限制。第二条路是用Selenium驱动真实浏览器模拟真人操作。优点是直观不用关心后端接口怎么设计页面能点就能自动化对新手来说调试时能肉眼看到浏览器在做什么出错了也好定位。缺点是启动浏览器比较慢大概会多花一两秒而且脚本对前端页面结构有一定依赖系统改版后定位器可能要跟着调整。但整体可控适合这种“每天跑两次、不追求极致性能”的场景。第三条路是直接用现成的RPA按键工具。配置起来确实快但可编程性弱日志、异常通知、定时策略这些想做得比较精细就很别扭后期维护也麻烦。我想要的是一个能持续迭代的脚本而不是一个录好的“宏”。最后我选了Selenium核心原因是它最贴合“Python首记”这个定位。打卡只是目标中间涉及的元素定位、显式等待、异常处理、定时任务全是办公自动化里的通用技能。做完这个项目这些技能都能迁移到其他自动化任务上比如自动填报表、自动发周报。1.3 项目边界与运行前提写这个脚本之前有几个外部条件必须先确认公司的OA系统必须支持浏览器登录。现在大部分OA都支持但如果公司有严格的网络准入或必须在公司内网才能访问那你要考虑脚本部署的位置不能让脚本在你家里连不上公司系统。需要有一台每天固定开机的电脑。可以是自己的工位电脑也可以是公司给的备用机或者在家里的旧笔记本。我一开始放在工位电脑上后来补了一台备用电脑具体原因后面会在“定时任务”和“踩坑实录”里详细说。必须确认公司是否允许自动化打卡。有些公司明确要求指定IP、指定设备打卡有些公司禁止任何脚本操作碰到这种情况就别碰了。我的原则很简单脚本是帮你“按时打卡”的不是帮你“假装在岗”的用途一旦变味问题就不是技术问题了。这些边界想清楚之后才开始动手。2. 环境准备与Selenium核心用法拆解2.1 搭建环境Python、Selenium、驱动管理器项目的第一步是把环境搭起来。我用的Python版本是当时最新的稳定版安装的时候记得把“Add Python to PATH”勾上不然后面命令行里敲python会提示找不到命令。然后是创建虚拟环境。这一步很多人会偷懒跳过但我建议不要省因为自动打卡脚本会用到一堆第三方库虚拟环境能把依赖隔离开避免和系统里其他Python项目互相污染。python -m venv venv source venv/bin/activate # Windows下用 venv\Scripts\activate pip install selenium webdriver-manager这里有个关键选择我安装了webdriver-manager这个辅助库。以前用Selenium的人都知道最头疼的问题之一就是浏览器驱动和浏览器版本对不上Chrome浏览器一更新旧驱动就失效然后脚本报一堆看不懂的错。用了webdriver-manager之后它会自动检测你本地浏览器的版本然后下载对应的驱动省掉了手工维护驱动这一步。初始化和打开OA系统的核心代码长这样from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice) driver.get(https://oa.example.com)如果你公司用的是Edge浏览器那就把ChromeDriverManager换成EdgeChromiumDriverManager写法几乎一模一样。这里要提醒一句OA地址我这里写的是占位符实际用的时候要替换成你自己公司系统的真实地址。2.2 定位打卡按钮优先稳定属性而不是花式选择器Selenium能做自动化核心能力就是“找到页面上的元素”。常见定位方式有这么几种By.ID通过元素ID找。最快但很多前端框架会生成动态ID每次刷新都不一样这点后面会踩坑。By.CLASS_NAME通过CSS类名找。适合类名稳定且唯一的场景。By.XPATH通过路径表达式找。最灵活可以按文本内容定位是我这个项目里用得最多的。By.CSS_SELECTOR类似前端的CSS选择器定位复杂结构很方便但可读性一般。我的建议是有稳定ID优先用ID没有ID用XPATH配合文本内容定位尽量不要用“第几个按钮”这种索引定位因为页面结构一变顺序就乱了脚本立刻失效。比如我们OA系统里下班打卡按钮的HTML大致是这样的结构按钮文字外面包了一层span。这种情况下用普通contains(text(), ...)可能匹配不到因为真正的文本不在button的直接子节点里。比较稳妥的写法是用contains(., ...)这个.代表当前节点的全部文本内容包括子孙节点from selenium.webdriver.common.by import By clock_btn driver.find_element(By.XPATH, //button[contains(., 下班打卡)])这种定位方式对前端还有一个好处哪怕按钮的样式、图标全换了只要文字还是“下班打卡”脚本就依然能跑。2.3 等待的艺术显式等待优于sleep刚写Selenium脚本的人十有八九会遇到NoSuchElementException。原因很简单页面还在加载元素还没渲染出来脚本就直接找了。所以“等待”是Selenium里绕不开的话题。等待有三种常见做法我对比一下time.sleep(n)强制睡n秒。最简单但不推荐。网络快的时候白等好几秒网络慢的时候睡几秒还是不够只能靠蒙。隐式等待给driver设置一个全局的轮询时间比如driver.implicitly_wait(10)之后每次找元素都会在10秒内反复尝试。它省心但不够精细无法表达“等这个按钮变得可点击”这种状态。显式等待针对某个元素、某个条件等待这是最推荐的方式。比如等登录按钮变得可点击再比如等某个元素出现在页面上。我的脚本里大量使用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, 15) login_button wait.until(EC.element_to_be_clickable((By.ID, btnLogin))) login_button.click()这里WebDriverWait(driver, 15)的意思是最长等15秒每隔一小段轮询一次直到按钮可点击。相比固定sleep它既稳又不会浪费过多时间。3. 实战从登录到打卡的完整脚本3.1 登录模块账号密码永远不要写死在代码里登录是自动打卡的第一步。我们公司OA的登录页面就是标准的账号密码加一个登录按钮没有验证码这给自动化省了很大麻烦。登录代码本身不复杂import os username os.getenv(OA_USERNAME) password os.getenv(OA_PASSWORD) driver.find_element(By.ID, username).send_keys(username) driver.find_element(By.ID, password).send_keys(password) driver.find_element(By.ID, loginBtn).click()两个地方需要重点说明。第一账号密码从环境变量里读取不要写成明文。原因很简单脚本文件很容易被到处拷贝如果你发到群里、传到仓库、或者同事看到了账号密码就直接泄露了。环境变量虽然也不是绝对安全但至少避免了口令直接躺在代码里。Windows下可以在系统环境变量里配置Linux下可以写在.bashrc里或者用一个不提交到版本库的config.py来管理。第二登录之后要判断有没有真正登录成功而不是点完按钮就继续。最简单的方式是等待登录后页面上的某个关键元素出现或者判断URL变化。我当时的判断是等待首页侧边栏的“考勤管理”菜单出现wait.until(EC.presence_of_element_located((By.XPATH, //span[contains(., 考勤管理)])))如果这一步卡住说明登录可能失败了后面会走到异常处理逻辑。3.2 打卡逻辑先判断状态再决定要不要点击登录之后进入考勤页面这时候脚本不能闭着眼睛点“打卡”而是要有一个状态判断。为什么因为早晚两次打卡如果脚本早上正常打了上班卡晚上它再跑到同一个页面上发现按钮可能已经变成了“你今天已完成打卡”的状态。如果还硬点一下“上班打卡”轻则提示重复打卡重则可能被系统记为异常。所以我写了一个简单的判断from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 15) def do_clock(): status_el driver.find_element(By.XPATH, //span[classclock-status]) status_text status_el.text current_day driver.find_element(By.XPATH, //div[classcurrent-date]).text if 已打卡 in status_text: print(f{current_day}: 已经打卡跳过本次操作) logging.info(已打卡跳过) return clock_btn wait.until(EC.element_to_be_clickable( (By.XPATH, //button[contains(., 打卡)]) )) clock_btn.click() time.sleep(1) logging.info(打卡完成)我特别推荐你加上这个“幂等判断”让脚本变成可重复执行的。这样哪怕定时任务因为某种原因跑了两次也不会做出重复操作。这就像人按了门铃以后又按第二次虽然大概率没事但没必要。另外这里的time.sleep(1)是点击打卡后给系统一个处理时间。虽然前面说过不建议滥用sleep但在点击按钮之后等待后端响应短时间的固定延时是可以接受的。3.3 日志和失败通知让脚本变成“会告警的员工”自动化脚本最怕的一种情况是“静默失败”。什么叫静默失败脚本挂了没有任何提示你以为已经打卡成功了其实它压根没运行。等你发现的时候已经过了补卡时间这是最悲剧的。所以日志和通知不是可有可无的功能是整个系统的“眼睛”。我用标准库logging把每次运行的结果写到日志文件里import logging logging.basicConfig( filenameoa_clock.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s ) logging.info(脚本启动开始登录OA系统) logging.info(打卡成功) logging.error(登录失败等待重试)日志写完后每次运行都能回头看。比如某天打卡没成功打开日志一看发现是“等待考勤管理菜单超时”就知道是登录环节出了问题而不是打卡按钮的问题。但日志只能事后看不能主动告诉你。所以我还加了一层“失败通知”当脚本出现异常时通过自定义机器人把报错信息推送到手机。实现方式有很多常见的是用requests给企业微信机器人或者类似的通知渠道发一条POST消息。这一段代码各家接口不一样我只说思路把打卡成功、失败、跳过这三种状态都作为消息发出来你就再也不用每天担心脚本有没有跑成功。3.4 定时任务把脚本交给系统按时执行脚本本身跑一次只能打一次卡真正让它每天自动执行的是操作系统级定时任务。我这边涉及两种环境分别说。在公司工位电脑上我用的是Windows任务计划程序。操作路径是“控制面板→管理工具-任务计划程序→创建基本任务”关键配置如下触发器选择“每天”开始时间设置为上班前五分钟左右比如公司九点上班就设成08:55。操作选择“启动程序”程序填python.exe的完整路径参数填脚本完整路径起始于填脚本所在目录。勾选“如果任务错过开始时间尽快启动任务”这一项很重要否则电脑休眠后醒来可能不会补跑。如果你有一台Linux机器用cron会更直接。比如工作日早8点55分执行55 8 * * 1-5 /usr/bin/python3 /home/user/oa_clock.py /home/user/oa_job.log 21这里1-5代表周一到周五把脚本标准输出和错误输出都重定向到日志文件方便排查。时间上为什么建议提前五分钟而不是刚好卡在上班时间因为浏览器启动、登录、页面加载、点击打卡每一个环节都需要时间。如果设成09:00整网络一慢真正点到按钮可能已经是09:01这卡打得就没有意义了。提前五分钟就算第一次跑失败了还来得及人工补一下。4. 常见问题与排查技巧实录4.1 元素定位失败动态ID和iframe是头号“凶手”最常见的报错就是NoSuchElementException。我在开发过程中遇到的情况基本分三类。第一类是元素在iframe里。很多OA系统页面结构复杂考勤模块可能嵌在iframe里直接在外层页面找元素当然找不到。解决办法是切换进iframe再操作driver.switch_to.frame(mainFrame) # 用iframe的id或name操作完成后再切回默认内容。第二类是动态ID。有的前端框架会给每个元素生成随机ID这次打开是btn_abc123下次就是btn_xyz789。这时候就不能写死ID要用XPATH按文本定位或者找其他稳定的属性。第三类是页面加载慢。这个之前说过了用显式等待解决。我的排查方法很简单粗暴但很有效脚本出错时先让浏览器截图保存到本地再打印当前页面的HTML片段。截图能直观看到页面停在哪一步比反复猜选择器高效得多driver.save_screenshot(debug.png) print(driver.page_source[:3000])4.2 网络抖动导致登录失败重试机制比一次性到底更重要内部OA系统偶尔也会抽风尤其早高峰网络拥堵登录请求可能超时或者登录按钮点了但页面没跳转。第一次遇到这种问题脚本直接崩溃退出打卡自然失败了。解决思路是给整个打卡流程包一层重试机制。很简单就是循环尝试失败后等一段时间再试最多试三次import time MAX_RETRY 3 for attempt in range(MAX_RETRY): try: do_clock() break except Exception as e: logging.error(f第{attempt 1}次尝试失败: {e}) send_alert(fOA打卡第{attempt 1}次失败: {e}) if attempt MAX_RETRY - 1: time.sleep(30) driver.refresh()重试间隔通常建议30秒到1分钟。太短触发系统风控太长可能错过打卡时间。我实测下来30秒比较合适既能避开短时网络波动又不会拖太久。4.3 浏览器更新后脚本莫名失效驱动版本要一直跟上这是我踩过最隐蔽的一个坑。某天早上起来我习惯性看了眼手机发现没有收到打卡成功的通知。去工位看了日志发现报的是驱动Session相关的错误。查了一圈原来是前一天Chrome自动更新了浏览器的内核版本变了但Selenium的驱动还是旧的两边对不上。如果你的环境允许联网安装依赖用webdriver-manager能省心很多它会在脚本启动时自动检测浏览器版本并匹配驱动。如果你的工作环境不允许自动下载那就只能手动管理固定浏览器版本关闭自动更新或者订个日历提醒定期升级驱动。无论如何驱动版本匹配问题必须纳入日常维护清单不然某天脚本突然失效你根本来不及反应。4.4 维护与合规自查清单自动化脚本写完并不意味着一劳永逸它需要定期维护尤其是在OA系统改版之后。我给自己列了一个自查清单整理成表格供你参考自查项检查方式频率账号密码是否有效登录后的日志是否正常每周一次页面元素定位是否仍然有效运行日志中有没有NoSuchElement异常每次运行后浏览器驱动与浏览器版本是否匹配启动日志有没有Session错误每次运行后定时任务是否按时触发检查计划程序运行历史或cron日志每周一次公司考勤制度是否允许自动化主动咨询行政或主管确认有疑问时立即确认脚本是否只做“按时打卡”检查逻辑中没有修改考勤数据的行为每次改代码时最后再强调一遍合规问题。我写这个脚本的前提是公司没有明确禁止远程登录和自动化操作而且我只是用脚本替代“手动点击”这个动作没有篡改任何考勤数据。如果你所在的公司制度明确不允许那这篇文章的内容就当个技术学习案例看看就好不要直接套用到违规场景里。自动化是用来解放重复劳动的不是用来钻制度空子的。最后分享一个我自己折腾后的经验。一开始我把脚本部署在工位电脑上以为每天上班反正电脑都开着任务计划肯定没问题。结果有一次Windows自动更新后自动重启任务计划因为“系统重启后未登录”直接没跑等我发现已经迟了。后来我把脚本挪到了一台无人值守的备用电脑上并且把运行结果通过消息通知实时推到手机。从那以后我再也没有因为打卡的事操过心。这个项目让我体会最深的一点是自动化脚本的部署环境往往比脚本本身更值得花时间。