小红书扫码登录的入口
小红书 web 端(www.xiaohongshu.com)登录弹窗默认就是扫码。它的扫码协议特点是:全程走自家 API 网关,且每个请求都带 x-s/x-t 签名(签名逆向见前文"小红书 x-s/x-t 签名参数还原"篇)——这是和别家最大的不同,扫码协议本身不复杂,但签名是前置门槛。
流程拆解
第一步:生成二维码
POST https://edith.xiaohongshu.com/api/sns/web/v1/login/qrcode/create
Headers: x-s, x-t (签名)返回 qr_id 和二维码内容(一个 URL)。qr_id 是轮询凭证。
第二步:轮询状态
POST https://edith.xiaohongshu.com/api/sns/web/v1/login/qrcode/status
Body: {"qr_id": "xxx"}
Headers: x-s, x-t状态码:0 等待扫码,1 已扫码,2 已确认,3 已过期。已扫码状态会返回扫码用户的头像和昵称(供展示"确认登录"界面)。
第三步:确认后换登录态
状态为 2 后,响应或后续激活请求种下登录 cookie:web_session(核心会话)、a1(设备标识,本来就有)、webId 等。
精髓一:签名是前置门槛
小红书扫码协议的每个请求都要 x-s/x-t 签名——这意味着你必须先搞定签名体系,才能谈扫码协议。好在签名方案是通用的(前文已详述):整段抠 webmsxyw 模块到 Node,扫码协议的签名和普通接口签名走同一个函数。
工程结构:
class XHSClient:
def __init__(self, sign_service_url):
self.s = requests.Session()
self.sign_url = sign_service_url # 本地 Node 签名服务
def _signed_post(self, path, body):
sig = requests.post(self.sign_url, json={'url': path, 'data': body}).json()
headers = {'x-s': sig['X-s'], 'x-t': str(sig['X-t']),
'Content-Type': 'application/json'}
return self.s.post('https://edith.xiaohongshu.com' + path,
json=body, headers=headers)
def qr_create(self):
return self._signed_post('/api/sns/web/v1/login/qrcode/create', {})
def qr_status(self, qr_id):
return self._signed_post('/api/sns/web/v1/login/qrcode/status', {'qr_id': qr_id})精髓二:a1 cookie 的连续性
小红书的设备标识 a1 在访问首页时就种下,扫码流程的所有请求必须带同一个 a1:
- 先 GET 首页拿 a1
- 后续 create/status/激活全部复用
- 登录成功后 a1 不变,变的是 web_session
a1 中途变化会导致"设备环境跳变",确认后种的 web_session 直接进高风险名单。
精髓三:登录后的激活动作
拿到 web_session 不算完——小红书的新登录态有"冷启动"特征:
- 先调
https://edith.xiaohongshu.com/api/sns/web/v1/user/selfinfo验证登录态生效 - 访问几个浏览类接口(feed、搜索)养行为
- 再进行业务操作(发布、私信等敏感操作建议登录 24 小时后再上)
新登录态立刻做敏感操作,是账号触发人脸/短信验证的最常见原因。
总结
- 小红书扫码协议简单,但所有请求要过 x-s/x-t 签名
- 流程:qrcode/create → qrcode/status 轮询 → web_session 落地
- a1 设备标识全程一致,登录前后不变
- 新登录态先养后用作,敏感操作等 24 小时
交流微信:run1255