中文 English

扫码成功、下载未登录:BBDown 弄丢的 Cookie 是这样找回来的

发布时间: 2026-09-26 · 阅读量 --
BBDown Bilibili 哔哩哔哩 扫码登录 Cookie 故障排查 Windows 11 自动化

「先说结论」

如果你用的 BBDown 还是 1.6.3(2024 年 8 月发布,也是官方仓库归档前的最后一个正式版本),那么从某次 B 站登录接口改版开始,扫码登录就已经「名存实亡」了:手机上明明提示登录成功,BBDown 的凭据文件里却只存下一张几分钟就作废的「临时取件票」(ticket),真正的登录 Cookie(SESSDATA)从来没被拿回来。于是每次下载都提示「你尚未登录B站账号」,扫一百次码结果都一样。

本文把这个问题从复现、定位、根因到修复完整讲一遍,并给出 Windows 11、Ubuntu 26.04、macOS 26 三套一键修复脚本(人工自动执行和 Agent 自动配置两种用法)。脚本只访问 B 站官方 passport 接口,不经过任何第三方服务器;文中所有真实输出里的凭据字段均已打码。

AI 生成封面:扫码后拿到的是一张临时 ticket,需要再换一次才能拿到真正的 Cookie。

图 1:AI 生成封面。修复的本质不是「重新登录」,而是把流程里缺失的「换票」一步补上。

一、问题背景:一个越来越常见的「登录玄学」

BBDown 是一款很流行的命令行哔哩哔哩下载器,配合 aria2c 和 ffmpeg,可以批量下载视频、番剧和合集。很多家庭场景会拿它下载儿歌、课程视频给离线设备用。要不要登录,区别是实打实的:不登录只能拿到有限清晰度,登录后 1080P 高码率、大会员画质才可选。

最近遇到一台长期用来下视频的电脑,BBDown 版本 1.6.3,症状非常典型:执行「BBDown login」后用手机扫码,手机提示登录成功;回到电脑上下载,却提示「你尚未登录B站账号, 解析可能受到限制」。重复扫了好几次,每次都一样。更奇怪的是,同一个目录里的 BBDownTV.data(TV 端登录)却一直是好的,用「-tv」模式解析不受影响。

BBDown 官方仓库:项目已归档,停留在 1.6.3。

图 2:官方仓库已归档为只读,不会再有新版本。这也意味着「升级到最新版解决登录问题」这条路走不通,需要我们理解问题并自己补上缺的步骤。

二、问题表现:成功的是扫码,失败的是登录

复现很简单,随便解析一个视频:

修复前的真实解析输出:先提示未登录,随后解析报错。

图 3:修复前的会话真实输出(凭据与路径已打码)。注意「加载本地cookie」之后紧跟着「你尚未登录」,说明程序读到了凭据文件,但认为里面没有有效的登录态。

扫码后 BBDown.data 里实际保存的内容:一张 ticket,不是 Cookie。

图 4:扫码「成功」后凭据文件的全部内容。ticket 是打码后的形态。gourl 和 first_domain 也都不是 Cookie——整个文件里找不到 SESSDATA 的影子。

把两个现象拼起来,问题的形状就出来了:扫码这一步是成功的(B 站确实认证了你的手机),失败的是「把登录态存下来」这一步。程序以为自己存好了,其实只存了一张几分钟后就作废的临时凭据。

三、问题分析:从「是不是版本太旧」到读源码

第一反应通常是两个猜测:Cookie 过期了,或者版本太旧。但这两个猜测都站不住。如果是过期,重新扫码就该恢复,而我们扫了很多次;如果是版本问题,官方仓库最新 release 停在 2024 年 8 月的 1.6.3,而本机正是这个版本,已经「最新」了。

BBDown 的版本发布页:1.6.3 至今仍是最新正式版。

图 5:版本发布页。值得注意的是 1.6.2 的更新日志里有一条「二维码登录接口修复」——说明扫码登录这个环节历史上就被 B 站的接口变化折腾过不止一次。

既然不能靠升级解决,就只能读源码了。BBDown 是开源的,扫码登录的核心逻辑在 BBDownLoginUtil.cs 里,逻辑大致是:生成二维码,每秒轮询一次扫码状态;一旦返回成功,从返回的 URL 里解析出登录态,把 query 参数转换成分号分隔的 Cookie 字符串,原样写进 BBDown.data。

旧版流程的完整链路:返回 URL 里直接带 Cookie,一步到位。

图 6:2024 年及更早,B 站扫码成功后返回的 URL 里直接装着 SESSDATA 和 bili_jct,BBDown 拆开存档即可。代码与当时的行为完全匹配。

代码没有写错——它只是过时了。它假设「登录成功后,Cookie 就在返回 URL 里」。B 站后来改掉了这个假设。

四、根因:B 站把「发 Cookie」拆成了两步,BBDown 只会走一步

用真实的扫码流程抓包对比可以确认:现在扫码确认成功后,轮询接口返回的 URL 里已经没有 SESSDATA,取而代之的是一个带 ticket 参数的跨域确认地址(crossDomain)。浏览器访问这个地址后,响应头里的 Set-Cookie 才把 SESSDATA、bili_jct、DedeUserID 等真正的登录态发下来。

新版流程:BBDown 1.6.3 在第二步把 ticket 当成 Cookie 存了下来。

图 7:新流程的断点一目了然。BBDown 把 ticket URL 的参数原样存进了 BBDown.data,而缺失的「访问跨域地址换 Cookie」一步,它从来没做过。

下载时程序按老格式找 SESSDATA,找不到,于是每次都判定未登录。

图 8:用小区快递柜打比方。ticket 就像「取件短信」,能证明柜子里有你的件,但打不开柜门,而且几分钟就作废;Cookie 才是柜门密码。BBDown 把短信纸条塞进了柜缝,柜门当然纹丝不动。修复脚本做的是先拿短信去驿站柜台换密码,再用密码开柜门。

还有一个容易踩的坑:ticket 的有效期极短。我们一开始尝试直接拿 BBDown.data 里残留的旧 ticket 去换 Cookie,接口直接 302 跳走,什么都拿不到——它早就作废了。所以修复必须「现场重新扫码、现场换票」,不能翻旧账。

五、如何解决:把「换票」一步补上

修复脚本做的事不多,但每一环都不能少:生成二维码、轮询扫码、拿到 ticket 后追加请求跨域确认地址、收下 Set-Cookie、按 BBDown 期望的格式(query 串、逗号转义为 %2C、分号分隔)写回 BBDown.data,最后自动解析一个视频做验收。

修复脚本的完整流水线:对用户来说只剩「扫码、确认」一个动作。

图 9:脚本的七步流水线。它只是把浏览器本来会做的事,在命令行里补做了。

实测一次成功的完整输出:

修复脚本运行日志:换票、写档、完成(SESSDATA 已打码)。

图 10:会话真实输出。注意第三行之后走的是「新流程」分支——这正是老版 BBDown 缺失的那一步。

修复后再解析,未登录提示消失,1080P 高码率的音视频流完整列出:

修复后的解析输出:登录态生效,解析成功。

图 11:修复后的会话真实输出(下载链接已截断)。判定标准很简单:不再出现「你尚未登录」,并且能列出完整清晰度。

5.1 三平台一键脚本(人工自动执行)

三套脚本共用同一个核心文件 bbdown_login_core.py(内容见 5.4),包装脚本负责准备环境、调用核心、自动验收。它们不依赖任何第三方云服务,只调用系统自带能力和两个纯 Python 库。

核心脚本 bbdown_login_core.py(三个平台都用这一份,与平台包装脚本放在同一目录):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# bbdown_login_core.py — BBDown 扫码登录修复核心
# 只访问 B 站官方 passport 接口;Cookie 只写本机 BBDown.data
import argparse, os, re, subprocess, sys, time
from urllib.parse import urlparse, parse_qs, quote

GEN_URL = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate?source=main-fe-header"
POLL_URL = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key={key}&source=main-fe-header"
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
      "(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")

def log(msg):
    print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)

def mask(v, keep=8):
    return v[:keep] + "……" if len(v) > keep else "……"

def collect_set_cookies(resp):
    out = []
    for r in resp.history + [resp]:
        for k, v in r.headers.items():
            if k.lower() == "set-cookie":
                pair = v.split(";")[0]
                if "=" in pair:
                    n, val = pair.split("=", 1)
                    if n.strip() and val.strip():
                        out.append((urlparse(r.url).netloc, n.strip(), val.strip()))
    return out

def exchange_ticket(session, ticket, gourl):
    """拿跨域 ticket 换真实 Cookie(老版 BBDown 缺失的一步)"""
    cookies = {}
    urls = [
        f"https://passport.biligame.com/x/passport-login/web/crossDomain?ticket={ticket}&gourl={quote(gourl, safe='')}",
        f"https://passport.bilibili.com/x/passport-login/web/crossDomain?ticket={ticket}&gourl={quote(gourl, safe='')}",
        f"https://passport.biligame.com/crossDomain?ticket={ticket}&gourl={quote(gourl, safe='')}",
    ]
    for url in urls:
        log(f"尝试跨域确认: {urlparse(url).netloc} ...")
        try:
            r = session.get(url, timeout=15, allow_redirects=True)
        except Exception as e:
            log(f"  请求失败: {e}")
            continue
        for src, n, v in collect_set_cookies(r):
            log(f"  Set-Cookie 来自 {src}: {n}={mask(v)}")
            cookies[n] = v
        for m in re.findall(r"https?://[^\"'\s<>]+", r.text):
            m = m.replace("\\u0026", "&").replace("&amp;", "&")
            if "sso" in m.lower() and ("bilibili" in m or "biligame" in m):
                try:
                    for _, n, v in collect_set_cookies(session.get(m, timeout=15)):
                        cookies[n] = v
                except Exception:
                    pass
        if "SESSDATA" in cookies:
            log("已获取 SESSDATA")
            break
    return cookies

def open_image(path):
    try:
        if sys.platform == "win32":
            os.startfile(path)
        elif sys.platform == "darwin":
            subprocess.Popen(["open", path])
        else:
            subprocess.Popen(["xdg-open", path])
    except Exception:
        pass

def do_login(data_dir):
    import requests, qrcode
    s = requests.Session()
    s.headers.update({"User-Agent": UA, "Referer": "https://passport.bilibili.com/"})

    qr_url = s.get(GEN_URL, timeout=15).json()["data"]["url"]
    key = parse_qs(urlparse(qr_url).query)["qrcode_key"][0]

    png = os.path.join(data_dir, "login_qrcode.png")
    qrcode.make(qr_url).save(png)
    open_image(png)
    log(f"二维码已生成并尝试打开: {png},请用哔哩哔哩 APP 扫码并确认")

    deadline = time.time() + 180
    success_url = None
    while time.time() < deadline:
        time.sleep(2)
        code = s.get(POLL_URL.format(key=key), timeout=15).json()["data"]["code"]
        if code == 86101:
            continue
        if code == 86090:
            log("已扫码,请在手机上确认登录...")
            continue
        if code == 86038:
            log("二维码已过期,请重新运行")
            return 2
        if code == 0:
            success_url = s.get(POLL_URL.format(key=key), timeout=15).json()["data"]["url"]
            log("登录确认成功")
            break
        log(f"未知状态 code={code}")
        return 2
    if not success_url:
        log("等待扫码超时,请重新运行")
        return 2

    q = parse_qs(urlparse(success_url).query)
    if "SESSDATA" in q:
        cookie_str = success_url.split("?", 1)[1].replace("&", ";").replace(",", "%2C")
        log("返回 URL 直接包含 Cookie(旧流程)")
    elif "ticket" in q:
        log("返回的是跨域 ticket(新流程),正在换取真实 Cookie ...")
        cookies = exchange_ticket(s, q["ticket"][0], q.get("gourl", ["https://www.bilibili.com"])[0])
        if "SESSDATA" not in cookies:
            log(f"换取 Cookie 失败!获取到的键: {', '.join(cookies.keys()) or '无'}")
            return 3
        cookie_str = ";".join(f"{k}={v}" for k, v in cookies.items()).replace(",", "%2C")
    else:
        log("无法识别的返回 URL")
        return 4

    data_file = os.path.join(data_dir, "BBDown.data")
    if os.path.exists(data_file):
        bak = data_file + ".bak-" + time.strftime("%Y%m%d-%H%M%S")
        os.replace(data_file, bak)
        log(f"已备份原凭据文件: {os.path.basename(bak)}")
    with open(data_file, "w", encoding="utf-8") as f:
        f.write(cookie_str)
    log("全部完成,请用 BBDown 正常下载验证")
    return 0

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--data-dir", default=".")
    ap.add_argument("--check", action="store_true", help="仅检查 BBDown.data 是否包含 SESSDATA")
    args = ap.parse_args()
    if args.check:
        data_file = os.path.join(args.data_dir, "BBDown.data")
        if os.path.exists(data_file):
            has = "SESSDATA=" in open(data_file, encoding="utf-8").read()
            print(f"BBDown.data 存在,包含 SESSDATA: {has}")
            sys.exit(0 if has else 1)
        print("BBDown.data 不存在")
        sys.exit(1)
    sys.exit(do_login(args.data_dir))

if __name__ == "__main__":
    main()

Windows 11 包装脚本 Fix-BBDown-Login.ps1(保存时请确保为 UTF-8 编码,放在核心脚本同目录):

param(
    [string]$BBDownDir = (Get-Location).Path,
    [switch]$CheckOnly
)
$ErrorActionPreference = "Stop"
$core = Join-Path $PSScriptRoot "bbdown_login_core.py"
if (-not (Test-Path $core)) { throw "bbdown_login_core.py not found next to this script" }

# 1. 找 Python 3(先 py 启动器,再 PATH;都没有则用系统自带的 winget 安装)
$py = $null
foreach ($cand in @(@("py", "-3"), @("python"))) {
    try {
        $v = & $cand[0] -c "import sys; print(sys.version_info[0])" 2>$null
        if ($v -eq "3") { $py = $cand; break }
    } catch { }
}
if (-not $py) {
    Write-Host "Python 3 not found. Installing via winget..."
    winget install --id Python.Python.3.12 --silent --accept-package-agreements --accept-source-agreements
    $py = @("py", "-3")
}

# 2. 独立虚拟环境 + 两个纯 Python 依赖(不污染系统环境)
$venv = Join-Path $env:TEMP "bbdown-login-venv"
if (-not (Test-Path (Join-Path $venv "Scripts\python.exe"))) { & $py[0] $py[1] -m venv $venv }
$vpy = Join-Path $venv "Scripts\python.exe"
& $vpy -m pip install --quiet --disable-pip-version-check requests qrcode
if ($LASTEXITCODE -ne 0) { throw "pip install failed" }

# 3. 检查或修复
if ($CheckOnly) { & $vpy $core --data-dir $BBDownDir --check; return }
& $vpy $core --data-dir $BBDownDir
if ($LASTEXITCODE -ne 0) { throw "login fix failed, see messages above" }

# 4. 验收:解析一个视频,确认不再提示未登录
$exe = Join-Path $BBDownDir "BBDown.exe"
if (Test-Path $exe) {
    Write-Host "`n[Verify] BBDown parse test..."
    $out = & $exe --only-show-info BV1Ad4y1g7AZ 2>&1 | Out-String
    if ($out -match "尚未登录") { Write-Host "VERIFY FAILED: still reports not logged in." -ForegroundColor Red }
    elseif ($out -match "任务完成|共计") { Write-Host "VERIFY OK: logged in and parse succeeded." -ForegroundColor Green }
    else { Write-Host "VERIFY UNCLEAR: review the output above manually." }
} else {
    Write-Host "BBDown.exe not found in $BBDownDir; skip verify."
}

用法:预演检查用「powershell -ExecutionPolicy Bypass -File .\Fix-BBDown-Login.ps1 -CheckOnly」;正式修复用「powershell -ExecutionPolicy Bypass -File .\Fix-BBDown-Login.ps1 -BBDownDir “你的BBDown目录”」。

Windows 侧依赖管理参考:WinGet 官方文档。

图 12:脚本在找不到 Python 3 时会用 Windows 自带的 WinGet 静默安装,这是系统组件而不是第三方升级助手。

Ubuntu 26.04 包装脚本 fix-bbdown-login-ubuntu2604.sh:

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CORE="$SCRIPT_DIR/bbdown_login_core.py"
[ -f "$CORE" ] || { echo "bbdown_login_core.py not found next to this script"; exit 1; }

MODE="${1:-}"
if [ "$MODE" = "--check" ]; then DATA_DIR="${2:-.}"; else DATA_DIR="${1:-.}"; fi

# 1. 确保 Python 3 与 venv 组件(缺失时才用 apt 安装)
if ! command -v python3 >/dev/null 2>&1; then
  sudo apt-get update && sudo apt-get install -y python3
fi
if ! python3 -m venv --help >/dev/null 2>&1; then
  sudo apt-get install -y python3-venv
fi

# 2. 独立虚拟环境 + 依赖
VENV="$HOME/.cache/bbdown-login-venv"
if [ ! -x "$VENV/bin/python" ] && [ ! -x "$VENV/Scripts/python.exe" ]; then
  python3 -m venv "$VENV"
fi
VPY="$VENV/bin/python"
[ -x "$VPY" ] || VPY="$VENV/Scripts/python.exe"
"$VPY" -m pip install --quiet --disable-pip-version-check requests qrcode

# 3. 检查或修复
if [ "$MODE" = "--check" ]; then exec "$VPY" "$CORE" --data-dir "$DATA_DIR" --check; fi
"$VPY" "$CORE" --data-dir "$DATA_DIR"

# 4. 验收
if [ -x "$DATA_DIR/BBDown" ]; then
  echo "[Verify] BBDown parse test..."
  if "$DATA_DIR/BBDown" --only-show-info BV1Ad4y1g7AZ 2>&1 | grep -q "尚未登录"; then
    echo "VERIFY FAILED: still reports not logged in."
  else
    echo "VERIFY OK: logged in and parse succeeded."
  fi
else
  echo "BBDown binary not found in $DATA_DIR; skip verify."
fi

用法:预演检查用「bash fix-bbdown-login-ubuntu2604.sh –check」;正式修复用「bash fix-bbdown-login-ubuntu2604.sh /path/to/bbdown-dir」。

Ubuntu 侧参考:apt-get 官方手册(26.04 系列)。

图 13:脚本只在缺组件时用系统包管理器补装 python3-venv,其余全部隔离在自己的虚拟环境里。

macOS 26 包装脚本 fix-bbdown-login-macos26.zsh:

#!/bin/zsh
set -euo pipefail
SCRIPT_DIR="${0:A:h}"
CORE="$SCRIPT_DIR/bbdown_login_core.py"
[ -f "$CORE" ] || { echo "bbdown_login_core.py not found next to this script"; exit 1; }

MODE="${1:-}"
if [[ "$MODE" == "--check" ]]; then DATA_DIR="${2:-.}"; else DATA_DIR="${1:-.}"; fi

# 1. macOS 26 自带 python3(随命令行工具);缺失时提示安装,脚本不擅自改装系统
if ! command -v python3 >/dev/null 2>&1; then
  echo "python3 not found. Run 'xcode-select --install' first, then re-run."
  exit 1
fi

# 2. 独立虚拟环境 + 依赖
VENV="$HOME/.cache/bbdown-login-venv"
if [[ ! -x "$VENV/bin/python" && ! -x "$VENV/Scripts/python.exe" ]]; then
  python3 -m venv "$VENV"
fi
VPY="$VENV/bin/python"
[[ -x "$VPY" ]] || VPY="$VENV/Scripts/python.exe"
"$VPY" -m pip install --quiet --disable-pip-version-check requests qrcode

# 3. 检查或修复
if [[ "$MODE" == "--check" ]]; then exec "$VPY" "$CORE" --data-dir "$DATA_DIR" --check; fi
"$VPY" "$CORE" --data-dir "$DATA_DIR"

# 4. 验收
BBDOWN_BIN=""
for c in "$DATA_DIR/BBDown" "$DATA_DIR/BBDown.exe"; do
  [[ -x "$c" ]] && BBDOWN_BIN="$c" && break
done
if [[ -n "$BBDOWN_BIN" ]]; then
  echo "[Verify] BBDown parse test..."
  if "$BBDOWN_BIN" --only-show-info BV1Ad4y1g7AZ 2>&1 | grep -q "尚未登录"; then
    echo "VERIFY FAILED: still reports not logged in."
  else
    echo "VERIFY OK: logged in and parse succeeded."
  fi
else
  echo "BBDown binary not found in $DATA_DIR; skip verify."
fi

macOS 侧参考:Homebrew 官方手册。

图 14:本修复不需要 Homebrew。如果你平时用 brew 管理 BBDown,注意 brew 安装的位置可能与手动下载的二进制不同,把实际使用的那个目录传给脚本即可。

5.2 三平台一键脚本(Agent 自动配置)

如果你已经有可执行本地命令的 Agent(Codex、Claude Code、HermesAgent 等),可以直接把下面这段交给它:

请帮我修复本机 BBDown 扫码登录后仍提示未登录的问题。要求:
1. 先定位 BBDown 可执行文件目录和 BBDown.data 的实际位置(注意:1.6.3 版写到 exe 同目录)。
2. 检查 BBDown.data 内容:若包含 ticket= 且不包含 SESSDATA=,即为本文所述问题,按根因修复;否则先向用户汇报实际内容再继续。
3. 按当前系统选择修复方式:Windows 11 用 PowerShell,Ubuntu 26.04 用 bash,macOS 26 用 zsh;用系统 Python 建独立 venv,安装 requests 与 qrcode 两个纯 Python 库后运行核心登录脚本。
4. 生成二维码后提醒用户扫码,不要跳过人工确认步骤;不要尝试用旧的 ticket 直接换 Cookie(有效期极短)。
5. 修复完成后必须验收:运行 BBDown 解析一个公开视频,确认输出不含「尚未登录」。
6. 全程不得输出完整 Cookie、SESSDATA、ticket 原文、内网地址、完整计算机名或任何账号信息;展示时一律打码。
7. 不修改 BBDown 主程序本身,不安装来路不明的二进制,不上传本机任何文件。

5.3 两种方法怎么选

只有一两台自己的机器,选人工方式:把两个文件放同目录、跑一次检查、再跑一次修复,全程盯着二维码,五分钟内结束。要照顾家里多台电脑、或有让 Agent 批量维护的习惯,选 Agent 方式:把 5.2 的任务书交给它,让它一台一台跑「检查、修复、验收」,失败机器单独汇总。两种方法的底层是同一套脚本,只是「按回车的人」不同。

5.4 关于核心脚本的两句提醒

第一,脚本会先把原 BBDown.data 备份成带时间戳的 .bak 文件,不满意随时可以还原。第二,脚本对 Cookie 内容做了打码输出,如果你打算把运行日志贴到网上求助,也请先检查有没有把 SESSDATA 原文贴出去——它是你账号的「柜门密码」,泄露了等于把账号借给别人。

六、Q&A:几个常见疑问

Q1:直接升级 BBDown 能解决吗?

目前不能。官方仓库已归档,1.6.3 就是最后的正式版。社区有一些活跃 fork,但选择第三方 fork 意味着把你的登录 Cookie 交给另一个构建产物的作者信任链,请自行评估。本文的修复不改动 BBDown 本体,风险面小得多。

Q2:修好之后能管多久?

SESSDATA 的有效期通常是半年左右。到那天之后会再次出现「未登录」,重跑一次修复脚本、重新扫一次码即可。这也解释了为什么这个问题值得留一个可重复执行的脚本,而不是手工修一次就完事。

Q3:为什么「-tv」模式一直正常?

TV 端登录走的是完全不同的 access_token 体系(存放在 BBDownTV.data),B 站没有改它的流程,所以 TV 模式不受影响。这也是判断问题出在 Web Cookie 流程的重要线索。

Q4:那个 Arg_KeyNotFound 报错是同一个问题吗?

不是。那是 1.6.3 对个别极老视频的解析兼容问题,与登录无关。登录修复后,常规视频和番剧都能正常解析;如果你恰好要下某个 2015 年前后的老视频遇到了,换个源或者用 fork 版解析即可。

Q5:脚本会不会把我的账号信息传到别处?

不会。核心脚本只与 B 站官方 passport 域名通信,换来的 Cookie 只写进本机 BBDown.data。你可以自己审阅脚本内容,所有请求地址都写在明面上。

Q6:用手机扫屏幕上的二维码安全吗?

二维码内容只是登录确认链接,与 B 站官方网页登录用的是同一套接口。确认动作发生在你自己的手机和 B 站之间,脚本拿到的只是登录结果的 Cookie。前提是脚本来源可信——所以正文把完整代码贴出来供你审阅。

七、最后的建议

这次排查给我最大的启发是:当「官方不再更新」的老工具遇到「平台持续变化」的接口,出问题的往往不是代码写错了,而是代码里某条曾经成立的假设悄悄失效了。BBDown 1.6.3 假设扫码成功即拿到 Cookie,这个假设在 2024 年成立,后来不在了。

遇到这类「玄学」,比较可靠的路径是:先把失败现象拆成「哪一步成功了、哪一步没发生」,再去看那个环节的代码假设了什么,最后对着接口的真实行为验证假设。把缺失的一步补上,比推翻重来安全得多。同样的思路也适用于其他「昨天还好好的,今天就坏了」的自托管工具。

参考资料

本文阅读量 --