
简介这是一套面向Python爬虫与数据分析初学者的B站番剧数据分析与可视化资源包也适合用作毕设或课程设计参考。项目以哔哩哔哩番剧为对象从总榜抓取动漫基本信息再进入详情页提取追番人数、评分等核心字段用于分析历年番剧热度与口碑变化并覆盖爬虫、数据清洗、探索分析与可视化全流程。压缩包共23个文件约3.58MB核心内容包括3个ipynb代码文件、2个xlsx数据文件、10个png可视化图表和1份md说明文档另有少量工程配置文件md文档对爬虫细节、数据处理与分析思路做了系统介绍。资源目前已有2976人学习下载。对于初学者可以直接从代码中了解从总榜到详情页的爬取逻辑、数据清洗与图表生成思路同时附带的爬取数据集和结果图也能省去重复采集时间结合说明文档中的细节讲解能较好地用于课程设计或入门实践。 做内容运营和数据分析的这几年我越来越发现一个事实B站动漫区的数据是全站最有分析价值的“富矿”之一。番剧的热度趋势、观众的互动习惯、评分走势与播放量之间的关系这些东西并不像表面看上去那么感性背后全是可量化的规律。而想拿到这些数据并用图表讲清楚“这部番为什么火了”“那部番为什么高开低走”靠手动截图和肉眼对比根本不现实。所以我做了这么一件事用Python写了一套针对B站动漫数据的采集与分析流程从请求番剧列表、拉取评分和播放数据到清洗入库最后用可视化图表把结论直接摆出来。整套流程跑下来不管你是想研究番剧市场、做自媒体选题参考还是单纯想练手Python爬虫和数据分析都很有借鉴价值。这篇文章就把完整的实现思路、关键代码、还有我踩过的坑一次讲清楚。1. 项目背景B站动漫数据里到底藏着哪些可分析的信息先说清楚一个前提做数据分析最怕的就是“拿到一堆数据但不知道拿来干嘛”。在动手写爬虫之前我先把B站动漫数据能回答的问题列了一遍确认了分析方向才去反推需要采集哪些字段。1.1 从观众视角到数据视角的转变作为一个常年追番的观众我关心的是“这部番好不好看”。但作为一个数据分析者我需要把“好不好看”拆解成可以度量的指标热度指标播放量、追番人数这两个指标反映的是作品的传播广度。互动指标弹幕数、评论数反映的是观众的表达欲望和沉浸程度。口碑指标评分反映的是作品质量和观众满意度的综合结果。时效指标开播时间、完结时间用于分析不同时段作品的竞争环境。这几个维度单独看各有局限但组合起来能回答很多有意思的问题。比如“播放量高的番剧评分一定高吗”再比如“弹幕密度和番剧类型有没有关系”。1.2 明确采集对象和分析边界这里要特别说明一下我分析的是B站动漫区的番剧动画作品数据不是UP主上传的普通视频。两者的数据接口完全不同番剧有独立的评分体系、追番人数和分P结构分析价值也更高。我给自己圈定的采集范围包括番剧名称、类型标签、开播时间、更新状态、播放量、追番人数、弹幕总数、评分、评分人数。这套字段既能支撑基础的热度排行也能做更深的相关性分析数据量不大但足够反映规律。建议第一次做这个项目的人不要贪多。先把核心字段采集干净跑通全流程再考虑扩展评论内容、弹幕文本这些非结构化数据。2. 数据采集方案Requests请求API接口与关键参数解析确定了要什么数据接下来就是怎么拿的问题。很多新手一上来就想用Selenium模拟浏览器这在小规模采集中完全没必要徒增性能开销和封号风险。B站的网页端本身就在调用JSON接口直接用Requests模拟这些接口效率和稳定性都高得多。2.1 锁定数据源网页接口优于页面解析我在浏览器开发者工具里抓包找到了几个关键接口番剧列表页接口返回番剧的id、标题、封面、开播时间和状态等基础信息。番剧详情接口返回播放量、追番人数、弹幕总数、评分等核心指标。分类筛选接口可以按动画类型、年份、季度等维度过滤数据。这些接口返回的都是结构清晰的JSON数据相比从HTML里用XPath或者BeautifulSoup提取信息解析成本低也不容易因为页面改版而失效。2.2 请求头的伪装艺术B站对请求头做了一定校验直接裸用Requests默认头大概率会失败或者返回异常结果。我实测下来最核心的是这三项headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.bilibili.com/, Origin: https://www.bilibili.com }其中Referer尤其重要B站的部分接口会校验这个字段不带上很可能拿不到数据。刚开始我没注意请求详情接口时返回了-412状态码排查半天才意识到是Referer的问题。另外有个容易被忽略的点B站部分接口对未登录用户做了访问频率限制如果只是采集几百条番剧数据不登录也能跑通。但如果采集规模大建议在Cookie里带上登录凭证实测能明显降低触发风控的概率。2.3 分页和参数完整性问题番剧列表不是一次请求就能拿全的B站接口通常采用pn页码和ps每页数量来控制翻页。我一开始用的是ps50后来发现部分类型下数据量太大有些页面的返回结果会出现字段缺失于是改成了ps20搭配循环翻页采集稳定很多。import requests import time def fetch_season_list(params): url https://api.bilibili.com/pgc/web/season/list resp requests.get(url, paramsparams, headersheaders, timeout10) if resp.status_code 200: data resp.json() if data.get(code) 0: return data[data].get(list, []) return []这里特别提醒一定不要忽略返回包里的code字段。B站接口正常返回时code为0如果遇到-412代表请求被风控拦截-404通常是参数错误。把这些异常状态码记录下来比直接抛异常要实用得多因为采集是批量行为个别失败是常态需要的是容错而不是中断。3. 数据清洗与分析逻辑播放量、追番数、评分的关联洞察数据拿到手只是第一步B站的原始JSON字段和最终分析需要的数据格式之间还有一堆脏活要做。这一部分如果不处理干净后面的图表全都会失真。3.1 字段单位统一和类型转换B站返回的数据里播放量、追番数等字段有两种情况一种是纯数字比如1234567一种是带单位的人工可读字符串比如123.4万。我在采集中发现不同接口返回的格式不一致这就需要在清洗阶段统一转成纯数字。def convert_count(value): if isinstance(value, (int, float)): return float(value) if isinstance(value, str): if 万 in value: return float(value.replace(万, )) * 10000 if 亿 in value: return float(value.replace(亿, )) * 100000000 return float(value) return 0.03.2 时间字符串转标准日期格式开播时间、完结时间在JSON里往往是2024-04-07 00:00:00这样的字符串。如果要做“某年某季上线的番剧平均播放量”这类聚合分析字符串格式没法直接按时间排序和分组必须转成datetime对象。from datetime import datetime def parse_time(time_str): try: return datetime.strptime(time_str, %Y-%m-%d %H:%M:%S) except (ValueError, TypeError): return None这里要注意空值和异常格式的处理我在实测中发现有少量番剧的开播时间字段是空的不做兜底处理整个清洗流程就会中断。3.3 从数据中提炼有意义的分析维度数据清洗完成后我按自己设定的分析目标做了几类探索全站番剧播放量Top20看看真正的头部内容长什么样验证“热门番剧是否集中在特定类型”。播放量、追番数、弹幕数、评分的相关性矩阵用相关系数确认哪些指标之间存在强关联。这一步特别有意思因为结果和很多人的直觉可能不一样。按季度分组的热度走势分析一年中哪些档期更容易出爆款。以相关性分析为例代码并不复杂import pandas as pd df pd.DataFrame(all_data) corr df[[play_count, follow_count, danmaku_count, score]].corr() print(corr)我跑出来的结果显示播放量和弹幕数的相关性比播放量和评分的相关性更高。这说明B站观众的“边看边发弹幕”行为更多是被话题性和情绪带动的而不是单纯由作品质量决定。换句话说高播放量的番剧不一定分高但大概率弹幕热闹。这一发现对我后续做内容运营很有启发如果一部番的目标是拉新和制造话题宣发资源可以重点投放在弹幕氛围活跃的作品上如果目标是口碑沉淀评分权重就得拉高。4. 可视化呈现制作直观反映热度与口碑的图表组合分析得再深入最后都要落在图表上让人一眼就能看懂结论。可视化环节我选择的是pyecharts原因是它生成的图表是交互式的可以缩放、悬停查看数值在博客或者汇报场景里展示效果比静态的matplotlib好不少而且它也是国内数据分析场景里很主流的可视化方案社区资料丰富遇到问题好查。4.1 播放量Top20横向条形图排名类数据最适合用横向条形图因为番剧名称通常较长横向布局能完整展示名字纵向条形图则容易挤压遮挡。from pyecharts.charts import Bar from pyecharts import options as opts def plot_top20(data): data data.sort_values(play_count, ascendingFalse).head(20) bar ( Bar() .add_xaxis(data[title].tolist()) .add_yaxis(播放量(万), [round(v/10000, 1) for v in data[play_count].tolist()]) .set_global_opts( title_optsopts.TitleOpts(titleB站动漫区播放量Top20), xaxis_optsopts.AxisOpts(name播放量(万)), yaxis_optsopts.AxisOpts(name番剧名称) ) ) return bar有个细节容易踩坑pyecharts的默认背景和字体在深色主题下会看不清发布前建议先本地渲染成HTML预览一遍必要时用set_global_opts里的init_optsopts.InitOpts(bg_colorwhite)强制白色背景。4.2 指标相关性热力图相关性矩阵用热力图展示是最直观的颜色深浅直接代表相关性强弱。from pyecharts.charts import HeatMap def plot_corr_heatmap(corr_matrix): x_labels corr_matrix.columns.tolist() y_labels corr_matrix.index.tolist() data [] for i, y in enumerate(y_labels): for j, x in enumerate(x_labels): data.append([j, i, round(corr_matrix.loc[y, x], 2)]) heatmap ( HeatMap() .add_xaxis(x_labels) .add_yaxis(相关系数, y_labels, data) .set_global_opts( title_optsopts.TitleOpts(title播放量、追番数、弹幕数、评分相关性), visualmap_optsopts.VisualMapOpts(min_-1, max_1) ) ) return heatmap热力图有一个要特别注意的问题相关系数的取值范围是-1到1视觉映射必须显式设置min_-1, max_1否则默认范围会基于当前数据动态调整导致颜色失真读者容易误判相关强度。4.3 热度走势折线图按季度聚合时间序列数据用折线图能清晰看出番剧热度的周期性规律。from pyecharts.charts import Line def plot_trend(df): df df.groupby(season).agg({play_count: mean}).reset_index() line ( Line() .add_xaxis(df[season].tolist()) .add_yaxis(平均播放量, [round(v/10000, 1) for v in df[play_count].tolist()]) .set_global_opts( title_optsopts.TitleOpts(title不同季度上线番剧的平均播放量走势), xaxis_optsopts.AxisOpts(name季度), yaxis_optsopts.AxisOpts(name平均播放量(万)) ) ) return line这里我做了一个关键的数据处理选择按季度分组而不是按月份分组。原因是我采集的样本量有限按月分组会导致每个时间点上的番剧数量太少平均值波动非常大很难呈现规律。季度粒度更粗但每个分组下的样本更充足趋势更平滑规律也更明显。5. 完整项目流程与实操心得从采集到展示的链路复盘最后把整套项目串起来看它其实是一个很标准的“采集-清洗-分析-展示”数据链路。我也把踩过的一些坑集中列出来给准备复现的朋友做个参考。5.1 整体流程梳理明确分析目标列出需要回答的问题反推需要采集的字段。抓包分析接口找到番剧列表和详情接口确认返回的数据结构。编写采集脚本用Requests请求接口带好伪装请求头做好翻页和异常捕获。数据清洗入库统一数字单位、解析时间字段、处理空值保存为DataFrame结构。探索性分析做排序、分组、相关性计算提炼结论。可视化展示用pyecharts生成交互图表把分析结论直观呈现出来。5.2 请求频率控制这是整个项目里最容易被忽略但最重要的一环。B站的接口虽然不像支付系统那样有极高的安全等级但短时间高频请求一样会触发风控。我实测下来比较稳妥的策略是每次请求之间间隔0.5到1秒每采集100条数据后暂停5秒。整个采集过程几千条数据跑下来耗时在10分钟左右完全可接受而且没有触发过一次风控。import time def safe_request(func, *args, **kwargs): time.sleep(0.8) for retry in range(3): try: return func(*args, **kwargs) except requests.RequestException as e: time.sleep(2 * (retry 1)) return None5.3 错误处理和日志记录批量采集时最忌讳的是脚本一遇到错误就停下来。我建议所有请求都套上异常捕获把失败的URL和参数写入日志文件跑完后再针对性补采。这样既不会因为个别数据缺失影响整体流程也不会让脚本跑一次就要全程盯着。5.4 个人实操体会这个项目做成之后我最大的感受是爬虫技术的难点从来不在写代码而在于对目标平台数据结构的理解和对边界规则的尊重。B站的数据接口是半开放的我们做小规模的数据分析完全可行但大规模抓取显然不合适既给平台服务器造成压力也容易触发法律风险。所以我想说的是做这类项目一定要把握好度。采集数据前先评估需求规模能用接口解决的就不要用页面解析能小规模采样的就不要全量爬取。数据分析的价值在于发现规律而不在于拥有多少数据量。另外一个小技巧B站的部分接口返回的JSON里隐藏着一些页面不展示的字段比如某些内容的推荐权重值、标签的层级关系。这些字段往往能带来额外分析价值建议拿到原始JSON后先完整打印出来看一遍再写解析逻辑有时候会有意外收获。最后的一点延伸想法如果你已经能稳定采集和分析番剧数据下一步可以考虑把评论和弹幕内容也纳入分析做情感倾向或热门话题词云。非结构化文本数据会带来完全不同的视角比如观众的吐槽点集中在剧情、作画还是声优这些信息在结构化指标里是看不到的。不过那就需要引入结巴分词、词频统计等新的技术组件了。项目做到这里已经是一个不错的起点剩下的扩展方向可以按自己的兴趣和需求慢慢加。本文还有配套的精品资源点击获取