douyin-downloader 的 Playwright Cookie 抓取工具:tools/cookie_fetcher.py 全解析
2026/9/15 18:33:41 网站建设 项目流程

douyin-downloader 的 Playwright Cookie 抓取工具:tools/cookie_fetcher.py 全解析

【免费下载链接】douyin-downloaderA practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音批量下载工具,去水印,支持视频、图集、合集、音乐(原声)。项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader

抖音下载器(douyin-downloader)的大部分接口都需要携带登录态 Cookie 才能访问完整数据,而手动从浏览器开发者工具复制 Cookie 既繁琐又易出错。本项目在 tools/ 目录中提供了一个独立、可复用的解决方案:基于 Playwright 启动真实浏览器、引导用户手动登录、再自动导出并清洗 Cookie 的工具cookie_fetcher.py。本文以 tools/AGENTS.md 为骨架,结合源码与测试,完整讲解该工具的定位、命令行用法、底层抓取原理、多源 msToken 提取、Cookie 筛选清洗,以及它与项目自动重登机制(cli/login_flow.py)和配置系统(ConfigLoader)的协作方式。读完本文,你将能独立完成浏览器登录态抓取、将 Cookie 写入配置并排查相关故障。

工具定位:独立于核心下载管线的用户侧工具

tools/AGENTS.md 开篇即明确了tools目录的定位:独立工具脚本目录(Standalone utility scripts),当前只包含一个核心工具——基于 Playwright 的浏览器 Cookie 抓取器cookie_fetcher.py。它被明确标注为"面向用户的工具,不属于核心下载管线"(This is a user-facing utility, not part of the core download pipeline),这意味着:

  • 它不参与视频下载、图集解析、音频提取等核心业务;
  • 它只负责"获取合法登录态"这一前置步骤;
  • 它依赖可选的[browser]扩展依赖,非核心依赖,用户按需安装。

从源码结构看,tools/cookie_fetcher.py对外提供两条使用入口:

  1. 命令行入口main()parse_args()asyncio.run(capture_cookies(args))(tools/cookie_fetcher.py);
  2. 程序化入口fetch_cookies(...)参数化封装,供 CLI 自动重登流程等调用方直接调用,无需伪造argparse.Namespace(tools/cookie_fetcher.py)。

这一"双入口"设计在 docs/superpowers/plans/2026-06-25-auto-relogin.md 中有明确记载:fetch_cookies封装是"加法式"修改,且tools/cookie_fetcher.py在 CLI 仓库与桌面仓库之间保持字节级一致(byte-identical)。

环境准备:安装 Playwright 可选依赖

由于 Playwright 是可选依赖,需要显式安装。在 pyproject.toml 的[project.optional-dependencies]中定义:

[project.optional-dependencies] browser = [ "playwright>=1.40.0", ]

安装并下载浏览器内核:

pip install "douyin-downloader[browser]" playwright install chromium

源码在 Playwright 未安装时也会给出防御性提示(tools/cookie_fetcher.py):

[ERROR] Playwright is not installed. Run `pip install playwright` first.

命令行用法与完整参数说明

工具的默认目标页面是抖音首页https://www.douyin.com/,默认输出文件为config/cookies.json。README 中给出了直接运行方式(见 README.md 与 README.zh-CN.md):

python -m tools.cookie_fetcher --config config.yml

parse_args(tools/cookie_fetcher.py)支持的参数如下表:

参数类型默认值说明
--urlstrhttps://www.douyin.com/要打开的登录页面地址
--browser枚举chromiumPlaywright 浏览器内核,可选chromium/firefox/webkit
--headless开关关闭无头模式运行浏览器(不推荐,因为需要手动登录)
--outputPathconfig/cookies.json抓取到的 Cookie 写入的 JSON 文件路径
--configPath可选:将抓取的 Cookie 一并回写进该 YAML 配置文件的cookies字段
--include-all开关关闭存储 douyin.com 的全部 Cookie,而非推荐的子集

几个实用的调用示例:

# 默认流程:打开 chromium 登录抖音,手动登录后回车,输出到 config/cookies.json python -m tools.cookie_fetcher # 指定浏览器内核与自定义输出文件 python -m tools.cookie_fetcher --browser firefox --output /tmp/my_cookies.json # 登录完成后同时回写 config.yml 配置 python -m tools.cookie_fetcher --config config.yml # 完整导出所有 Cookie(不推荐,默认子集已足够) python -m tools.cookie_fetcher --include-all

核心工作流:capture_cookies 的五步流程

capture_cookies(tools/cookie_fetcher.py)是整个工具的骨架,分为五个阶段:

① 启动浏览器与页面:通过getattr(p, args.browser)动态选择浏览器引擎,以headless=args.headless启动,创建新的浏览器上下文与页面。同时注册page.on("request", ...)请求监听器,实时收集两类信息:请求头中的cookie字段、以及 URL 查询参数与文本中的msToken——这是后续 msToken 兜底提取的数据来源。

② 等待手动登录:打印提示信息后,调用wait_for_login_confirmation(page, args.url)等待用户在浏览器中完成登录并回到终端按 Enter(详见下文"登录确认等待")。

③ 收集 Cookie:登录确认后,通过context.storage_state()获取整个浏览器上下文的存储状态,只保留domaindouyin.com结尾的 Cookie,组装成{name: value}字典,并立即调用sanitize_cookies清洗。

④ 兜底提取 msToken:调用try_extract_ms_token从多个来源尝试获取 msToken(详见下文),若成功且当前 Cookie 中缺失,则补入。

⑤ 筛选、落盘与回写:根据--include-all决定是否全量保留,否则调用filter_cookies筛选推荐子集;再次清洗后写入--output指定的 JSON 文件;打印缺失的必要键警告(REQUIRED_KEYS未覆盖时);若指定了--config,则调用update_config将 Cookie 回写进 YAML。

登录确认等待:后台导航与终端回车协同

wait_for_login_confirmation(tools/cookie_fetcher.py)解决了一个真实的并发问题:如果同步等待页面导航完成再阻塞读终端输入,页面加载缓慢时终端将长时间无法响应 Enter。实现方案是:

  • asyncio.create_taskgoto_with_fallback放入后台任务;
  • await asyncio.sleep(0)确保导航任务至少进入第一个await点(否则用户立刻回车可能导致goto尚未被调度就被 cancel,漏掉页面加载);
  • 通过asyncio.to_thread(input_func)将阻塞式input()丢到线程池,等待用户回车;
  • 用户回车后,若导航任务未完成则 cancel,并吞掉CancelledError,正常继续流程。

配套的goto_with_fallback(tools/cookie_fetcher.py)实现了等待策略降级:默认以wait_until="networkidle"、超时 300 秒加载页面;部分站点会持续发送请求导致 networkidle 永远达不到,超时后自动降级为wait_until="domcontentloaded"再试一次。测试 tests/test_cookie_fetcher.py 用FakePage/SlowPage模拟了全部四条分支:

  • networkidle 超时 → 降级 domcontentloaded 成功;
  • 非超时异常(如 RuntimeError)→ 直接抛出;
  • TargetClosedError(目标页面/上下文/浏览器被关闭)→ 返回"target_closed"继续流程;
  • 两次都超时 → 返回"timeout"继续流程。

msToken 多源提取:从请求、存储到正则兜底

msToken 是抖音风控体系中的重要参数,单靠storage_state()不一定能拿到(它常出现在 URL 查询串、请求头或 localStorage 中)。try_extract_ms_token(tools/cookie_fetcher.py)按优先级依次探测六个来源:

  1. 已有 Cookie 中的msToken(直接返回);
  2. 反向遍历observed_mstokens(请求监听器收集的 URL 查询参数 msToken 与文本提取结果);
  3. 反向遍历observed_cookie_headers(请求头中的 Cookie,用parse_cookie_header解析,失败则正则提取);
  4. document.cookie(通过page.evaluate执行 JS 读取);
  5. localStorage中键名包含mstoken的值;
  6. sessionStorage中键名包含mstoken的值。

兜底的文本提取函数extract_ms_token_from_text(tools/cookie_fetcher.py)内置三条正则,覆盖三种格式:

  • Cookie 风格:msToken=xxx(形如;,&、空白、引号等分隔);
  • JSON 风格:"msToken": "xxx"
  • 单引号 JSON 风格:'msToken': 'xxx'

测试 tests/test_cookie_fetcher.py 验证了 URL 查询串与 JSON 格式的提取,例如从https://www.douyin.com/?foo=1&msToken=query-token&bar=2中提取出query-token

Cookie 筛选与净化:只保留风控与登录必需的键

抖音会设置大量 Cookie,但下载器只需要其中一小部分。filter_cookies(tools/cookie_fetcher.py)在默认(非--include-all)模式下按三层规则筛选:

① 必要键(REQUIRED_KEYS)——缺失会触发警告:

REQUIRED_KEYS = {"msToken", "ttwid", "odin_tt", "passport_csrf_token"}

② 建议键(SUGGESTED_KEYS)——在必要键基础上补充会话三件套:

SUGGESTED_KEYS = REQUIRED_KEYS | {"sid_guard", "sessionid", "sid_tt"}

③ 辅助键(DEFAULT_AUXILIARY_KEYS + 前缀匹配)——WAF、指纹与安全相关键:

DEFAULT_AUXILIARY_KEYS = { "_waftokenid", "s_v_web_id", "__ac_nonce", "__ac_signature", "UIFID", "UIFID_TEMP", "d_ticket", "x-web-secsdk-uid", "__security_server_data_status", } DEFAULT_AUXILIARY_PREFIXES = ("__security_mc_", "bd_ticket_guard_", "_bd_ticket_crypt_")

注意:筛选结果为空时会回退返回全部 Cookie,避免把工具用死。

在写出 JSON 前,所有 Cookie 还会经过sanitize_cookies(utils/cookie_utils.py)净化:键必须是非空字符串、字符在 ASCII 33–126 范围内、且不含 RFC6265 非法分隔符(()<>@,;:\"/[]?={} \t\r\n,见INVALID_COOKIE_NAME_CHARS),空值统一转为空字符串。测试 tests/test_cookie_fetcher.py 验证了 WAF/指纹键被保留、无关键(如random_cookie)被过滤。

配置回写与消费:Cookie 如何进入下载流程

抓取到的 Cookie 通过两条路径进入下载器:

路径一:--config直接回写 YAMLupdate_config(tools/cookie_fetcher.py)读取已有 YAML(用yaml.safe_load),设置existing["cookies"] = cookies后以allow_unicode=Truesort_keys=False写回:

cookies: msToken: xxx ttwid: xxx sessionid: xxx ...

路径二:JSON 文件被 ConfigLoader 自动发现。默认输出config/cookies.json会被 config/config_loader.py 的get_cookies()消费:当配置中cookiescookie字段为字符串"auto"时,触发_load_auto_cookies(),依次在配置目录、配置目录父级、当前工作目录下的config/cookies.json.cookies.json中查找并读取(config/config_loader.py)。也支持将 Cookie 字符串直接写在配置的cookie字段(_parse_cookie_stringparse_cookie_header解析)。这套消费逻辑与抓取工具共用同一套sanitize_cookies清洗函数。

与自动重登机制的集成:登录失效后的自助恢复

这是cookie_fetcher.py在项目中最核心的联动场景。当接口请求返回登录失效时,core/api_client.py抛出LoginRequiredError,CLI 顶层捕获后触发重登(详见 docs/superpowers/specs/2026-06-25-auto-relogin-design.md)。

cli/login_flow.py 是 CLI 专属的交互式重登编排层:

  • can_interactive_login(*, serve=False)(cli/login_flow.py):只有stdin是 TTY 且非--serve服务模式时才返回 True,避免在 CI/无交互环境下卡死;
  • interactive_relogin(cookies_path=...)(cli/login_flow.py):调用fetch_cookies(output=cookies_path)启动浏览器引导登录,返回码非 0 或结果中缺少sessionid均视为失败并返回 None(带中文错误提示),成功则返回清洗后的 Cookie 字典。

cli/main.py 的重试逻辑为:首次请求失败且允许交互登录时,执行interactive_relogin()刷新 Cookie 并重试一次;非交互场景则提示用户手动运行python tools/cookie_fetcher.py或手动更新config/cookies.json。相关行为由 tests/test_login_flow.py 与 tests/test_relogin_retry.py 覆盖验证。

测试策略:不启动真实浏览器的确定性验证

tools/AGENTS.md 明确要求:测试必须 mock Playwright,禁止启动真实浏览器Tests mock Playwright — do not launch real browsers)。

tests/test_cookie_fetcher.py 通过两个轻量假页面对象实现这一约束:

  • FakePage:按预设队列依次返回goto结果,可注入异常,断言每次调用的url/wait_until/timeout
  • SlowPagegotoawait asyncio.sleep(60),用于验证用户在导航完成前回车时任务被正确 cancel(elapsed < 1秒且cancelled is True)。

pytest.ini 配置 开启了asyncio_mode = "auto",因此这些异步函数可直接以普通def测试用例配合asyncio.run运行,无需额外装饰器。配套测试还包括 tests/test_cookie_fetcher_fetch.py(验证fetch_cookies正确构造 Namespace 并委托给capture_cookies)以及上文提到的登录流与重试测试。

内部与外部依赖一览

tools/AGENTS.md 的 Dependencies 章节给出的依赖关系,结合源码可确认如下:

内部依赖

依赖用途源码位置
utils.cookie_utils.parse_cookie_header解析 Cookie 请求头字符串utils/cookie_utils.py
utils.cookie_utils.sanitize_cookies按 RFC6265 规则净化 Cookie 键值utils/cookie_utils.py
yaml配置回写时读写 YAMLtools/cookie_fetcher.py

外部依赖

依赖用途说明
playwright浏览器自动化(异步 API)可选依赖[browser],需playwright install chromium
Python 标准库argparse/asyncio/json/re/urllib.parse/pathlib参数解析、并发、序列化与文本提取

实践要点与注意事项

  1. 手动登录必须是有头模式--headless不推荐,登录过程中需要验证码/扫码等交互,无头环境通常无法完成;
  2. Enter 时机:在浏览器中看到首页已登录后再回终端按 Enter,过早回车可能拿不到完整会话 Cookie;
  3. 会话完整性检查interactive_reloginsessionid作为会话有效的最低判据,缺sessionid视为登录失败;
  4. 敏感信息管理config/cookies.json与回写的 YAML 均含会话凭证,请勿提交进版本库;config/config_loader.py 在 POSIX 下对含敏感字段的配置文件写回时会收紧权限为0o600
  5. 抓取失败兜底:msToken 缺失时工具会从 URL 查询串、请求头、document.cookie、localStorage/sessionStorage 逐级兜底,仍失败则打印缺失键警告,不会静默产出残缺配置;
  6. 多浏览器支持--browser支持chromium/firefox/webkit,不同环境间可切换,但需对应安装 Playwright 浏览器内核。

总结

tools/cookie_fetcher.py是 douyin-downloader 中一个"小而完整"的工具模块:它以 Playwright 异步 API 驱动真实浏览器完成手动登录,兼顾了导航等待的降级策略、msToken 的多源兜底提取、Cookie 的按需筛选与 RFC6265 清洗,并通过--config回写与config/cookies.json自动发现两条路径将登录态无缝接入下载管线。在 CLI 场景下,它还支撑起"登录失效 → 自动开浏览器重登 → 刷新 Cookie → 重试一次"的完整自助恢复链路(cli/login_flow.py + cli/main.py)。无论你是想手动维护登录态,还是想理解项目的自动重登机制,这个工具及其测试都是最直接的参考起点。

本文全部结论均来自仓库源码与测试:tools/cookie_fetcher.py、utils/cookie_utils.py、config/config_loader.py、cli/login_flow.py、tests/test_cookie_fetcher.py 及 pyproject.toml。

【免费下载链接】douyin-downloaderA practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音批量下载工具,去水印,支持视频、图集、合集、音乐(原声)。项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询