WBI 签名是什么

B 站在 2023 年引入 WBI(Web Bilibili Interface)签名机制,用于保护 Web 端 API 接口不被爬虫滥用。与 App 端 MD5 签名不同,WBI 使用动态密钥 + 混淆映射表

密钥获取

WBI 密钥从 /x/web-interface/nav 接口动态获取:

import requests

def get_wbi_keys(cookies: dict) -> tuple:
    r = requests.get(
        'https://api.bilibili.com/x/web-interface/nav',
        cookies=cookies
    )
    data = r.json()['data']['wbi_img']
    # img_url: https://i0.hdslb.com/bfs/wbi/abc123.png
    img_key = data['img_url'].rsplit('/', 1)[1].split('.')[0]
    sub_key = data['sub_url'].rsplit('/', 1)[1].split('.')[0]
    return img_key, sub_key

混淆映射表(从 B 站前端 JS 中提取)

MIXIN_KEY_ENC_TAB = [
    46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35,
    27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13,
    37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4,
    22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52,
]

def get_mixin_key(img_key: str, sub_key: str) -> str:
    raw = img_key + sub_key  # 两个 key 拼接
    # 按映射表取字符,取前 32 位
    return ''.join(raw[i] for i in MIXIN_KEY_ENC_TAB)[:32]

完整 WBI 签名

import hashlib, time, urllib.parse, re

def wbi_sign(params: dict, mixin_key: str) -> dict:
    params = dict(params)
    params['wts'] = str(int(time.time()))
    # 按 key 字母排序
    sorted_params = dict(sorted(params.items()))
    # URL encode 后过滤特殊字符 !'()*
    query = urllib.parse.urlencode(sorted_params)
    query = re.sub(r"[!'()*]", '', query)
    # 追加 mixin_key 后取 MD5
    w_rid = hashlib.md5((query + mixin_key).encode()).hexdigest()
    sorted_params['w_rid'] = w_rid
    return sorted_params

浏览器指纹构造

B 站的 Web 风控会检查客户端指纹,需要同时上报 b_nut(canvas hash)、buvid_fp(指纹 ID)等字段。从真实 Chrome 中提取后存入 fp_data.json

import json

with open('fp_data.json') as f:
    fp = json.load(f)

# 上报心跳时携带
params = {
    'bvid': 'BV1xx411c7mD',
    'aid': aid,
    'cid': cid,
    'buvid': fp['buvid3'],
    'b_nut': fp['b_nut'],
    ...
}
signed = wbi_sign(params, mixin_key)

播放心跳上报流程

1. GET /x/click-interface/click/web/h5   初始化点击事件
2.  15  POST /x/click-interface/web/heartbeat  持续上报
   参数aid, cid, bvid, mid, played_time, realtime, start_ts, type, dt

注:App 端心跳使用 /x/report/heartbeat/mobile,参数与 Web 端不同,签名使用 App 套件(appkey=1d8b6e7d45233436)。