网上流传的百度网盘签到脚本几乎都用的同一套接口,拿来实测直接报 400。排查发现这批接口已经被百度下线了一年左右,现行可用的参数组合完全不同。本文记录从排查到复现的完整过程,以及防封号的请求编排设计。
背景 百度网盘会员有一套成长值体系,每天可以白嫖两笔成长值:
签到 :每日签到领成长值(连续签到有递增奖励)
答题 :每日一题,接口直接返回答案,提交就给分
老版本脚本(各大签到聚合项目抄来抄去的版本)都是这三个请求:
1 2 3 GET https://pan.baidu.com/rest/2.0/membership/level?app_id=250528&web=5&method=signin GET https://pan.baidu.com/act/v2/membergrowv2/getdailyquestion?app_id=250528&web=5 GET https://pan.baidu.com/act/v2/membergrowv2/answerquestion?app_id=250528&web=5&ask_id=xxx&answer=x
带 BDUSS cookie 实测第一个请求直接挂:
1 2 HTTP 400 {"error_code":100002,"error_msg":"Param error","request_id":"..."}
排查过程 先排除 cookie 问题 第一步是分清”cookie 失效”还是”接口变了”。拿同一个 cookie 打两个不同端点:
端点
参数
结果
/rest/2.0/membership/user?method=query
web=5
200,返回真实会员数据(等级 9、成长值 69925、年度 SVIP)
/rest/2.0/membership/level?method=query
web=5
400 + 100002 Param error
/rest/2.0/membership/level?method=signin
web=5
400 + 100002 Param error
结论很清晰:cookie 是好的(user 接口正常吐数据),是 membership/level 这个端点的参数契约 变了。再换成无 cookie 请求 level 端点,同样 400——它压根没走到鉴权那步,参数校验就拒了。
换 web=1、POST、加 clienttype=0 等参数变体全部无效,这条老路彻底死了。
GitHub 考古 去翻还在维护的签到项目,Sitoi/dailycheckin 的 issue #534(2025-10)就在报”百度网盘签到无反应”,至今没修——全量代码搜索发现几十个项目还在抄这套死掉的接口。
唯一有价值的是 xvkong233/qlscript 的 baidu_netdisk.py:作者在 2026-10 用 App 13.34.3 真机抓包重做了整套实现,注释里连反欺诈指纹、风控错误码表都写全了。从中拿到关键结论:
1 2 QUERY_PARAMS = {"app_id" : "250528" , "web" : "1" , "clienttype" : "5" , "clientinfo" : "wap" }
老接口 web=5 被服务端下线,现行可用的是 web=1&clienttype=5&clientinfo=wap(wap 网页端通道)。
现行接口 以下全部实测验证过(2026-10-28)。请求头只需要三条:
1 2 3 4 5 6 7 HEADERS = { "User-Agent" : "Mozilla/5.0 (Linux; Android 16; Xiaomi 17 Pro Max) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Mobile Safari/537.36" , "Referer" : "https://pan.baidu.com/wap/svip/growth/task" , "Accept" : "application/json, text/plain, */*" , "X-Requested-With" : "XMLHttpRequest" , }
1. 签到状态查询(只读) 1 GET /rest/2.0/membership/level?app_id=250528&web=1&clienttype=5&clientinfo=wap&method=signinlist
1 2 3 4 5 6 7 8 9 10 11 12 13 { "data" : { "list" : [ { "id" : 1 , "is_signin" : false , "points" : 2 } , { "id" : 2 , "is_signin" : false , "points" : 4 } , { "id" : 7 , "is_signin" : false , "points" : 14 } ] , "signed_cnt" : 0 , "today_signed" : false } , "error_code" : 0 , "error_msg" : "" }
today_signed 是当日是否已签的权威判定,signed_cnt 是连续签到天数,list 是本月签到日历(id 即第几天,points 是当日可领成长值)。
2. 签到(写) 1 GET /rest/2.0/membership/level?app_id=250528&web=1&clienttype=5&clientinfo=wap&method=signin
1 2 3 4 5 { "error_code" : 0 , "error_msg" : "" , "result" : { "points" : 2 } }
签完再查一次 signinlist,日历第一天 is_signin 变 true、signed_cnt 变 1。重复请求时返回 error_code: 421001(error_msg 含 repeat)。
3. 每日答题(这组接口用老参数 web=5 反而活着) 取题:
1 GET /act/v2/membergrowv2/getdailyquestion?app_id=250528&web=5
1 2 3 4 5 6 7 8 9 10 { "data" : { "answer" : 1 , "answer_status" : 1 , "ask_id" : 1237 , "question" : "做成长值任务是否可以加速提升会员等级?" , "score" : 3 } , "errno" : 0 }
answer 字段直接就是正确答案的序号——出题接口就是答案接口。answer_status: 1 表示今天已答过。
提交(把取到的 ask_id 和 answer 原样带回去):
1 GET /act/v2/membergrowv2/answerquestion?app_id=250528&web=5&ask_id=1237&answer=1
成功响应含 data.score;重复提交返回 errno: 9502。
4. 会员信息(顺带验证签到收益) 1 GET /rest/2.0/membership/user?app_id=250528&web=5&method=query
成长值在 level_info 里:current_level(等级)、current_value(成长值)。实测签到 +2 后这里从 69925 变 69927,数字对得上。
关键细节 cookie 放会话 jar,不要写死 Cookie 头 把 cookie 字符串塞进 requests.Session().cookies(domain 填 .baidu.com),而不是拼到请求头里。原因:请求过程中服务端会下发 BAIDUID、PANWEB 等 Set-Cookie,会话 jar 会自动带上后续请求,写死 Cookie 头这些就全丢了,会话上下文不完整,风控侧看起来就是异常客户端。
cookie 要求很低:成长值/答题/信息查询这条链路只要 BDUSS 一个字段 就够(实测无 STOKEN 全通)。整串浏览器 cookie 复制进来也行。
判定位汇总
场景
判定
签到状态
data.today_signed
签到成功
error_code == 0,积分在 result.points
重复签到
error_code == 421001 或 error_msg 含 repeat/已签
cookie 失效
error_code == -6 或 error_msg 含”登录”
取题成功
errno == 0,答案在 data.answer/data.ask_id
已答过
data.answer_status == 1(取题时)或 errno == 9502(提交时)
答题成功
响应含 data.score
Python 实现 自包含版本,cookie 作为参数传入,结果直接打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 import reimport timeimport requestsAPI_BASE = "https://pan.baidu.com" SIGNIN_PARAMS = {"app_id" : "250528" , "web" : "1" , "clienttype" : "5" , "clientinfo" : "wap" } GROWTH_PARAMS = {"app_id" : "250528" , "web" : "5" } UA = ("Mozilla/5.0 (Linux; Android 16; Xiaomi 17 Pro Max) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Mobile Safari/537.36" ) def build_session (cookie_str: str ) -> requests.Session: """cookie 放入会话 jar 而非写死 Cookie 头, 保证服务端运行中下发的 cookie(BAIDUID/PANWEB 等)能在后续请求回传""" session = requests.Session() session.headers.update({ "User-Agent" : UA, "Referer" : "https://pan.baidu.com/wap/svip/growth/task" , "Accept" : "application/json, text/plain, */*" , "X-Requested-With" : "XMLHttpRequest" , }) for part in re.split(r"[;\n]+\s*" , cookie_str): if "=" not in part: continue name, value = part.split("=" , 1 ) if name.strip(): session.cookies.set (name.strip(), value.strip(), domain=".baidu.com" , path="/" ) return session def pan_get_json (session, path, params ): resp = session.get(API_BASE + path, params=params, timeout=15 ) if resp.status_code != 200 : raise RuntimeError(f"HTTP {resp.status_code} " ) return resp.json() def growth_signin (session ) -> str : """成长值签到:先读 signinlist 判断今日是否已签(避免重复写请求触发风控), 未签才调用 signin""" data = pan_get_json(session, "/rest/2.0/membership/level" , dict (SIGNIN_PARAMS, method="signinlist" )) if data.get("error_code" ) != 0 : return f"签到状态查询失败: {data.get('error_msg' ) or data} " inner = data.get("data" ) or {} if inner.get("today_signed" ): cnt = inner.get("signed_cnt" ) return f"今日已签到, 已连续签到 {cnt} 天" if cnt else "今日已签到" time.sleep(2 ) result = pan_get_json(session, "/rest/2.0/membership/level" , dict (SIGNIN_PARAMS, method="signin" )) code = result.get("error_code" ) if code == 0 : points = (result.get("result" ) or {}).get("points" ) cnt = inner.get("signed_cnt" ) msg = f"签到成功, 获得成长值: {points if points is not None else '未知' } " if cnt: msg += f", 已连续签到 {cnt + 1 } 天" return msg error_msg = str (result.get("error_msg" ) or "" ) if code == 421001 or "repeat" in error_msg.lower() or "已签" in error_msg: return "今日已签到(重复请求)" if code == -6 or "登录" in error_msg: return "签到失败: cookie(BDUSS) 已失效" return f"签到失败: {error_msg} (error_code={code} )" def answer_daily_question (session ) -> str : """每日答题:出题接口直接返回答案,照抄提交即可; answer_status=1 表示今天已答过,直接短路,不再发提交请求""" data = pan_get_json(session, "/act/v2/membergrowv2/getdailyquestion" , dict (GROWTH_PARAMS)) if data.get("errno" ) not in (0 , None ): return f"获取日常问题失败: {data.get('show_msg' ) or data.get('errno' )} " inner = data.get("data" ) or {} ask_id, answer = inner.get("ask_id" ), inner.get("answer" ) if not ask_id: return "未找到日常问题或答案" if inner.get("answer_status" ) == 1 : return "今日已答题" if answer is None : return "今日题目暂无答案, 跳过答题" time.sleep(2 ) result = pan_get_json(session, "/act/v2/membergrowv2/answerquestion" , dict (GROWTH_PARAMS, ask_id=ask_id, answer=answer)) if result.get("errno" ) == 9502 : return "今日已答题" score = (result.get("data" ) or {}).get("score" ) if score is not None : return f"答题成功, 获得成长值: {score} " show_msg = (result.get("data" ) or {}).get("show_msg" ) or result.get("show_msg" ) or "" return f"答题未生效: {show_msg or result} " def get_user_info (session ) -> str : """查询会员等级与成长值""" data = pan_get_json(session, "/rest/2.0/membership/user" , dict (GROWTH_PARAMS, method="query" )) info = data.get("level_info" ) or {} level, value = info.get("current_level" ), info.get("current_value" ) if level is None or value is None : return "获取用户信息失败: 响应中未找到等级/成长值字段" return f"当前会员等级: {level} , 成长值: {value} " def baidupan_checkin (cookie_str: str ) -> str : """流程: 成长值签到 → 每日答题 → 查询会员等级与成长值""" session = build_session(cookie_str) results = [growth_signin(session)] time.sleep(3 ) results.append(answer_daily_question(session)) time.sleep(1 ) results.append(get_user_info(session)) return "\n" .join(results) if __name__ == "__main__" : cookie = "BDUSS=xxxxxxxx" print (baidupan_checkin(cookie))
运行效果 1 2 3 4 $ python baidupan_checkin.py 签到成功, 获得成长值: 2, 已连续签到 1 天 今日已答题 当前会员等级: 9, 成长值: 69927
第二次运行(幂等路径,只有只读请求):
1 2 3 4 $ python baidupan_checkin.py 今日已签到, 已连续签到 1 天 今日已答题 当前会员等级: 9, 成长值: 69927
防封号设计 签到类自动化最大的风险不是接口,是请求行为把自己送进风控。这套实现的约束:
先查后写 :signinlist 是只读查询,先看 today_signed,已签就绝不再发 signin 写请求。写请求每天最多出现一次
幂等短路 :答题先看 answer_status,已答过不发提交请求;服务端兜底的 421001/9502 也按”已签/已答”归为幂等成功,不重试
请求量恒定 :每天全流程 35 个请求(3 只读 + 最多 2 写),间隔 23 秒,不并发、不重试轰炸
浏览器同源 :UA/Referer 对齐 wap 成长值任务页,cookie 走会话 jar 保持会话连续性
即便脚本一天跑多次,重复执行时全部走只读路径,行为等价于多刷新了几次页面。
边界:没做的事 百度任务体系还有几条通道,逆向结论记录在这里但没有自动化 :
任务中心签到 (/coins/taskcenter/signin,金币/任务系统 H5):必须带 z/channel/offlinepackage 等完整客户端参数,其中 z 是 sofire 反欺诈指纹(com.baidu.sofire.ac.FH.gz),由设备+登录态在本机算出,不能跨账号/跨设备复放 ,拿别人的抓包值只会收到 errno=2。要自动化这条通道得上 Frida 在真机现场取指纹,成本完全不成比例
PC 积分签到 (/coins/pc/signin):需要伪装 PC 客户端 Electron UA,奖励是金币不是成长值
补签 (/coins/taskcenter/suppsignin):与任务中心共用设备链和 z 指纹,同上不碰
成长值签到+答题这条 BDUSS 就能走的通道,是整套体系里唯一不需要对抗设备指纹的,性价比最高。
Reference