资讯动态

Python签到脚本实战:接口自动化、重试与定时调度

发布时间:2026/10/29 4:19:24 来源:尧图企业网站定制
1. 我为什么最终决定自己写一个签到脚本1.1 每天点几下这件事到底值不值得自动化先说结论一个持续运行的签到脚本本质上解决的是“重复性人机交互”的问题。你每天要在某个积分平台上完成签到、领任务奖励、看几个页面、点几次按钮单次操作不到一分钟但一年三百多天累积下来就是十几个小时的纯机械劳动而且最容易出问题的恰恰是人——出差忘了、熬夜睡过头、手机换设备登录态失效任何一次遗漏都意味着连续签到天数归零之前攒的积分加成直接打水漂。我接触这类需求是从一个很朴素的场景开始的某积分平台的规则是连续签到第 7 天、第 30 天有额外奖励断一天就从头计算。手点的那几个月里我至少断了四五次每次都得重新攒。后来索性花一个周末写了套自动化签到脚本跑了大半年除了两次凭证过期需要重新登录其余时间基本没管过。这篇内容讲的就是这套东西的完整实现思路签到脚本怎么设计、请求怎么还原、凭证怎么保存、定时怎么调度、出错怎么排查。适合两类人看一类是有少量 Python 基础、想自己动手做一个日常自动化小工具的人另一类是想了解“接口自动化”这件事的通用方法论的读者——签到只是最轻量的应用场景同样的骨架换成数据采集、报表拉取、消息提醒逻辑是相通的。我不打算把它写成一份教科书式的 API 文档而是按我实际搭建的顺序来先讲清楚为什么这么设计再讲每一步的具体做法最后把踩过的坑摊开给你看。如果你只想抄作业第 3 章的代码和第 5 章的排查表可以直接拿去用如果你想理解原理第 1、2 章值得慢慢看。1.2 三种实现路径的取舍插件、云端、本地脚本在动手写代码之前我先花了半天时间对比三条路这个环节很多人会跳过结果写到一半发现方向不对返工成本很高。方案实现方式优点明显短板适用人群浏览器插件用油猴类脚本在页面里注入点击逻辑上手最快几十分钟能跑通不用处理登录态必须开着浏览器和页面换设备就失效页面改版就得跟着改选择器完全不想碰代码的人云端定时任务把脚本部署到一台长期在线的机器上不用管本地是否开机触发准时调试链路长日志不直观凭证更新麻烦有一定运维经验的人本地常驻脚本自己电脑上跑 Python配系统定时任务调试直接、日志清楚、改一行立刻生效依赖本机开机笔记本合盖会错过触发时间大多数个人使用者我最后选的是第三条理由很实际签到这个动作对时间精度要求不高只要当天内完成即可本地机器哪怕晚几个小时执行也完全没问题。而云端方案虽然听着高级但为了一个每天几十秒的任务去维护一台远端机器、处理部署和日志回传投入产出比太低。浏览器插件则被我直接排除了——它的脆弱性太强页面结构稍微改一下选择器就全废而且我经常在几台设备之间切换登录态不同步。这里有个取舍原则值得说清楚自动化工具的价值 节省的时间 − 维护成本。签到脚本每天省你 1 分钟一年 6 小时如果维护它每个月要花你半小时一年也是 6 小时那就白干了。所以设计的核心目标不是“功能多”而是“改版后能在 10 分钟内修好”。后面所有的结构设计包括配置文件外置、日志分级、失败重试都是围绕这个目标来的。1.3 整体架构三层结构各管什么我把整个脚本拆成三层这个划分方式沿用自我做其他自动化项目时的习惯好处是每层职责单一出问题定位快。第一层是凭证层负责保存和管理登录态。这一层最容易坏因为凭证会过期所以它必须做到“坏了能马上换、换了不用改代码”。我的做法是把 Cookie 和 Token 统一放进一个配置文件脚本启动时读取不硬编码在代码里。第二层是执行层负责构造请求、发送、解析返回、判断结果。这一层是核心逻辑也是改版频率最高的地方——平台的接口参数一变改的就是这里。第三层是调度与观测层负责什么时候跑、跑完告诉我结果。定时任务、日志记录、失败告警都在这一层。三层之间用函数调用串联不互相耦合。举个具体的例子某次平台给接口加了个时间戳签名参数我只改了执行层的一个函数配置文件没动、定时任务没动十分钟收工。这就是分层带来的实际收益。2. 环境准备与脚本骨架搭建2.1 Python 环境与依赖清单别装多余的东西环境这块我建议越简单越好。我用的是 Python 3.9 以上的版本依赖只有三个包pip install requests pyyaml logururequests发 HTTP 请求的主力比 urllib 好用太多会话保持、超时控制、重试适配都很成熟。pyyaml读写 YAML 配置文件比 JSON 更适合手写能写注释这点很关键三个月后你自己都忘了某个字段是干嘛的。loguru日志库比标准库 logging 少写很多样板代码一行配置就能做到按天切分、自动清理旧文件。有人会问要不要上 selenium 或者 playwright 这类浏览器自动化工具。我的建议是能用接口解决就绝不碰浏览器驱动。浏览器方案启动慢、占内存、页面一改就崩而且调试时看不到中间请求出问题只能干瞪眼。只有当签到动作绑定了极强的前端行为校验比如必须真实点击、必须滑动验证时才退而求其次考虑浏览器方案。我遇到的这个平台没有这类限制一个 POST 请求就完事了。关于虚拟环境我强烈建议用 venv 隔离开python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate原因很现实这类小脚本往往一放就是一年半载其间你系统里的 Python 版本、全局依赖都可能变。用虚拟环境锁住依赖半年后重新激活还是那套东西不会因为某个包升级导致脚本突然跑不起来。我曾经因为 requests 大版本升级导致某个参数行为变化排查了一晚上从那以后所有长期运行的脚本一律进虚拟环境。2.2 会话管理Cookie 和 Token 到底该抓哪个这是新手最容易卡住的地方。打开开发者工具一堆请求头里的字段看得眼花到底哪个才是必须的先讲原理。服务端识别“你是谁”靠的是凭证。凭证一般有两种形态Cookie是一组键值对浏览器每次请求自动带上。它的特点是生命周期由服务端控制可能几小时就失效也可能几个月。优点是抓取简单从开发者工具的请求头里整段复制就行。Token常见的是放在 Authorization 头里的那串字符串通常是一个长效凭证有效期更长而且更新机制更明确——一般有专门的刷新接口。我的实践结论是优先用 Token没有 Token 再用 Cookie。原因是 Cookie 往往跟设备指纹、IP、UA 绑定换个环境就失效而且过期时间不透明你只能等它坏了才知道。Token 则相对独立过期时间通常在返回体里明示可以提前预警。抓取的具体操作步骤浏览器登录目标平台按 F12 打开开发者工具切到 Network 面板。手动完成一次签到操作。在请求列表里找到那个提交签到动作的请求一般是 POST路径里带 sign、checkin、task 之类的关键词。右键该请求选择 Copy as cURL粘贴到文本编辑器里。从 cURL 命令里把-H后面的请求头逐条抽出来重点是 Cookie、Authorization、User-Agent、Referer 这几个。注意抓取到的凭证等同于你的登录身份绝对不要贴到公开的地方包括代码仓库、截图、聊天群。我自己的做法是配置文件加入 .gitignore仓库里只保留一份config.example.yaml模板。2.3 配置文件设计把易变的东西全部抽出来配置文件我分成四块这个结构用了很久没改过你可以直接照搬# config.yaml account: cookie: 你的完整 Cookie 字符串 user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... referer: https://example.com/checkin target: checkin_url: https://example.com/api/v1/checkin info_url: https://example.com/api/v1/user/info runtime: timeout: 15 max_retry: 3 retry_backoff: 5 sleep_between: 2 notify: enable: false webhook: 设计要点有三个。第一URL 单独拎出来。平台改版最常见的就是接口路径变化把 URL 放在配置文件里改版时只改一行 YAML不用碰 Python 文件也就不会因为手抖改错缩进把脚本搞崩。第二重试参数可调。max_retry和retry_backoff放在配置里意味着你可以在网络状况差的时候临时调大而不用改代码。这个灵活性在排查问题时特别有用。第三保留 example 模板。仓库里放一份config.example.yaml字段全是占位符真正的config.yaml不进版本控制。这样做的好处是半年后你换机器部署照着模板填一遍就能跑不用回头翻代码猜字段名。我踩过的一个坑早期我把 Cookie 直接写在 Python 源码里后来想换账号得先找到那行代码、改字符串、再确认没改错引号。现在改成读配置换账号就是编辑一个文本文件三十秒的事。3. 核心实现请求、重试、日志三件套3.1 接口还原的实操手法从 cURL 到可运行代码拿到 cURL 之后我一般先用一个最小的可运行片段验证通路确认能拿到正确返回再往下写业务逻辑。这一步非常重要很多人在还没验证接口的情况下就写了一堆逻辑最后报错根本分不清是逻辑问题还是请求本身就不对。import requests headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Cookie: sessionxxxxx; uid12345, Referer: https://example.com/checkin, Accept: application/json, text/plain, */*, } resp requests.post( https://example.com/api/v1/checkin, headersheaders, json{type: daily}, timeout15, ) print(resp.status_code) print(resp.text[:500])跑通之后注意看返回体的结构。绝大多数平台的返回都是一个 JSON里面有几个关键字段状态码、提示消息、业务数据。你要做的是把这个结构记下来然后在代码里做判断——不要只看 HTTP 状态码很多平台即使业务失败也返回 200真正的结果藏在返回体的 code 字段里。我的判断顺序是先看 HTTP 状态码是不是 2xx再看返回体里的业务码是不是成功值最后看返回体里有没有“已签到”“重复”这类提示文字。三层判断缺一不可尤其是第三层因为重复签到在某些平台上返回的是业务成功码但实际什么都没给如果你不识别出来日志里就会显示“成功”而你其实什么都没拿到。3.2 请求头与参数构造几个容易翻车的细节请求头这块看起来是照抄实际上有几个地方特别容易出错。User-Agent 必须和抓包时一致。有些平台会把 UA 纳入风控判断你抓包用的是 Chrome脚本里写个 python-requests 默认 UA直接给你返回一个空结果或者要求验证。这个坑我踩过表现是请求返回 200 但 body 是空的查了很久才发现是 UA 的问题。Content-Type 要和实际传参方式匹配。用json传参时 requests 会自动设成 application/json用data传表单时要手动设成 application/x-www-form-urlencoded。这两者混了服务端要么解析不出来要么直接报参数错误。Referer 不是可有可无的。部分平台会校验来源页缺失时拒绝请求。这个字段抄过来就行成本极低但漏了就是查半天的故障。时间戳和随机数。有些接口为了防止重放会要求带一个当前毫秒时间戳和一个随机字符串。这类参数必须在代码里动态生成不能从抓包结果里抄一个固定值——固定的时间戳超过有效窗口就失效了。import time import random import string def gen_nonce(length16): return .join(random.choices(string.ascii_letters string.digits, klength)) def build_params(base: dict) - dict: params dict(base) params[_t] int(time.time() * 1000) params[_nonce] gen_nonce() return params如果接口要求签名把参数按字典序拼接后做哈希那就得额外实现一个签名函数。我的建议是签名逻辑单独放在一个函数里并且写好注释说明拼接规则。因为这是整个脚本里最难逆向、也最容易因为平台调整而出错的部分独立出来以后改版时你只需要盯着这一个函数。关于时间戳的精度这里有个细节值得提一句有的平台校验的是秒级有的要求毫秒级还有的要求与服务端时间偏差在某个窗口内。如果你本机时钟不准比如长期没同步毫秒级校验会直接失败。排查这类问题时先确认一下本机时间能省掉很多无效排查。3.3 重试与退避让脚本真正能长期跑的关键单个请求发出去失败是常态——网络抖动、服务端瞬时 502、偶发的限流这些都不是你的代码有问题但如果不处理一次抖动就意味着当天签到失败。所以重试机制不是锦上添花是必需品。我的重试策略遵循三个原则只重试可恢复的错误。连接超时、读取超时、5xx 状态码这些重试有意义401/403 这种凭证问题重试一百次也没用应该直接退出并告警。指数退避不要固定间隔。第一次失败等 5 秒第二次等 15 秒第三次等 45 秒。固定间隔重试在服务端限流场景下等于自杀——你越密集地打它越不给你过。设置总时长上限。重试次数和单次超时要乘起来算总耗时别让一个卡死的请求把整个任务拖到定时窗口之外。import time import requests def request_with_retry(method, url, *, headers, max_retry3, backoff5, timeout15, **kwargs): last_err None for attempt in range(1, max_retry 1): try: resp requests.request( method, url, headersheaders, timeouttimeout, **kwargs ) if resp.status_code in (401, 403): raise PermissionError(f凭证无效: {resp.status_code}) if 500 resp.status_code 600: raise RuntimeError(f服务端异常: {resp.status_code}) resp.raise_for_status() return resp except PermissionError: raise except Exception as e: last_err e if attempt max_retry: break wait backoff * (3 ** (attempt - 1)) print(f第 {attempt} 次失败: {e}{wait}s 后重试) time.sleep(wait) raise RuntimeError(f重试 {max_retry} 次仍失败: {last_err})这段代码里backoff * (3 ** (attempt - 1))就是退避计算以 5 秒为基础三次重试分别等 5、15、45 秒总耗时 65 秒加上三次请求本身的超时最坏情况两分钟出头完全在接受范围内。这个系数你可以根据实际容忍度调整2 倍也行3 倍是我试下来比较舒服的值——既能扛过短时限流又不至于等太久。3.4 日志设计无人值守的前提是事后能查脚本跑在后台你不可能盯着看。所以日志的作用不是“打印点东西显得专业”而是让你在出问题后能快速定位是哪一步失败的、失败时的返回是什么、重试了几次。我用 loguru 的配置是这样的from loguru import logger import sys logger.remove() logger.add(sys.stdout, levelINFO, format{time:HH:mm:ss} | {level} | {message}) logger.add(logs/checkin_{time:YYYY-MM}.log, rotation10 MB, retention6 months, levelDEBUG, encodingutf-8)这里有两个设计考虑。控制台只输出 INFO 及以上保持清爽文件里记录 DEBUG 级别的完整信息包括请求参数和返回体片段方便事后回溯。retention6 months自动清理半年以上的旧日志省得磁盘被占满——这种小细节不做跑一年后你会发现日志目录几百兆。日志内容上我坚持记录四样东西时间戳、步骤名、结果状态、关键返回片段。比如签到成功时记一行“签到成功获得 12 积分连续 8 天”失败时记下返回体的前 200 字符。这 200 字符在排查时价值极高很多问题看一眼返回原文就明白了。心得日志里千万不要打印完整的 Cookie 或 Token。我见过有人为了调试方便直接把请求头整个打进日志然后把日志文件发到群里求助等于把账号拱手让人。要打就打前 8 位加星号。4. 定时调度与长期运行方案4.1 定时任务怎么配三种场景的不同做法脚本能跑通只是一半另一半是让它按时自己跑。Linux / macOS 用 cron这是最省事的方式crontab -e # 每天早上 8:10 执行输出重定向到日志 10 8 * * * cd /path/to/project ./venv/bin/python main.py logs/cron.log 21两个细节必须注意。第一cron 环境里的 PATH 和你终端里的不一样所以一定要写 Python 的绝对路径虚拟环境里的那个不能只写python。第二cd进项目目录很重要因为脚本读相对路径的配置文件不切目录会找不到文件。这两点是我见过最多的“cron 不生效”原因。Windows 用任务计划程序。创建基本任务触发器选每天固定时间操作选“启动程序”程序填venv\Scripts\pythonw.exe用 pythonw 可以避免弹黑框参数填脚本路径起始位置填项目目录。这里有个坑起始位置不填的话脚本的工作目录会是 system32配置文件直接读不到。macOS 除了 cron 还可以用 launchd但对于签到这种简单任务cron 足够了不必折腾 plist。关于时间点的选择我的经验是避开整点。整点是最拥挤的时间段很多平台的定时任务、其他用户的脚本都堆在这时候响应变慢、偶发限流的概率明显上升。我一般选 8:10 或者 7:43 这种零散时间跑了大半年基本没遇到过拥塞。另外签到任务对时间精度要求不高但如果你的机器是笔记本合盖休眠会错过触发时间cron 不会补跑。解决办法有二一是把时间设在早上刚开机后不久二是让系统在指定时间自动唤醒Linux 用 rtcwakeWindows 任务计划里勾选“唤醒计算机运行此任务”。我选的是前者简单粗暴反正早上开机是必然的。4.2 幂等设计同一天跑十次也只能算一次这个点很多人不重视但它直接关系到脚本的可靠性。设想这么个场景定时任务触发了签到成功了但因为通知环节报错脚本非零退出某些调度器会认为任务失败并重新触发于是同一天跑了第二遍。如果没有幂等设计第二遍可能会因为重复请求被平台判定异常。我的做法是本地记录执行状态import json import os from datetime import date STATE_FILE state/last_run.json def already_done() - bool: if not os.path.exists(STATE_FILE): return False with open(STATE_FILE, r, encodingutf-8) as f: data json.load(f) return data.get(date) date.today().isoformat() def mark_done(extra: dict | None None): os.makedirs(os.path.dirname(STATE_FILE), exist_okTrue) payload {date: date.today().isoformat(), ts: time.time()} if extra: payload.update(extra) with open(STATE_FILE, w, encodingutf-8) as f: json.dump(payload, f, ensure_asciiFalse)主流程开头加一句如果already_done()返回 True 就直接退出。这样无论被触发多少次实际发请求的只有一次。有人会问服务端本身是不是也有防重复有的平台有有的没有。不能依赖服务端——就算它有防重复多打一次请求也是浪费而且某些平台的防重复逻辑是直接给你返回错误码反而污染了日志。本地做一层拦截成本几十行代码收益是彻底安心。这里还有一个进阶考虑状态文件要和实际结果对齐。签到成功才写状态失败不写。这样如果当天第一次失败了手动补跑一次还能生效不会被状态文件挡住。4.3 凭证过期的预警设计长期跑下来唯一必然会发生的事就是凭证过期。Cookie 可能几周失效Token 可能几个月总之会坏。问题是你不能等它坏了才发现因为那时候你可能已经连续好几天没签到而你还以为一切正常。我的做法是每次执行时顺手请求一个用户信息接口检查返回是否正常。如果返回 401 或者提示未登录说明凭证已失效立刻触发告警而不是等到签到失败才发现。def check_login(headers) - bool: try: resp requests.get( https://example.com/api/v1/user/info, headersheaders, timeout10 ) if resp.status_code in (401, 403): return False body resp.json() return body.get(code) 0 except Exception: return False告警渠道看个人习惯。我用的是一个简单的 webhook 推送——脚本往一个自定义地址 POST 一段文本手机就能收到通知。也可以写邮件、写本地文件再配个桌面提醒甚至退化成每天把日志摘要打印到终端。关键是别把告警做得太复杂太复杂的告警你会嫌烦然后关掉那就等于没有。我给自己定的规则是连续两天检测到凭证失效才告警一次避免网络抖动误报告警文案里带上“请重新抓取 Cookie 并更新 config.yaml”这句提示。半年后我重新看到这条消息时这句话能立刻让我知道该干什么不用回头翻代码。5. 常见问题与排查实录5.1 故障速查表对号入座先看现象下面这张表是我这大半年里真实遇到并记录下来的问题按出现频率排序。遇到异常先查表多数情况能直接定位。现象大概率原因处理方式返回 200 但 body 为空User-Agent 或 Referer 不匹配换成抓包时的完整请求头返回 401 / 403Cookie 或 Token 过期重新抓取并更新配置返回“参数错误”Content-Type 与传参方式不匹配json 传参用 json表单用 data 并设头返回“签名错误”时间戳过期或签名算法变了检查本地时间重新逆向签名逻辑提示“已签到”当天重复执行检查幂等逻辑是否生效cron 不执行路径是相对路径 / Python 用了系统路径写绝对路径并 cd 到项目目录脚本无输出直接退出依赖装在别的解释器里确认用的是虚拟环境内的 Python偶发超时网络抖动或服务端限流调大 timeout 和 max_retry加退避日志文件暴涨retention 没配加 rotation 和 retention 参数这张表建议你自己也维护一份每解决一个问题就加一行。三个月后你会发现它的价值远超任何教程——因为教程讲的是通用情况而你自己的表记录的是你这一套环境下的真实故障。5.2 我踩过的三个坑以及事后总结的判断标准坑一以为是代码问题其实是系统时间。有一次脚本突然连续报签名错误我改了半小时签名逻辑最后发现是机器休眠唤醒后系统时间慢了 8 分钟导致时间戳超出了服务端的校验窗口。从那以后我加了一条前置检查执行前先比对本机时间和请求返回头里的 Date偏差超过 60 秒就告警。这个检查花了十行代码但省掉的排查时间不可估量。判断标准凡是涉及时间戳、签名、有效期的问题先把时钟对准再怀疑代码。坑二重试反而把任务搞失败。早期我的重试逻辑没有区分错误类型401 也重试结果连续三次无效请求触发了平台的风控账号被临时限制。教训是重试只对“可能自己恢复的错误”有意义凭证类、参数类错误必须立即终止。现在的代码里401/403 直接抛异常跳出循环一次都不浪费。坑三告警太多等于没有告警。有一段时间我把所有非成功情况都设为告警结果遇到平台每日维护窗口凌晨 2 点到 3 点每天都收到一堆通知一周后我就开始无视它了。后来改成维护时段内失败不告警、单次失败不告警、连续两天凭证失效才告警。判断标准告警的密度要低到你每次看到都会当真否则它只是噪音。5.3 关于合规和自我约束说几句实在话这类脚本本质上是把自己的账号操作自动化用在自己有权访问的账号上是合理的。但有几条线我一直守着请求频率必须克制一次签到就一个请求不要为了测试反复打接口不要试图绕过任何验证机制遇到滑块、验证码就该停手说明这条路不该硬走不要拿别人的凭证跑脚本这是底线问题没有讨论空间。还有一条经验每个平台的使用条款值得花两分钟看一眼。有些平台明确允许接口调用有些不鼓励自动化操作。如果条款里写明了不建议我通常就放弃了——为了每天几十积分去试探边界性价比太低。这个判断不是技术问题是取舍问题每个人自己权衡。脚本本身的技术含量不高真正有价值的是这套“先验证通路、再做分层、最后加观测”的方法。同样的套路我后来用在了好几个地方定时拉取报表、监控某个页面变化、每天自动整理一份数据摘要。签到脚本只是最好上手的练手项目把它的骨架吃透你会发现日常里那些“每天要点几下”的活儿绝大多数都能这么收拾掉。我自己的习惯是每做一个这类小工具都在 README 里写三段话这个工具解决什么问题、坏了怎么修、凭证在哪更新。写的时候觉得啰嗦等半年后重新打开这个目录你会感谢当时的自己。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑