快手网页版扫码登录的Python逆向手记:我是如何‘抓’出那三个关键接口的
快手网页版扫码登录的Python逆向工程实战:从抓包到自动化登录
第一次打开快手网页版扫码登录页面时,我盯着那个黑白相间的二维码陷入了思考——这背后究竟隐藏着怎样的通信流程?作为开发者,我们往往只关心最终能否获取到cookies,却忽略了中间那些精妙的设计。本文将带你以逆向工程的视角,一步步拆解快手扫码登录的全过程,并最终实现Python自动化登录。
1. 逆向工程前的准备工作
逆向分析一个复杂的登录流程,就像侦探破案一样需要合适的工具和方法论。在开始之前,我们需要准备好以下几样"侦探装备":
- 抓包工具:Chrome开发者工具是最基础的选择,但专业工具如Charles或Fiddler能提供更强大的过滤和分析功能
- Python环境:建议使用Python 3.7+版本,并安装以下关键库:
pip install requests Pillow - 测试账号:准备一个专门用于测试的快手账号,避免频繁操作触发风控
提示:在实际操作中,建议使用无痕浏览器窗口进行抓包,避免已有cookies的干扰
逆向工程的核心思路是"观察-假设-验证"。我们需要先完整走一遍人工扫码登录流程,记录下所有网络请求,然后分析其中的关键节点和参数传递逻辑。
2. 扫码登录流程的抓包分析
打开Chrome开发者工具的Network面板,勾选"Preserve log"选项,然后开始扫码登录流程。通过观察,我发现整个登录过程实际上经历了四个关键阶段:
- 二维码生成阶段:浏览器向服务器请求生成登录二维码
- 扫码确认阶段:手机端扫码后确认登录
- 令牌交换阶段:将临时令牌转换为长期有效的认证令牌
- cookies获取阶段:最终获取到维持登录状态的cookies
2.1 二维码生成接口分析
第一个关键接口是二维码生成接口,通过抓包我们找到了这个请求:
POST https://id.kuaishou.com/rest/c/infra/ks/qr/start请求参数非常简单:
{ "sid": "kuaishou.web.cp.api" }服务器返回的数据结构如下:
{ "result": 1, "qrLoginToken": "bX...", # 重要参数,后续请求都需要 "qrLoginSignature": "3l...", # 签名参数 "imageData": "iVBORw0KGgo..." # 二维码图片的base64编码 }这里有几个关键点需要注意:
qrLoginToken是本次登录会话的唯一标识,后续所有请求都需要携带qrLoginSignature是签名参数,用于防止请求被篡改imageData可以直接解码显示为二维码图片
2.2 扫码确认接口分析
当用户用手机扫码后,系统会调用第二个关键接口:
POST https://id.kuaishou.com/rest/c/infra/ks/qr/scanResult请求参数需要携带上一步获取的token和signature:
{ "qrLoginToken": token, "qrLoginSignature": signature }这个接口会返回用户基本信息,但此时还未完成真正的登录认证。
3. 关键跳转链路的破解
通过进一步分析,我发现快手采用了一种"令牌逐步升级"的策略,需要通过三个隐藏接口才能最终获取有效的cookies。这三个接口形成了一个严密的认证链条:
| 接口顺序 | 接口地址 | 输入参数 | 输出参数 |
|---|---|---|---|
| 1 | /qr/acceptResult | qrLoginToken, qrLoginSignature | qrToken |
| 2 | /qr/callback | qrToken | kuaishou.web.cp.api.at |
| 3 | /verifyToken | authToken | 最终cookies |
3.1 第一个跳转接口:获取中间令牌
第一个隐藏接口将短期二维码令牌转换为一个中间令牌:
response = session.post( 'https://id.kuaishou.com/rest/c/infra/ks/qr/acceptResult', data={ "qrLoginToken": token, "qrLoginSignature": signature, "sid": "kuaishou.web.cp.api" }, headers=headers ) qr_token = response.json()['qrToken'] # 获取中间令牌3.2 第二个跳转接口:获取认证令牌
第二个接口将中间令牌升级为认证令牌:
response = session.post( 'https://id.kuaishou.com/pass/kuaishou/login/qr/callback', data={ "qrToken": qr_token, "sid": "kuaishou.web.cp.api" }, headers=headers ) auth_token = response.json()['kuaishou.web.cp.api.at'] # 获取认证令牌3.3 最终验证接口:获取cookies
最后一个接口使用认证令牌完成最终验证:
session.post( 'https://www.kuaishou.com/account/login/api/verifyToken', json={ "authToken": auth_token, "sid": "kuaishou.web.cp.api" }, headers=headers ) # 此时session中已经包含了有效的cookies4. Python自动化登录实现
基于上述分析,我们可以将整个流程封装为一个Python类。以下是核心代码实现:
import base64 import requests from io import BytesIO from PIL import Image from threading import Thread class KuaishouQRLogin: def __init__(self): self.session = requests.Session() self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } def get_qr_code(self): """获取登录二维码""" url = 'https://id.kuaishou.com/rest/c/infra/ks/qr/start' response = self.session.post(url, data={"sid": "kuaishou.web.cp.api"}, headers=self.headers) data = response.json() # 显示二维码 qr_image = base64.b64decode(data['imageData']) Thread(target=lambda: Image.open(BytesIO(qr_image)).show()).start() return data['qrLoginToken'], data['qrLoginSignature'] def wait_for_scan(self, token, signature): """等待用户扫码确认""" url = 'https://id.kuaishou.com/rest/c/infra/ks/qr/scanResult' while True: response = self.session.post(url, data={ "qrLoginToken": token, "qrLoginSignature": signature }, headers=self.headers) result = response.json() if result['result'] == 1: # 扫码成功 return True elif result['result'] == 707: # 二维码过期 raise Exception("二维码已过期,请重新获取") def complete_login(self, token, signature): """完成登录流程""" # 第一步:获取中间令牌 response = self.session.post( 'https://id.kuaishou.com/rest/c/infra/ks/qr/acceptResult', data={ "qrLoginToken": token, "qrLoginSignature": signature, "sid": "kuaishou.web.cp.api" }, headers=self.headers ) qr_token = response.json()['qrToken'] # 第二步:获取认证令牌 response = self.session.post( 'https://id.kuaishou.com/pass/kuaishou/login/qr/callback', data={ "qrToken": qr_token, "sid": "kuaishou.web.cp.api" }, headers=self.headers ) auth_token = response.json()['kuaishou.web.cp.api.at'] # 第三步:验证令牌获取cookies self.session.post( 'https://www.kuaishou.com/account/login/api/verifyToken', json={ "authToken": auth_token, "sid": "kuaishou.web.cp.api" }, headers=self.headers ) return self.session使用这个类可以非常简单地实现扫码登录:
login = KuaishouQRLogin() token, signature = login.get_qr_code() print("请扫描弹出的二维码...") if login.wait_for_scan(token, signature): session = login.complete_login(token, signature) print("登录成功!Cookies:", session.cookies.get_dict())在实际项目中,我们可以将会话对象保存下来,后续直接使用这些cookies来访问需要登录的接口。
5. 常见问题与调试技巧
在实现过程中,可能会遇到各种问题。以下是一些常见问题及解决方案:
二维码过期问题:快手的二维码有效期约为3分钟,超时需要重新获取。可以在代码中添加超时检测逻辑。
import time start_time = time.time() while time.time() - start_time < 180: # 3分钟超时 # 检测扫码状态 pass else: raise Exception("二维码已过期")请求频率限制:快手对频繁请求会有频率限制,建议在请求之间添加适当的延迟:
import time time.sleep(1) # 每次请求间隔1秒签名验证失败:确保每次请求都携带正确的
qrLoginSignature,且不要修改其值。cookies持久化:可以使用
requests.Session的cookies管理功能,将会话保存到文件:import http.cookiejar as cookielib session.cookies = cookielib.LWPCookieJar('cookies.txt') # 登录成功后保存 session.cookies.save() # 下次启动时加载 session.cookies.load(ignore_discard=True)
逆向工程最有趣的部分就是不断遇到问题并解决问题的过程。记得在调试时保持耐心,仔细对比正常登录和自动化登录的请求差异,往往能快速定位问题所在。
