
简介day17-B站播放量完播率破解代码是一套针对哔哩哔哩移动端的数据采集方案主要面向APP爬虫与接口逆向学习者帮助解决播放量、完播率等指标难以直接获取的问题。压缩包共11个Python脚本整体仅8KB包体虽小但流程完整从端口转发与Hook抓包入手逐步定位AES密钥、SHA256盐值与明文再依托Python还原sign签名和请求体加密最终获取视频aid/cid并整合为可运行代码。脚本按步骤拆分便于逐段调试与理解完整展示了移动端爬虫从数据抓取到加密破译的典型路径。整套代码覆盖了APP爬虫中常见的逆向分析思路尤其适合希望掌握动态加载处理、反爬绕过和加密参数还原技巧的开发者参考。目前已有239人学习下载既可研究B站接口调用逻辑也能作为自制爬虫框架的骨架但使用时需注意遵循相关法律法规与平台规则。1. B站播放量完播率怎么抓盯这两个数的人十有八九卡在同一个地方做过内容分析或账号运营的人都知道播放量是面子完播率是底子。播放量是平台推流后的结果值完播率才是判断内容能不能被继续推的因变量。可问题是网页端的创作者后台能看到完播率但那只能是自己的视频想看竞品、想看某个品类下的爆款规律、想批量拉一批视频做回归分析网页端根本不给这个字段。于是很多人的目光转向APP端的数据通道——那里接口字段全更新及时也是这份名为“day17-B站播放量完播率破解代码”的资源真正要做的事不靠人工一个个点开视频猜数据而是用APP爬虫的思路把播放量和完播率从接口层稳定地捞出来落成结构化数据。适合谁做视频运营分析、做内容竞调、做推荐策略研究的人以及想入门APP抓包和接口模拟的开发者。下文把这套资源的完整拆解写清楚从接口选型到签名处理到排坑照着走能少踩大半的雷。2. 先搞清数据从哪来APP接口与网页接口的差异和选型2.1 网页端与APP端同是B站两套数据通道B站的网页端和APP端虽然共用同一套用户体系但数据接口的服务对象完全不同。网页端面向浏览器场景接口以web路径为主特点是请求头简单、不需要额外签名但字段聚合程度高很多细粒度指标被隐藏。APP端的接口路径通常挂x或pgc前缀面向移动客户端字段越细越是运营后台不愿意批量开放的数据越可能藏在APP接口里。我拆过不少视频类APP的数据通道有一个经验可以共享网页端接口给的是“展示数据”APP接口给的是“行为数据”。播放量、弹幕数、点赞数这类展示数据两边都有但完播率、人均播放时长、播放入口分布这类行为数据基本只在APP侧的接口出现。所以如果你确实需要完播率网页端这条路从一开始就该排除。拿这份资源里的做法来说它的抓取入口选的是APP接口体系而不是网页端。核心原因就一条完播率这个字段只在APP接口返回网页端拿不到。这个选型判断看起来简单但实际是很多人卡住的第一步——用网页端接口调试半天发现字段压根不存在回头才意识到接口体系选错了。2.2 为什么要选APP接口字段归属和数据粒度选APP接口不是因为它“高级”而是因为字段归属决定了能不能拿到目标数据。完播率在B站的数据模型里属于视频行为统计维度它不是一个固定的静态值而是平台根据播放行为实时计算的比值。网页端的接口聚合层只暴露播放量、弹幕数、评论数这些结果指标完播率被放在用户行为分析域里不对网页端开放。相比之下APP端的统计接口粒度更细一次请求往往能同时返回播放量、完播率、平均播放进度、播放入口等一堆字段。而且APP接口的返回体结构相对稳定JSON字段名几乎不带混淆适合做自动化解析。这是选择APP接口的第二个理由解析成本低字段直接可用。当然选APP接口也有代价——请求头要模拟移动端环境Cookie要带登录态部分接口还要做签名。这些在后面章节会一步步展开。2.3 接口访问的基础请求头、Cookie与登录态模拟APP接口不像网页端那么好说话它的反爬校验主要集中在三个维度请求头完整性、Cookie有效性、签名正确性。三者缺一不可。先看请求头。移动端接口会校验User-Agent里是否带 Android 或 iOS 标识同时会校验Referer、Accept等字段。如果请求头里的 User-Agent 是浏览器的接口直接拒掉。常见的做法是抓一次真实APP流量把请求头完整复制下来后续所有请求都基于这组请求头发送。实际操作中我一般会在资源里自带一个请求头模板把关键字段替换成变量方便根据自己设备的实际值调整。再说Cookie。APP接口的Cookie通常包含登录态令牌和部分业务标识。登录态过期是高频问题尤其是当你用一个账号频繁请求时服务端会逐步收紧校验。处理策略一般有两种一是定期手动刷新Cookie填入脚本配置二是做一个简易的Cookie池多个账号轮换。对个人分析场景来说前一种够用。签名是最后一个坎。B站现在普遍用的签名机制是动态签名参数里带时间戳和一个随机数种子。这部分是这份资源的重点之一下一章会单独讲。3. 写一个能跑的采集脚本请求、签名、字段提取全流程3.1 脚本分层采集任务不能写成一坨很多第一次接触APP爬虫的人习惯把请求、解析、存储写在同一个循环里。这种写法对小规模采集没问题但一旦要扩展成多视频批量采集翻车概率急速上升。这份资源的代码在结构上做了分层大致分为配置层、请求层、解析层、存储层。配置层管Cookie、UA、接口地址、目标视频ID列表请求层管带签名发请求解析层管从JSON里拖字段存储层管落盘。分层的好处是接口改了只动请求层字段变了只动解析层不用把整个脚本翻出来重写。下面是一个典型的请求层代码import requests import time import hashlib from urllib.parse import urlencode def fetch_video_stats(bvid, cookie, ua): # 公共请求头模拟APP环境 headers { User-Agent: ua, Referer: https://www.bilibili.com/, Cookie: cookie, Content-Type: application/json; charsetutf-8 } # 构造带时间戳的查询参数 params { bvid: bvid, ts: int(time.time()) } # 签名逻辑对参数做hash后拼接 sign generate_sign(params) params[w_sign] sign url https://api.bilibili.com/x/web-interface/view resp requests.get(url, headersheaders, paramsparams, timeout10) return resp.json()这段代码里的generate_sign是签名入口实际算法在下一节展开。先关注两个细节一是ts参数用的是当前unix时间戳这是大多数签名机制的基础参数之一用来防止请求重放二是返回结果统一为JSON方便后续解析。3.2 wbi签名机制看懂参数混淆和hash拼接B站的接口签名这两年经历了好几轮升级。早期是一些固定参数的MD5拼接后来演进为动态签名现在主流的机制是在参数里加入经过混淆的密钥通过特定算法生成签名值。这份资源里对应的处理方式是 wbi 签名名字不重要重要的是它的原理服务端动态下发两个密钥值客户端拿这两个值对参数做变换最后生成一个签名字段。签名逻辑拆开来看是三步第一步从预请求接口拿到密钥对第二步对URL参数做排序、拼接、字符替换第三步用MD5生成32位hash作为签名。def generate_sign(params): # 模拟wbi签名流程 # 1. 密钥对真实值从预请求接口获取这里用占位符 salt_key abc123 salt_value xyz789 # 2. 对参数按key做字典序排序拼接成待签名字符串 sorted_params sorted(params.items(), keylambda item: item[0]) query_string urlencode(sorted_params) # 3. 把明文和密钥组合后做md5 raw query_string salt_key salt_value sign hashlib.md5(raw.encode(utf-8)).hexdigest() return sign这张签名的核心逻辑就三点排序稳定、密钥不漏、算法固定。但必须说真实环境里的密钥不是写死在代码里的而是从某个预请求的返回体里动态抽取的。拿到密钥的请求往往有频率控制所以签名模块在实际运行时要加缓存不能每次请求都去拉一次密钥。资源代码里把密钥做了缓存封装第一次请求拉取后续直接从内存取直到失效才重新拉取。3.3 播放量与完播率字段的提取和归一化请求返回JSON后最关键的环节就是把播放量和完播率字段提取出来。B站返回体结构通常是外层code/message/data三层播放量在data.stat.view这个路径下完播率相关字段则藏在data.stat或扩展字段里。但这里有一个隐蔽的坑完播率字段名在不同接口版本下不完全一致有的版本返回view_finish_rate有的版本返回finish_percent。所以解析层不能硬编码字段名要做一次字段名兼容处理把这些可能的别名都检查一遍取第一个非空值。这和做数据分析时处理多来源数据要归一化是同一个道理只不过发生在JSON层。def extract_metrics(data): stat data.get(stat, {}) # 播放量一般直接挂在stat.view view_count stat.get(view, 0) # 完播率兼容多种字段名写法 finish_rate None for key in [view_finish_rate, finish_percent, finish_rate]: if stat.get(key) is not None: finish_rate stat.get(key) break # 完播率可能有千分位和百分位两种口径统一转为百分数 if finish_rate is not None: if finish_rate 1: finish_rate finish_rate / 100 return { bvid: data.get(bvid), view_count: view_count, finish_rate: finish_rate }归一化的目的很简单——有的接口把0.85直接返回有的返回85如果不做转换后面做分析时同一个指标两种量纲模型直接失真。我一般在提取后还会顺手做一次类型强转确保数值型字段是float而不是字符串这个细节能让后续的聚合分析少踩不少坑。3.4 数据落地CSV与SQLite双份保存数据提取出来后落地方式不建议走Excel采集规模一大Excel就卡。更实用的做法是同时输出CSV和SQLiteCSV给人看SQLite给程序查。import csv import sqlite3 def save_record(record, csv_path, db_path): # CSV追加写入 with open(csv_path, modea, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[bvid, view_count, finish_rate]) writer.writerow(record) # SQLite入库 conn sqlite3.connect(db_path) cursor conn.cursor() cursor.execute( INSERT OR REPLACE INTO video_stats(bvid, view_count, finish_rate) VALUES(?, ?, ?), (record[bvid], record[view_count], record[finish_rate]) ) conn.commit() conn.close()SQLite这里用INSERT OR REPLACE而不是普通INSERT好处是重复采集同一视频时不会产生脏数据而是直接覆盖最新结果。对于需要定时跑采集来做趋势观测的场景这个设计是必须的。CSV则保留追加语义每次运行都新增一行保留历史快照。数据库表结构建议提前建好字段名和解析层的输出严格对齐避免入库时出现映射错位。常见的低级错误就是CSV字段顺序和字典键顺序不一致导致数据串列。4. 避坑指南签名失效、风控限流、字段缺失的三类高发问题4.1 签名密钥失效导致所有请求返回 -403现象脚本跑得好好的某一天开始所有请求都报-403无论怎么刷新Cookie都无效。原因签名密钥对是服务端动态下发的会周期性轮换。资源代码里的密钥缓存机制虽然能减少请求次数但缓存过期后没有及时拉取新密钥导致后续所有请求都拿着旧密钥做签名服务端验签不通过。解决在签名请求的外层加异常捕获当响应码出现-403时主动清除本地密钥缓存强制触发一次预请求拉取新密钥。更稳妥的做法是给密钥加一个过期时间属性比如缓存60分钟到期后即使没报错也主动刷新。从那以后我再也没被密钥过期问题卡住过。4.2 一个小号跑高频请求被限流封禁风险高现象连续采集几十个视频后返回体里数据正常但登录态突然失效或者接口返回-412状态码。原因单账号高频请求触发了服务端风控。-412通常代表请求被风控拦截不是说你的签名错了而是你的访问模式太像机器。同一IP短时间访问大量视频详情这在正常用户行为里几乎不可能。解决第一控制采集频率单个视频之间至少sleep 1~2秒不要用并发第二请求头里的User-Agent和Cookie要与账号实际使用的设备环境一致否则风控模型很容易从设备指纹维度识别出异常第三必要的时候做代理池轮换但这块要看资源本身的支持情况。如果只是做小规模分析建议把采集量控制在一个合理范围内别贪多。4.3 字段返回为空但请求成功解析层别硬编码现象请求没有报错code为0但找到的完播率字段是None脚本直接抛异常。原因两种可能。第一种是当前账号对该视频没有完播统计权限接口在字段级别做了控制第二种是字段名变了接口升级后改了别名旧字段名已经废弃。解决解析层要用兼容式提取前面代码里已经写了多字段名轮询。另外要做空值兜底字段确实为空时不抛异常而是把记录标记为missing并继续跑完整个批次。采集任务最重要的是稳定跑完而不是某一条数据的完整性。这一条我从第一次大规模采集时就写进了代码规范里后面几乎没再为字段缺失的事翻过车。4.4 长时间采集后Cookie失效脚本中断现象采集跑到一半突然报登录态过期日志里开始刷-101错误。原因Cookie里的登录凭证有有效期长任务跑久了超过了凭证的过期时间。这事属于时间维度上的必然大概率会发生。解决在做任务设计时把Cookie有效期纳入考虑。简单方案是把Cookie配置做成外部输入失效后手动替换并重新启动脚本进阶方案是做一个简易的自动续期逻辑检测到-101时读备用Cookie池自动切换。但自动切换Cookie这事有一个前置条件备用Cookie的账号要有相同的权限否则依然拿不到数据。我自己的做法是准备两个小号Cookie轮流用一个被限另一个立刻顶上。5. 进阶玩法把一次性脚本改成可持续的视频数据观测系统一套脚本写完不是终点真正有用的是把它改造成能每天自动跑、持续出数的观测系统。这里分享一下我在复现这套资源后做的升级路径分成三个层次。第一个层次是任务化。把单次采集脚本包一层调度壳——记录上次采集到的视频ID列表每次启动时只采集新增和状态变化的视频采集结果追加到同一张表。这样跑一个月后表里就有了每个视频的多日快照可以做播放量增速和完播率变化趋势分析而不只是一天的截面数据。第二个层次是数据看板化。采集完的数据放在SQLite里配合轻量级的可视化工具做每日自动出图。比如播放量的环比增幅、完播率分布直方图、近7天新发视频的完播率排名。这块不需要上重型框架能解决“人工盯数”的痛点就够了。我是把可视化脚本挂在服务器上每天定时跑一次采集数据更新后自动生成新的图表页第二天早上直接看数据不用再手动打开后台一个个查。第三个层次是阈值告警。在采集任务后加一个规则引擎比如完播率连续三天下降且播放量过万自动标记为“内容衰退信号”播放量增速异常快且完播率高于平时两倍标记为“疑似爆款苗头”。这些规则不复杂本质上就是对查询结果做几个条件判断但价值密度比单纯的数据快照高很多。这套升级路径也不是一上来就全做完的。我的建议是先在本地跑通基础采集攒一周数据后做趋势分析分析结果能指导你的实际决策了再考虑上调度和告警。从那次以后我每次拿类似的接口资源都会强制自己先做一次数据落库的完备性检查再谈其他。写到这里希望这份完整拆解能帮你在采集播放量和完播率的路上少走几步弯路。本文还有配套的精品资源点击获取