ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

用Python合规分析B站播放量:爬虫、数据采集与自动化实战

用Python合规分析B站播放量:爬虫、数据采集与自动化实战 经常有人私信问我“用Python刷B站播放量到底行不行”还有人更直接上来就想要一个能自动刷播放量的脚本。我先把话说在前面从技术上讲这件事能做但它不是一个值得投入的技术方向。平台的风控不是摆设真正能“刷”出有效播放量的成本远高过你踏实做内容、做数据分析的成本。而且一旦被识别轻则视频限流重则账号封禁甚至可能踩到法律红线。与其整天琢磨怎么刷量我更建议你把Python这套东西用在正道上用爬虫获取B站公开数据用自动化工具做功能测试用数据分析反推内容方向。这才是真正能让你长期受益的技能组合。这篇文章就从这个“刷播放量”的八卦切入把B站播放量的判定逻辑、Python爬虫和自动化相关的技术细节、以及实际操作中的各种坑一次讲清楚。如果你正准备学Python爬虫、自动化或者数据分析拿B站当练手目标非常合适。如果你脑子里的目标是“绕过风控刷量”那建议你调整下预期这里只讲合规、安全、可落地的技术方案。1. 先搞清楚B站播放量是怎么算的1.1 播放量的基础判定规则很多人以为播放量就是“页面被打开一次算一次”其实早就不是这个玩法了。B站对一个有效播放的判定涉及非常多的维度用户是否登录、IP地址、设备信息、页面停留时长、视频播放进度、是否切后台、是否静音、有没有发弹幕或点赞这些行为都会被记录。简单理解B站想统计的是“真实用户产生了多少有效观看”而不是“页面被刷新了几次”。所以同一台设备、同一个IP在短时间内反复刷新同一个视频页面大概率不会刷新出有效播放。平台还做了去重逻辑一个用户在短时间内多次观看同一个视频可能只记一次或者把权重压得很低。另外不同来源的播放权重也不一样。从首页推荐点进来的、从搜索结果点进来的、从评论区跳转的在风控模型里会有不同的行为模式。纯脚本模拟的请求往往缺少完整的行为链路一眼就能被识别出来。1.2 为什么无脑刷很容易翻车B站的异常检测机制并不是盯着某一个请求看而是看整体数据曲线。一个视频正常播放量是按天、按小时缓慢爬坡的如果你用脚本在几分钟内跑出几千次播放曲线就会变成一根几乎垂直的直线这种形态非常扎眼。平台的反作弊体系通常会做几层校验首先是IP维度短时间内大量请求会直接触发频率限制然后是设备维度通过浏览器指纹、Canvas指纹、WebRTC信息等识别你的设备再往上是行为维度鼠标移动轨迹、滚动速度、点击间隔是否像真人。这里说一个我实际见过的例子。有朋友拿网上找的“刷播放量脚本”跑了几个小时播放量确实涨了几百后台数据一眼假全部集中在一个时间点而且用户画像全是同一个操作系统的浏览器版本。第二天账号就被限制推荐了视频几乎零流量。这就是典型的“刷了个寂寞”。所以我一直认为与其研究怎么骗过风控不如老老实实弄明白这套规则然后用Python去做合规的数据采集和分析让数据告诉你用户喜欢什么。2. Python环境准备与必备库2.1 环境安装与虚拟环境配置不管你是Windows、macOS还是Linux装Python都建议直接从官网下载最新稳定版。安装时记得勾选“Add Python to PATH”这个步骤很多人会忽略导致后面在命令行里输入python直接提示未找到命令。装好之后建议为每个项目建一个独立的虚拟环境避免乱七八糟的第三方库互相打架。创建一个专门做B站数据采集的项目目录然后执行mkdir bili-project cd bili-project python -m venv venvWindows下激活虚拟环境venv\Scripts\activatemacOS和Linux下激活source venv/bin/activate激活后命令行前面会出现一个(venv)前缀说明已经进入虚拟环境了。这一步看着啰嗦但我强烈建议养成习惯。我见过太多因为全局环境库冲突最后整个Python环境都坏掉的例子。项目独立环境删掉重建也就是一条命令的事。国内用户安装第三方库时建议先配置一个国内镜像源不然下载速度会让人崩溃。用清华源来设置pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple这个操作会把pip的默认下载源改成清华镜像后续pip install速度会快很多。2.2 常用第三方库选型做B站数据采集和自动化我会用到下面这些库各自负责不同的事。库名用途适用场景requests发送HTTP请求调用公开API获取视频信息、播放量、评论等beautifulsoup4解析HTML页面从网页源码中提取内容selenium驱动真实浏览器需要模拟用户操作或测试页面时aiohttp异步HTTP请求大量请求时提高效率pandas数据处理统计播放量、排序、过滤matplotlib数据可视化画播放量趋势图、分布图requests是最基础的绝大多数公开接口用requests就够了。aiohttp适合做并发采集后面我会单独讲。selenium更适合做自动化测试而不是用来刷量。pandas和matplotlib负责把采集到的数据变成有价值的分析结果。安装这些库一条命令搞定pip install requests beautifulsoup4 selenium aiohttp pandas matplotlib装完之后验证一下环境python -c import requests, pandas, matplotlib; print(ok)如果输出ok说明环境没问题可以进入下一步了。3. 基于B站公开API获取播放量数据3.1 找接口别瞎猜很多初学者一上来就去解析网页HTML费了半天劲还容易出错。其实B站有一套公开的WEB API可以直接返回视频的JSON数据里面就包含了播放量、点赞、投币、收藏等信息。找接口的方法是打开B站任意视频页按F12打开开发者工具切到Network面板刷新页面在过滤输入框里输入view就能看到一个名为view的请求URL大概长这样https://api.bilibili.com/x/web-interface/view?bvidBV1xx411c7mD这就是获取视频信息的基础接口。它返回的JSON里data.stat.view就是播放量data.stat.like是点赞数data.stat.danmaku是弹幕数。比在HTML里正则匹配稳定太多了。需要注意的是调用这个接口时请求头里最好带上User-Agent和Referer否则有时候会返回风控提示。Referer填视频页面的地址就行这不是什么高深的绕过手段只是模拟正常浏览器行为。3.2 写一个获取单个视频信息的脚本下面这段代码是完整可跑的我建议你先自己动手敲一遍别直接复制。理解每个字段的含义比代码本身更重要。import requests import json url https://api.bilibili.com/x/web-interface/view params { bvid: BV1xx411c7mD } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.bilibili.com/ } resp requests.get(url, paramsparams, headersheaders, timeout10) data resp.json() if data[code] 0: video_info data[data] stat video_info[stat] print(标题:, video_info[title]) print(播放量:, stat[view]) print(点赞:, stat[like]) print(投币:, stat[coin]) print(收藏:, stat[favorite]) print(分享:, stat[share]) else: print(请求失败code:, data[code], message:, data[message])这里面有一个小细节timeout10一定要加。不加的话遇到网络问题脚本可能一直卡在那里加了超时之后至少能及时报错。3.3 批量获取多个视频信息实际分析场景下单个视频的数据没什么意义我们要的是批量数据。比如拿到一个Up主的所有视频BV号然后逐个获取播放量。这个逻辑也很简单循环调用上面的接口就行了。这里我加了一个time.sleep(1)意思是每个视频请求之间至少间隔1秒。很多人的IP封禁原因就是请求太快完全没有间隔B站服务器又不是看不出来你在用脚本。import requests import csv import time import random HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.bilibili.com/ } def get_video_stat(bvid): url https://api.bilibili.com/x/web-interface/view try: resp requests.get(url, params{bvid: bvid}, headersHEADERS, timeout10) data resp.json() if data[code] 0: d data[data] return { bvid: bvid, title: d[title], view: d[stat][view], like: d[stat][like], coin: d[stat][coin], favorite: d[stat][favorite], share: d[stat][share] } except Exception as e: print(bvid, 请求异常:, e) return None bvid_list [ BV1xx411c7mD, BV1Q5411w7z5, BV1GJ411x7h7 ] results [] for bvid in bvid_list: info get_video_stat(bvid) if info: results.append(info) print(info[title], 播放量:, info[view]) time.sleep(random.uniform(1, 2)) with open(bili_stats.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[bvid, title, view, like, coin, favorite, share]) writer.writeheader() writer.writerows(results) print(数据已保存到 bili_stats.csv)编码用了utf-8-sig这样用Excel打开CSV时中文不会乱码。这个小坑很多新手踩过我直接帮你填平了。这里要特别强调一个原则只请求公开数据不做任何爆破、采集用户隐私、绕过登录限制的事情。礼貌爬虫不仅是对平台规则的尊重也是保护自己不惹麻烦的底线。4. 自动化模拟浏览器操作的边界与风险4.1 Selenium能做什么不该做什么Selenium可以驱动一个真实的浏览器自动打开网页、点击按钮、填写表单、滚动页面几乎模拟真人的所有操作。很多人一看到这个就想“那我不就可以用Selenium自动看视频、刷播放量了吗”技术上确实可以但我不建议你这么做。先说后果B站的检测机制对Selenium并不陌生浏览器自动化工具会在运行时留下大量特征比如navigator.webdriver的值、浏览器窗口的启动方式、鼠标事件的机械化规律。平台要识别这些非常容易识别之后轻则限流重则封号。那Selenium在B站相关的正经场景里能做什么我举几个例子做网页功能的自动化冒烟测试比如定时检查自己的视频页面是否能正常打开做页面截图比如自动截取每天的视频播放量变化图做前端开发调试比如验证某个组件在不同分辨率下的表现。这些都是Selenium的舒适区。4.2 平台如何识别浏览器自动化我把识别逻辑拆开说你就能理解为什么“刷量脚本”越来越不好用了。浏览器指纹是第一步。Selenium启动的浏览器和普通浏览器相比很多属性是异常的。比如navigator.webdriver这个属性在Selenium控制的浏览器中会自动变为true普通浏览器中这个属性不是true。虽然可以用JavaScript代码把它改掉但平台还有别的检测点。行为轨迹是第二步。真人浏览视频页通常会有一个打开页面、视线移动、鼠标滚动、点击播放按钮、看完一部分再拖进度条的过程。而脚本执行的动作往往是瞬间完成、频率固定、路径直来直去这些特征在行为序列里非常明显。数据特征分析是第三步。如果你的账号之前一直看美食视频突然开始机械化地反复打开几个视频每个视频停留时间完全一致风控模型大概率会把这个账号标记为异常账号。所以与其花精力研究绕过检测不如彻底放弃这个想法。真正有价值的工作是合规的自动化测试和数据采集而不是和风控玩猫鼠游戏。4.3 一个合规的Selenium示例读取视频标题和播放量为了让你看到Selenium的正确用法我写一个从视频页面读取标题和播放量的示例。它只做数据读取不做任何自动播放、循环刷新操作适用于页面巡检场景。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager options webdriver.ChromeOptions() options.add_argument(--start-maximized) options.add_experimental_option(excludeSwitches, [enable-automation]) driver webdriver.Chrome(serviceService(ChromeDriverManager().install()), optionsoptions) driver.get(https://www.bilibili.com/video/BV1xx411c7mD) try: WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, //h1[title])) ) title driver.find_element(By.XPATH, //h1[title]).get_attribute(title) view_text driver.find_element(By.XPATH, //span[classview]).text print(标题:, title) print(页面播放量文本:, view_text) finally: driver.quit()注意页面里播放量的文本可能是“1.2万播放”这种格式需要自己写个转换函数处理。代码里等待元素出现用的是WebDriverWait比time.sleep更稳妥页面加载快的时候不用傻等固定时间加载慢的时候也能等到元素出现。这个示例的唯一目的就是告诉你自动化浏览器可以做数据采集和测试但不要拿去做违反平台规则的刷量操作。这是原则问题。5. 多进程与协程大规模数据采集提速5.1 为什么不能用简单循环处理几百个视频上面的批量采集代码是串行的一次请求等返回再发下一次几百个视频还好但如果你要抓几千个视频串行速度就会慢得让人抓狂。这时就有必要引入并发。但在提升速度之前必须先明确一条铁律并发不是让你加速作死的。很多人一上来就把并发开到100结果几十个请求之后IP就被B站风控直接412。合理的做法是控制并发数限速遵守平台的频率约束。5.2 用asyncio和aiohttp写异步批量采集如果你请求的都是回调频率不高的公开APIasyncio加aiohttp是一个很轻量又高效的方案。下面的代码实现了同时最多发起5个请求所有请求结束后统一汇总结果。import asyncio import aiohttp import random HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.bilibili.com/ } semaphore asyncio.Semaphore(5) async def fetch_video_stat(session, bvid): url https://api.bilibili.com/x/web-interface/view params {bvid: bvid} async with semaphore: try: async with session.get(url, paramsparams, headersHEADERS, timeout10) as resp: data await resp.json() if data[code] 0: d data[data] return { bvid: bvid, title: d[title], view: d[stat][view] } except Exception as e: print(bvid, 请求异常:, e) return None async def main(bvid_list): async with aiohttp.ClientSession() as session: tasks [fetch_video_stat(session, bvid) for bvid in bvid_list] results await asyncio.gather(*tasks) return [r for r in results if r] if __name__ __main__: bvid_list [ BV1xx411c7mD, BV1Q5411w7z5, BV1GJ411x7h7 ] results asyncio.run(main(bvid_list)) for item in results: print(item[title], item[view])asyncio.Semaphore(5)是并发限制器意思是最多同时跑5个请求。这样可以保证速度比串行快但又不会快到触发风控。如果你发现还是被限制就把5改成3甚至改成2。5.3 多进程的使用场景异步适合I/O密集型的请求等待但如果你的任务里有大量的HTML解析、数据处理想要吃满CPU多核那就得上多进程。Python的concurrent.futures模块用起来比较简单。from concurrent.futures import ProcessPoolExecutor def process_one(bvid): # 这里放你的请求和解析逻辑 return bvid, ok if __name__ __main__: bvid_list [BV1xx411c7mD, BV1Q5411w7z5] with ProcessPoolExecutor(max_workers4) as executor: results executor.map(process_one, bvid_list) for result in results: print(result)实际场景里我会把“采集”和“解析”分开用异步并发去抓取接口返回的JSON再用多进程去处理已经落盘的原始数据。这样既快又稳。5.4 限速与重试策略不管用异步还是多进程采集类代码必须有限速和重试机制。最简单的限速方式就是上面那个信号量再加一个随机小延迟模仿真实用户的访问节奏。重试策略我常用指数退避第一次失败等1秒第二次失败等2秒第三次失败等4秒最多重试3次。这个逻辑可以用tenacity库也可以自己写代码很简单import time def fetch_with_retry(func, *args, max_retries3, **kwargs): for attempt in range(max_retries): try: return func(*args, **kwargs) except Exception as e: if attempt max_retries - 1: raise e time.sleep(2 ** attempt)指数退避的好处是既能让服务端有喘息空间又不会在临时故障时直接放弃任务。这个模式在上手爬虫和API调用时都很常用。6. 数据分析与可视化播放量背后的价值6.1 把采集结果变成DataFrame前面的代码已经把数据存到了CSV里现在用pandas读进来分析起来非常顺手。import pandas as pd df pd.read_csv(bili_stats.csv) print(df.head()) print(df.describe())df.describe()会直接给你播放量、点赞、投币等列的平均数、最大值、最小值、分位数。这一步能快速发现数据里有没有异常值比如某个视频的播放量高得离谱或者大部分视频播放量都低于某个水平。6.2 画播放量趋势图解决横坐标太密集的问题假设你采集了一个视频最近30天的播放量数据想画一条折线图。新手最常见的坑就是横坐标日期太多全部挤在一起根本看不清楚。解决办法有两个一个是旋转刻度一个是隔几个显示一个刻度。import matplotlib.pyplot as plt import matplotlib.dates as mdates # 假设 df 中有 date 列和 view 列 df[date] pd.to_datetime(df[date]) df df.sort_values(date) fig, ax plt.subplots(figsize(12, 6)) ax.plot(df[date], df[view], markero, linestyle-) ax.xaxis.set_major_locator(mdates.DayLocator(interval3)) ax.xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) plt.xticks(rotation45, haright) plt.xlabel(日期) plt.ylabel(播放量) plt.title(视频播放量趋势) plt.tight_layout() plt.show()关键就三行DayLocator(interval3)表示每隔3天标一个刻度DateFormatter设定日期的显示格式plt.xticks(rotation45, haright)让文字倾斜45度并且右对齐。这样横坐标再也不会密成一条黑线了。6.3 播放量异常检测思路有了数据可以做一件很有意义的事发现播放量的异常波动。正常的播放量曲线通常是缓慢上升、偶尔出现一个高峰不会是一条标准直线。你可以用简单的规则来做异常检测计算最近7天播放量的均值和标准差如果某一天的播放量超过了均值加3倍标准差就认为是异常点。这个方法的本质是假设正常数据服从正态分布虽然朴素但非常实用。mean_val df[view].tail(7).mean() std_val df[view].tail(7).std() threshold mean_val 3 * std_val abnormal df[df[view] threshold] print(abnormal)这个思路不仅能检测刷量异常也能帮你发现“视频突然被官方推荐了”或“某条引流渠道起了效果”。数据不只是数字它背后是用户行为和平台算法的反馈。7. 常见问题与避坑实录7.1 接口返回code -352或412code为-352时通常说明请求触发了平台的安全验证需要完成验证码或者重新登录。这个不是代码bug是你被风控盯上了。解决办法是立即降低请求频率检查自己的User-Agent是否正常同时确认是不是带了乱七八糟的参数。返回412是更严重的风控信号说明后台已经对你的IP或设备做了限制。第一时间停手别再继续试了。正常情况下公开API是不需要登录的只要频率合理不会遇到这种问题。我见过很多人一看到412就问怎么换IP、怎么绕过风控这种思路我不建议也不是我们这里该讨论的内容。7.2 requests返回403403一般是请求头缺少必要字段。大多数情况下加上User-Agent和Referer就能解决。尤其Referer这个字段很多接口会校验来源页面不带Referer直接返回403带上之后就正常了。7.3 selenium运行时提示驱动版本不匹配这是一个非常常见的坑。Chrome浏览器每隔几周就更新一次老版本的chromedriver就带不动新浏览器了。解决办法是用webdriver_manager它会自动检测当前浏览器版本并下载对应驱动。这也是前面代码里用webdriver_manager的原因。如果你是自己管理驱动就去ChromeDriver镜像站下载和浏览器版本号一致的驱动核心是版本号前几位必须一致。7.4 关于“刷播放量”我的真实看法最后说点掏心窝的话。我见过太多人想走捷径最后反而吃了大亏。网上所谓“稳定刷量脚本”要么是钓鱼木马要么是用了几天就彻底失效真正靠刷量做起来的账号我反正是没见过。我做技术这些年最大的一个体会是所有需要跟风控博弈的事情都不值得投入时间。你做出来的东西越接近真实用户行为花的成本就越高高到还不如把精力花在内容上、花在数据分析上。用Python能做B站相关的事情太多了分析同行视频的数据、监控自己视频的播放量趋势、挖掘选题方向、做自动周报。这套技术学下来你的收获不是“播放量高了”而是真正掌握了一门能吃饭的技能。这条路上的坑我今天基本上帮你踩了一遍剩下的就看你自己怎么走了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进