用Python+uiautomator2打造高可靠钉钉自动打卡系统
每天早上匆忙赶地铁时,最怕的就是错过打卡时间。传统的钉钉机器人消息打卡虽然简单,但遇到需要滑动验证或点击按钮的复杂场景就束手无策。本文将带你用Python和uiautomator2构建一个能模拟真实手机操作的自动打卡系统,彻底解决迟到烦恼。
1. 为什么需要UI自动化打卡
钉钉的打卡系统越来越智能,简单的HTTP请求打卡已经无法满足所有场景:
- 地理围栏打卡:需要真实获取手机GPS位置
- 人脸识别打卡:部分企业启用了生物识别验证
- 滑动验证码:防止机器人操作的常见手段
- 动态按钮点击:打卡按钮位置可能随版本更新变化
传统消息打卡的局限性:
import requests response = requests.post('https://oapi.dingtalk.com/robot/send', json={ "msgtype": "text", "text": {"content": "打卡"} })这种方式的缺陷很明显:
- 无法处理图形验证
- 不支持需要定位的打卡
- 容易被系统识别为机器人操作
2. 环境搭建与工具准备
2.1 基础环境配置
首先确保你的开发环境满足以下要求:
- Python 3.7+
- 安卓手机(开发者模式已开启)
- USB调试线或稳定的无线ADB连接
- 电脑安装ADB工具
安装核心工具链:
pip install --pre uiautomator2 # UI自动化核心库 pip install pillow # 图像处理支持 adb devices # 确认设备连接2.2 手机端服务部署
在手机上部署atx-agent服务:
- 连接手机并确保adb devices能识别设备
- 初始化uiautomator2服务:
python -m uiautomator2 init- 手机上会出现ATX应用,保持运行状态
注意:部分手机需要手动允许ATX的悬浮窗权限和后台运行权限
3. 钉钉自动化操作实战
3.1 设备连接与屏幕控制
建立Python与手机的连接:
import uiautomator2 as u2 # 通过设备序列号连接 d = u2.connect('123456f') # 或者通过无线ADB连接 d = u2.connect('192.168.1.100:5555') # 基础操作示例 d.screen_on() # 唤醒屏幕 d.unlock() # 解锁(需提前设置好解锁方式)3.2 定位钉钉元素
使用元素定位技术找到打卡按钮:
# 通过文本定位 d(text="工作台").click() # 通过资源ID定位(需先获取界面层级) d(resourceId="com.alibaba.android.rimet:id/home_bottom_tab_button_work").click() # 通过XPath定位 d.xpath('//*[@text="考勤打卡"]').click()元素定位策略对比:
| 定位方式 | 稳定性 | 执行速度 | 适用场景 |
|---|---|---|---|
| 文本定位 | ★★★☆ | 快 | 文字固定的按钮 |
| ID定位 | ★★★★ | 最快 | 标准安卓应用 |
| XPath | ★★☆☆ | 慢 | 复杂层级结构 |
3.3 处理特殊打卡场景
案例1:滑动验证码处理
# 获取滑块位置 slider = d(resourceId="nc_1_n1z") slider_pos = slider.info['bounds'] # 计算滑动距离 start_x, start_y = slider_pos['left'], slider_pos['top'] end_x = start_x + 300 # 滑动300像素 # 执行滑动操作 d.swipe(start_x, start_y, end_x, start_y, 0.5) # 0.5秒完成案例2:人脸识别绕过方案
if d(text="人脸识别").exists: d.press("back") # 返回键退出识别 # 自动切换到密码验证 d(text="使用密码验证").click() d.send_keys("your_password")4. 完整自动化流程实现
4.1 主流程设计
def auto_clock_in(): d = u2.connect() # 自动选择可用设备 try: # 启动钉钉 d.app_start("com.alibaba.android.rimet") # 导航到打卡页面 d(text="工作台").click() d.xpath('//*[@text="考勤打卡"]').click(timeout=10) # 处理可能出现的弹窗 if d(text="立即打卡").exists: d(text="立即打卡").click() elif d(text="上班打卡").exists: d(text="上班打卡").click() # 验证打卡结果 if d(text="打卡成功").exists(): return True except Exception as e: print(f"打卡失败: {str(e)}") return False4.2 定时执行与异常处理
使用APScheduler实现智能定时:
from apscheduler.schedulers.blocking import BlockingScheduler def job(): if not auto_clock_in(): # 失败重试逻辑 time.sleep(30) auto_clock_in() scheduler = BlockingScheduler() scheduler.add_job(job, 'cron', hour=9, minute=0) # 每天9点执行 scheduler.start()异常处理增强方案:
- 截图保存错误现场
- 自动重启ADB服务
- 失败通知(邮件/短信)
5. 高级优化技巧
5.1 动态元素等待策略
# 智能等待元素出现 element = d(text="打卡").wait(timeout=10) if element: element.click() else: raise Exception("打卡按钮未找到") # 轮询检查 def wait_until(condition, timeout=10): start = time.time() while time.time() - start < timeout: if condition(): return True time.sleep(0.5) return False wait_until(lambda: d(text="打卡成功").exists())5.2 多设备支持方案
devices = u2.list_devices() for serial in devices: d = u2.connect(serial) try: auto_clock_in(d) except: continue5.3 性能优化建议
- 使用
d.dump_hierarchy()缓存界面结构 - 减少不必要的截图操作
- 并行处理多个设备
6. 实际部署注意事项
- 设备保持充电状态:防止夜间断电
- 关闭系统自动更新:避免界面变化导致脚本失效
- 备用网络方案:同时配置Wi-Fi和移动数据
- 定期维护脚本:钉钉更新后及时调整元素定位
在小米手机上测试时发现,某些MIUI版本会限制后台应用的活动,需要在电池设置中将钉钉和ATX设置为"无限制"。