# 抖音 Cookie 一键提取 扫码登录抖音后,自动导出 JSON 格式 Cookie。 ## 原理 采用 **HTTP SSO 接口**(无需 Playwright / 浏览器): 1. 注册 `ttwid` 设备标识 2. 调用 `passport/web/get_qrcode` 或 `sso.douyin.com/get_qrcode` 获取二维码 3. 轮询 `check_qrconnect` 检测扫码状态 4. 确认后跟随 `redirect_url` 完成会话,导出 Cookie > 若服务器 IP 被抖音风控(错误码 4031),请配置**住宅代理 API**。 ## 为什么不使用 Playwright 本项目早期版本基于 **Playwright + Headless Chromium** 实现扫码登录,但在实际使用与维护中暴露出大量问题。经过多轮修复后,仍无法稳定支撑「一键提取 Cookie」这一核心目标,因此彻底移除 Playwright,改为直接调用抖音 Passport / SSO 的 HTTP 接口。 ### 1. 登录流程依赖脆弱的 DOM 自动化 Playwright 方案需要模拟用户在网页上的每一步操作: - 打开 `douyin.com` 并点击「登录」 - 切换到「扫码登录」标签 - 从页面 `` / `` 中提取二维码 - 扫码后识别「身份验证」弹窗,再自动点击「短信验证码」「发送验证码」等按钮 - 跨 iframe、Shadow DOM 查找输入框并填入验证码 抖音前端 UI 频繁改版,按钮文案、CSS 类名、弹窗结构随时可能变化。任何一次改版都会导致: - 找不到登录入口或二维码 - 误判登录状态(例如把「等待确认」当成「需要验证」) - 自动填验证码失败,用户无法完成二次验证 这类问题无法通过局部修补根治,因为**本质是在用浏览器自动化对抗一个持续变化的前端**。 ### 2. Headless 模式与风控冲突 无头浏览器(`headless: true`)极易触发抖音反爬与风控策略: - `navigator.webdriver` 等特征可被检测 - 滑块、图形验证码在 headless 下几乎无法完成 - 扫码后常出现二次验证(短信 / 密码 / 滑块),自动化脚本无法可靠处理 改为「可见浏览器 + 用户手动验证」虽能缓解,但又带来新问题:必须在服务器上弹出 GUI 窗口(Linux 无桌面环境不可用),且仍依赖 DOM 点击逻辑,**并没有降低维护成本**。 ### 3. 二次验证是最大不稳定因素 真实场景中,用户手机确认登录后,网页端仍可能要求: - 接收短信验证码 - 验证登录密码 - 完成滑块验证 旧方案试图用 Playwright 自动点击、自动填码、自动提交,实际效果很差: - 验证码输入框分布在不同 iframe 中,选择器经常失效 - 轮询逻辑与用户手动操作互相覆盖(例如自动点击「接收短信」与用户选择密码验证冲突) - 提交按钮文案不统一(「确定」「提交」「验证」等),跨 frame 点击失败率高 **Cookie 提取工具不应承担「代替用户在网页里完成风控验证」的职责**;这类步骤更适合由用户在 App 或官方页面自行完成,服务端只负责检测登录结果。 ### 4. 架构复杂、难以维护 Playwright 版本将全部逻辑塞在单文件 `app.py`(约 1200 行),并引入额外复杂度: | 问题 | 说明 | |------|------| | Flask 与 asyncio 桥接 | 需要自定义 `run_async_sync()` 和全局 event loop,易出现协程/线程竞态 | | 浏览器生命周期 | 登录成功即关闭浏览器,调试困难;异常时容易残留僵尸进程 | | 单全局会话 | `LoginSession` + 锁,同一时刻仅支持一个扫码任务 | | 部署依赖重 | 除 Python 外还需 `playwright install chromium`(数百 MB) | | 二维码提取不可靠 | 常退化为「整页截图」,用户扫到的并非标准 QR | 模块化拆分后,浏览器相关代码仍是最难测试、最难 Mock 的部分,**投入产出比极低**。 ### 5. 与项目目标不匹配 本项目的核心目标是: > **扫码登录成功后,导出网页端可用的 Cookie JSON** 完成这一目标,并不需要渲染完整页面,只需要走通抖音官方的 **扫码 SSO 协议**: ``` get_qrcode → 展示二维码 → check_qrconnect 轮询 → redirect_url 换票 → 拿到 Cookie ``` 这是一条稳定的 HTTP 状态机,与页面 DOM 无关。抖音网页版、创作者平台、早期开源项目均采用同一套 `sso.douyin.com` 接口,**协议层比 UI 层可靠得多**。 ### 6. HTTP SSO 方案的优势与代价 **优势:** - 无需安装 Chromium,部署仅需 `pip install -r requirements.txt` - 无 GUI / 无头浏览器依赖,适合 Linux 服务器与 Docker - 逻辑清晰:`backend/sso/qr_login.py` 独立维护,易于单测 - 资源占用低:一个 `requests.Session` 即可,内存占用从数百 MB 降至数 MB - 前端只展示二维码与轮询状态,不再耦合浏览器截图、验证表单等复杂 UI **代价(需知晓):** - 依赖抖音 SSO 接口,若接口参数变更需跟进适配 - 部分机房 IP 会被风控(错误码 `4031`),需配置住宅代理 API - 若账号在 SSO 层仍要求额外验证,需用户在手机端完成;HTTP 层无法代替网页内操作 综合权衡:**HTTP SSO 更轻、更稳、更易部署**,更符合「一键提取 Cookie」的产品定位;Playwright 适合需要完整浏览器行为的场景(如自动化发帖、复杂交互),**不适合作为 Cookie 提取的主路径**。 ### 与其他方案对比 | 方案 | 说明 | 本项目 | |------|------|--------| | HTTP SSO | 直接调用 passport / SSO 接口,轻量、可部署 | **当前采用** | | Playwright | 模拟真实浏览器,重、易受 UI 改版影响 | **已移除**(见上文) | | 开放平台 OAuth | 需注册应用,返回 `access_token` 而非网页 Cookie | 不适用 | | 读取本机浏览器 Cookie | 需用户事先在 Chrome 登录,非远程一键扫码 | 未采用 | ## 环境要求 - Python 3.9+ - Node.js 18+(仅构建前端时需要) ## 安装 ```bash pip install -r requirements.txt cd frontend npm install npm run build ``` ## 运行 ```bash py -3 app.py ``` 访问:http://127.0.0.1:5001 ## 环境变量 | 变量 | 默认值 | 说明 | |------|--------|------| | `PORT` | `5001` | 服务端口 | | `LOGIN_TIMEOUT` | `600` | 登录超时(秒) | | `QR_POLL_INTERVAL` | `2` | 建议前端轮询间隔 | | `DOUYIN_AID` | `6383` | 抖音 Web 应用 ID | | `DOUYIN_SERVICE` | `https://www.douyin.com` | SSO service 参数 | ## API | 接口 | 说明 | |------|------| | `POST /api/start_qr` | 获取二维码(HTTP SSO) | | `GET /api/check_login` | 轮询扫码/登录状态 | | `POST /api/reset` | 重置会话 | | `GET /api/debug` | SSO 轮询调试信息 | | `POST /api/test_proxy` | 测试代理 | ## 项目结构 ``` app.py backend/ sso/qr_login.py # HTTP 扫码登录核心 cookies.py # Cookie 检测 routes/ # API + SPA frontend/ # Vue 3 前端 ```