GitHub Actions 自动化打卡:定时任务与脚本实现指南
1. 从手动打卡到自动化一个真实需求的拆解每天睁眼第一件事就是摸手机、点开某个页面、找到打卡入口、勾选健康状态、提交——这套动作我连续做了大半年直到某天早上因为睡过头漏了一次被提醒补报才下定决心把它彻底自动化掉。这个项目的核心目标很明确用 GitHub Actions 这个免费的持续集成服务在每天固定时间自动触发一个脚本完成健康信息填报和网站签到全程不需要本地电脑开机也不需要服务器常驻。说白了就是把人每天手动点一遍这件事交给云端的一台临时机器去干。GitHub Actions 对公开仓库完全免费私有仓库每月也有 2000 分钟的额度而一次打卡任务通常十几秒就跑完了一个月撑死消耗不到 10 分钟成本几乎为零。这套方案适合所有被重复性网页操作困扰的人——不管是每日健康填报、论坛签到、还是各种需要每天点一下的积分任务思路都是通用的。我把它拆成三个层次来理解触发层谁来定时叫醒任务、执行层脚本怎么模拟人的操作、通知层跑完了怎么告诉我结果。很多人一上来就写脚本结果定时不准、登录失效、失败了还不知道问题全堆在一起。正确的做法是先想清楚这三层各自用什么方案再动手写代码。2. 整体方案设计与技术选型2.1 为什么选 GitHub Actions 而不是其他方案自动化定时任务的选择其实不少我对比过几种常见路线最后选 GitHub Actions 是有明确理由的。方案成本稳定性维护难度是否需要常驻设备本地电脑定时任务免费差关机就断低是云服务器 cron每月十几到几十元好中否树莓派等设备一次性硬件成本中断电断网就挂中是GitHub Actions免费好低否本地电脑方案最大的问题是你永远不知道它什么时候会关机或休眠我试过用系统自带的任务计划结果出差几天笔记本合盖任务全断了。云服务器稳定但要花钱而且为了一个打卡任务养一台机器实在浪费。GitHub Actions 的优势在于它本身就是为定时跑一段代码设计的仓库里放好脚本和配置文件剩下的交给平台你连服务器都不用碰。注意GitHub Actions 的定时触发schedule在仓库长时间无活动时可能会被平台暂停这是很多人踩过的坑。解决办法是偶尔手动触发一次或者让任务本身产生一次提交记录保持仓库活跃。2.2 执行层脚本用什么写执行层我推荐用Python requests或者Python Playwright具体看目标网站的类型。如果打卡页面是纯表单提交、接口清晰用 requests 直接构造 HTTP 请求最轻量几行代码搞定运行时间以毫秒计。但现实情况往往是页面有 JavaScript 渲染、有验证码、有动态 token这时候 requests 就力不从心了得请出 Playwright 这种能驱动真实浏览器的工具。我的经验是先抓包看接口能用 requests 就别上浏览器。浏览器方案虽然通用但启动慢一次要十几秒、依赖重、在 CI 环境里还容易因为缺少系统库而报错。只有当接口加密复杂到逆向成本过高时才退而求其次用浏览器模拟。2.3 通知层跑完了怎么知道结果自动化任务最怕的是静默失败——你以为它在跑其实早就挂了。所以通知层不是可选项是必选项。常见的通知方式有几种邮件通知GitHub Actions 自带失败邮件但只在任务失败时发成功不发而且邮件容易被淹没。Server 酱 / 推送机器人国内常用的微信推送方案配置简单成功失败都能推。企业协作工具的 Webhook如果团队在用可以直接推到群里。我个人的做法是脚本里主动调用一个推送接口把执行结果成功/失败/关键返回值发出来。这样每天收到一条消息心里踏实。配置方式通常是在仓库的 Secrets 里存一个推送 key脚本读取环境变量后发送。2.4 敏感信息怎么存Secrets 的正确用法账号密码、Cookie、推送 key 这些东西绝对不能明文写在代码里尤其是公开仓库。GitHub 提供了Secrets功能在仓库的 Settings → Secrets and variables → Actions 里添加脚本通过${{ secrets.XXX }}读取。这里有个细节很多人不知道Secrets 一旦保存就无法再查看原值只能覆盖。所以第一次配置时一定要确认无误。另外Secrets 在日志里会被自动打码成***但如果你在脚本里把它打印出来又被平台识别失败仍有可能泄露所以永远不要在脚本里 print 密码。3. 核心实现细节与实操要点3.1 抓包分析搞清楚打卡到底发了什么请求这是整个项目最关键的一步也是最容易被跳过的一步。很多人上来就问脚本怎么写其实应该先问这个网站到底是怎么完成打卡的。打开浏览器的开发者工具F12切到 Network 面板然后手动完成一次打卡观察期间发出的请求。你要重点关注这几个信息请求 URL打卡接口的地址。请求方法GET 还是 POST。请求头Headers尤其是 Cookie、User-Agent、Referer、Content-Type有些网站还会校验自定义头。请求体PayloadPOST 请求提交的表单数据或 JSON。响应内容返回的 JSON 里通常有状态码和提示信息用来判断是否成功。我实测下来大部分健康打卡类页面的接口都比较直白就是一个 POST 请求带着几个字段。但要注意有些网站会在请求头里带一个动态生成的 token这个 token 可能是页面加载时由 JS 算出来的直接复制会过期。遇到这种情况要么找到 token 的生成规律用代码复现要么改用浏览器方案。提示抓包时记得勾选Preserve log保留日志否则页面跳转后请求记录会被清空你就抓不到关键请求了。3.2 用 requests 构造请求的完整思路假设抓包后发现打卡就是一个简单的 POST那脚本结构大致如下以下为通用示例字段名需按实际替换import os import requests # 从环境变量读取敏感信息避免硬编码 cookie os.environ.get(TARGET_COOKIE) push_key os.environ.get(PUSH_KEY) url https://example.com/api/checkin headers { Cookie: cookie, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..., Content-Type: application/json, Referer: https://example.com/checkin/page, } payload { status: healthy, location: school, } resp requests.post(url, headersheaders, jsonpayload, timeout15) result resp.json() print(返回状态:, result.get(code))这段代码有几个要点值得展开。第一timeout 必须设否则网络卡住时任务会一直挂着直到 GitHub Actions 的默认超时6小时才结束白白浪费额度。第二headers 要尽量还原浏览器尤其是 User-Agent 和 Referer很多网站会做基础校验缺了就直接拒绝。第三判断成功不能只看 HTTP 状态码很多接口 HTTP 返回 200 但 JSON 里的 code 是失败的必须解析响应体确认。3.3 Cookie 的获取与保鲜问题Cookie 是这类脚本的命脉也是最容易失效的部分。手动从浏览器复制 Cookie 能用一段时间但通常几天到几周就会过期届时任务会静默失败。解决思路有两条。一是接受手动更新把 Cookie 存在 Secrets 里失效了就去浏览器重新复制一次覆盖。这种方式简单但需要你留意通知收到失败提醒就去更新。二是自动登录获取 Cookie脚本里先用账号密码调登录接口拿到新的 Cookie 再去打卡。后者一劳永逸但前提是登录接口没有验证码、没有复杂的加密。我个人的建议是如果登录流程简单优先做自动登录如果登录有验证码等障碍就老老实实手动维护 Cookie同时把失败通知做扎实别让自己蒙在鼓里。3.4 浏览器方案Playwright 的取舍当接口实在搞不定时Playwright 是可靠的备选。它的思路是启动一个无头浏览器像真人一样打开页面、填表单、点按钮。from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch() context browser.new_context() page context.new_page() page.goto(https://example.com/login) page.fill(#username, os.environ[USERNAME]) page.fill(#password, os.environ[PASSWORD]) page.click(#submit) page.wait_for_load_state(networkidle) # 后续打卡操作... browser.close()在 GitHub Actions 里跑 Playwright 需要额外安装浏览器依赖配置稍麻烦启动也慢。所以我的原则始终是能用接口就别用浏览器浏览器是最后的手段。它的价值在于通用性强页面改版了往往也不用大改脚本容错率高。3.5 定时触发的写法与坑GitHub Actions 的定时用 cron 表达式写在 workflow 文件里on: schedule: - cron: 0 22 * * * workflow_dispatch:这里有个极其重要的坑cron 用的是UTC 时间不是北京时间。北京时间比 UTC 快 8 小时所以如果你想在早上 6 点打卡UTC 时间应该是前一天 22 点也就是0 22 * * *。我第一次配置时没换算结果任务在下午才跑白白错过。另外GitHub 的定时任务不保证准点执行高峰期可能延迟几分钟到几十分钟。所以别把时间卡得太死比如要求必须 8:00 整打卡留出缓冲更稳妥。workflow_dispatch这一行是手动触发开关方便你调试时不用等定时。注意定时任务只在默认分支上生效。如果你在别的分支测试schedule 是不会触发的必须合并到主分支。4. 完整实操流程与配置落地4.1 仓库结构规划一个清晰的仓库结构能让后续维护省心很多。我习惯这样组织repo/ ├── .github/ │ └── workflows/ │ └── checkin.yml # 工作流定义 ├── scripts/ │ └── checkin.py # 打卡脚本 ├── requirements.txt # Python 依赖 └── README.md脚本和配置分离依赖单独管理这样以后要加第二个打卡任务时直接复制一份脚本改改就行工作流里加一行调用即可。4.2 工作流文件的完整写法下面是一个可以直接参考的工作流模板name: Daily Checkin on: schedule: - cron: 0 22 * * * workflow_dispatch: jobs: checkin: runs-on: ubuntu-latest steps: - name: 拉取代码 uses: actions/checkoutv4 - name: 配置 Python 环境 uses: actions/setup-pythonv5 with: python-version: 3.11 - name: 安装依赖 run: pip install -r requirements.txt - name: 执行打卡 env: TARGET_COOKIE: ${{ secrets.TARGET_COOKIE }} PUSH_KEY: ${{ secrets.PUSH_KEY }} run: python scripts/checkin.py逐段解释一下。runs-on: ubuntu-latest指定运行环境用最新的 Ubuntu 就行。actions/checkout把仓库代码拉到运行环境里。setup-python装好指定版本的 Python。依赖安装和脚本执行是核心两步。env块把 Secrets 注入成环境变量脚本里用os.environ读取。4.3 参数计算与时间换算实操时间换算是新手最容易翻车的地方我专门整理一个对照表省得每次都要心算想要的北京时间对应的 UTC 时间cron 表达式06:00前一天 22:000 22 * * *08:0000:000 0 * * *12:0004:000 4 * * *20:0012:000 12 * * *cron 五个字段依次是分、时、日、月、周。0 22 * * *表示每天 UTC 22:00也就是北京时间次日 6:00。如果你想工作日才打卡把周字段改成1-5即0 22 * * 1-5。4.4 首次调试的正确姿势别指望一次就成功调试是必经环节。我的调试流程是这样的先在本地把脚本跑通确认能成功打卡这一步排除掉脚本本身的逻辑问题。把脚本推到仓库配置好 Secrets。用workflow_dispatch手动触发一次看日志。日志里重点看脚本的 print 输出和报错堆栈。确认成功后再等定时触发验证一次。本地跑通这一步千万别省。我见过太多人直接在 Actions 上调试结果分不清是脚本问题还是环境问题来回折腾。本地能跑通说明逻辑没问题剩下的就是环境配置排查范围一下子缩小了。4.5 通知推送的接入在脚本末尾加上推送逻辑把结果发出来def notify(push_key, title, content): if not push_key: return requests.post( https://push.example.com/send, data{title: title, content: content}, timeout10, ) # 打卡结束后调用 notify(push_key, 打卡结果, f状态码: {result.get(code)}, 消息: {result.get(msg)})推送内容里带上关键返回值这样你一眼就能看出是真成功还是假成功。有些网站打卡成功和今日已打卡返回的 code 不同脚本里要分别处理避免重复打卡报错。5. 常见问题与排查技巧实录5.1 任务没触发或触发时间不对这是最高频的问题。排查顺序如下先确认 workflow 文件在默认分支上再确认 cron 时间换算是否正确然后检查仓库是否因为长期无活动被暂停了定时任务。如果都正常但还是不准点那就是平台调度延迟属于正常现象把时间预期放宽即可。5.2 脚本报错但看不出原因Actions 的日志其实很详细关键是会看。展开失败的 step从下往上看报错堆栈。常见错误类型我整理成表报错关键词可能原因解决方向ModuleNotFoundError依赖没装检查 requirements.txtKeyError环境变量没读到检查 Secrets 名称拼写ConnectionError网络问题或地址错误检查 URL 和超时设置JSONDecodeError返回的不是 JSON打印原始响应排查401 / 403Cookie 失效更新 Cookie5.3 Cookie 频繁失效怎么办先判断是 Cookie 本身过期还是被网站的风控识别了。如果是前者考虑做自动登录如果是后者检查请求头是否完整、请求频率是否过高。我遇到过一次是因为脚本没带 Referer被判定为异常请求补上之后就稳定了。5.4 打卡显示成功但实际没生效这种情况通常是脚本判断逻辑太宽松只要 HTTP 200 就认为成功。解决办法是解析响应体里的业务状态码只有明确的成功标识才算通过。另外有些网站打卡后需要二次确认抓包时要留意是否有后续请求。5.5 独家避坑经验分享几个文档里不会写、但实际很关键的技巧。第一给脚本加详细的日志每一步都 print 出来出问题时日志就是你的眼睛。第二把关键配置做成可开关的比如调试时打开详细输出稳定后关掉。第三定期检查任务是否还在跑别配完就不管了我一般每周瞄一眼通知记录。第四Secrets 更新后要重新触发一次验证别想当然以为改了就生效。6. 方案的可扩展性与个人体会这套框架搭好之后它的价值远不止打卡这一件事。任何每天需要点一下的重复操作都可以套用同样的模式抓包分析接口、写脚本模拟请求、用 Actions 定时触发、加通知兜底。我后来陆续把好几个类似的日常任务都迁移到了这个框架上维护成本极低。扩展的方向也很多。比如把多个任务合并到一个工作流里顺序执行减少触发次数比如把执行结果写入一个文件并提交回仓库形成历史记录方便回溯再比如用矩阵策略matrix同时跑多个账号。这些都属于锦上添花核心逻辑始终是那三层触发、执行、通知。我个人在实际操作中的体会是自动化这件事最大的门槛不是写代码而是耐心地抓包分析和反复调试。很多人卡在第一步就放弃了其实只要把请求搞清楚后面的代码都是体力活。另外一定要对静默失败保持警惕通知层做扎实比脚本写得多漂亮都重要。最后再分享一个小技巧把常用的抓包字段和请求头整理成一个模板存起来下次遇到新任务时直接套能省下大量重复劳动。