
简介基于大数据的B站热门视频数据分析与研究毕业设计论文文档面向需要完成相关选题的本专科学生、数据分析爱好者及视频平台运营人员。内容基于Python、Flask框架与Hadoop技术完整阐述了B/S架构系统的设计思路覆盖用户信息管理、注册审核、权限分配、热门视频多维度热度评估、动态榜单更新、视频分类管理与标签优化等模块并结合平台、创作者、行业三个层面分析了应用价值。文档包含中英文摘要、绪论、技术概述等章节结构完整既可作为毕业设计撰写范例也可为视频内容分析项目提供参考。资源包共1个doc文件大小约3.66MB已有68人浏览学习。该文档将大数据分析落地到B站热门视频场景详细展示了研究方法与系统模块划分对同类课题研究和社会化媒体数据分析有直接借鉴意义。1. 用 Python 把 B 站热门视频从“看得见”变成“算得清”这个题目摆在开题答辩现场本质上不是“写一篇论文”而是要在论文之外交付一套能说清楚“热门为什么是热门”的小型数据分析系统。B 站热门视频每天更新一批播放、点赞、投币、收藏、转发、弹幕这些指标堆在一起肉眼只能看出量级大小看不出哪个指标在真正驱动热度。把这套逻辑做成可复现的 Python 工程才是数据分析与数据研究这两个词在这个毕设里的真实分量。做这件事的人一般是数据科学、大数据或软件工程方向的学生也可能是在职工程师拿来练手。工作流并不复杂先把热门视频列表抓下来再逐条取统计详情清洗后放进 DataFrame做相关性分析和综合热度打分最后用图表把结论导出成论文配图。相比推荐系统或自然语言处理方向的毕设它的工程量适中核心风险全在数据质量、字段口径和结果可解释性这三处。下面按我通常会采用的落地顺序展开。2. 先立框架B 站热门视频数据分析系统的分层设计很多毕设翻车不是因为爬虫写不出来而是数据抓完才发现不知道下一步该算什么。拿回几十万行原始记录没有分层设计清洗和分析写在同一段脚本里改一个字段就要全流程重跑。所以第一步不是写 requests而是把系统拆成四层采集层、存储层、分析层、展示层。2.1 数据研究的分析链路采集、清洗、建模、可视化的边界采集层只做一件事把 B 站接口返回的原始报文完整落盘不加工字段语义。存储层建议保留两种形态raw JSONL 是原始证据清洗后的 CSV 或 SQLite 是分析输入。分析层负责去重、缺失值处理、标准化和建模计算结果统一输出成带版本号的中间表。展示层只读取中间表不碰原始数据。这样拆分的好处体现在论文评审环节导师问“你这份图的数据从哪来”你能直接指到数据快照文件而不是含糊说“跑了一下爬虫”。各层之间传递的唯一格式是 DataFrame任何一层出了问题都可以从上一层的输出重新计算不用重抓网络数据。2.2 字段模型B 站视频指标里哪些字段对“热门”真正有用分析系统不是字段越多越好字段太少则结论单薄字段太多则清洗成本失控。参考 B 站公开接口的常见返回结构我一般保留以下字段作为基础指标字段类型含义是否参与建模bvidstring视频唯一编号主键不建模titlestring视频标题文本分析用owner_namestring作者昵称不建模pub_tsint发布时间时间戳时间维度viewint播放量是danmakuint弹幕数是replyint评论数是favoriteint收藏数是coinint投币数是shareint分享数是likeint点赞数是注意两个容易踩坑的点。第一B 站各端接口对字段命名不完全一致有的叫stat.view有的直接平铺落盘前要统一映射。第二pub_ts尽量用原始时间戳不要在地层里就转成北京时间的字符串时区换算放到分析层做。我用过一次性把时间转成字符串的写法后面做小时级分布分析时被迫重新解析白白浪费一晚上。2.3 用 dataclass 把数据定义写死在代码里字段不落到代码里分析脚本就会到处出现魔法字符串。我习惯用dataclass冻结一个视频记录对象同时兼顾类型提示和字段映射。from dataclasses import dataclass, asdict from typing import List dataclass class VideoStat: bvid: str title: str owner_name: str pub_ts: int view: int danmaku: int reply: int favorite: int coin: int share: int like: int classmethod def from_api(cls, item: dict) - VideoStat: 把接口返回的 dict 转成结构化对象同时做字段默认值兜底 stat item.get(stat, {}) return cls( bviditem.get(bvid, ), titleitem.get(title, ), owner_nameitem.get(owner, {}).get(name, ), pub_tsitem.get(pubdate, 0), viewstat.get(view, 0), danmakustat.get(danmaku, 0), replystat.get(reply, 0), favoritestat.get(favorite, 0), coinstat.get(coin, 0), sharestat.get(share, 0), likestat.get(like, 0), ) def to_dict(self) - dict: return asdict(self)这里from_api是关键。接口偶尔会缺失某个统计字段直接item[stat][view]会抛 KeyError用get配合默认值 0 可以保证单条数据异常不会中断整个采集。to_dict用于后续把对象写入 CSV 或 JSONLdataclasses.asdict会自动把嵌套对象递归转成字典。工程目录我会按下面的习惯组织论文里画系统架构图时直接对应到目录层级答辩时很容易讲清楚bilibili_hot_research/ crawler/ # 采集层 hot_list.py video_detail.py storage/ # 存储层 raw/ # jsonl 原始数据 cleaned/ # csv 清洗结果 analysis/ # 分析层 preprocess.py models.py correlation.py heat_score.py visualization/ # 展示层 charts.py dashboard.py main.py # 总入口3. 数据生命线B 站热门视频数据采集与预处理的落地方案架构搭完下一步就是让数据流动起来。这个章节解决两个问题数据从哪个口子拿拿到手之后怎么变成能算的样子。3.1 数据源选型为什么优先读公开 Web API 而不是硬解析网页B 站有多个视频列表入口但“热门视频”这个场景下最可靠的做法是直接请求官方 Web 端公开接口拿到 JSON 后解析字段。为什么不用网页解析因为 HTML 结构改版频繁一个 class 名变了正则和 XPath 全部失效而 JSON 接口的字段相对稳定容错空间更大。采集节奏上我建议低频慢跑。以 300 到 500 条视频为一批每两条请求之间至少停顿 2 到 3 秒一整天跑下来数据量已经足够做分布分析。高频抓取对论文研究没有任何增量贡献反而容易触发风控轻则验证码重则封 IP。作为毕设项目数据合法性说明也要写进论文明确标注采集时间窗口、数据用途仅限于学术研究。3.2 最小采集脚本从热门列表到视频统计详情先取热门视频 ID 列表再逐条请求统计详情接口这个两段式结构比单次拉全量数据更稳。部分场景下热门列表接口只返回视频元信息统计值不全所以video_detail这一步是必要的。import json import time import requests HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: https://www.bilibili.com/, } def fetch_hot_list(page_size: int 50) - list[str]: 拉取热门视频列表返回 bvid 数组 url https://api.bilibili.com/x/web-interface/popular params {ps: page_size, pn: 1} resp requests.get(url, headersHEADERS, paramsparams, timeout10) resp.raise_for_status() data resp.json() if data.get(code) ! 0: raise RuntimeError(f接口返回异常: {data.get(message)}) return [item[bvid] for item in data[data][list]] def fetch_video_stat(bvid: str) - dict: 按 bvid 取视频详情返回原始 JSON url fhttps://api.bilibili.com/x/web-interface/view params {bvid: bvid} resp requests.get(url, headersHEADERS, paramsparams, timeout10) resp.raise_for_status() payload resp.json() if payload.get(code) ! 0: return {} return payload[data] # 主流程取列表 - 逐条取详情 - 写 JSONL bvids fetch_hot_list(page_size50) with open(storage/raw/hot_videos.jsonl, a, encodingutf-8) as f: for bv in bvids: detail fetch_video_stat(bv) if detail: f.write(json.dumps(detail, ensure_asciiFalse) \n) time.sleep(2.5) # 控制请求频率避免高频触发风控这段代码需要重点解释三个参数。第一HEADERS里的Referer在 B 站接口中有实际作用不带可能直接 412 错误User-Agent用常规浏览器标识即可满足多数场景。第二timeout10是必填项不设超时某个接口卡住会让整个采集进程永久挂起写论文数据时一个请求等十分钟没有任何意义。第三time.sleep(2.5)是整段脚本的节奏器它的作用是让单 IP 请求频率保持在一个低水平这也是数据分析采集模块里合法且必要的自我约束。采集过程中如果出现code: -412表示请求被风控拦截正确做法是停下来等待较长时间后再继续而不是换着法去绕过限制。毕设数据采集讲究的是稳定可持续不是短时间梭哈。3.3 清洗与规整把脏字段整理成 Pandas 能直接计算的样子接口数据落到 JSONL 之后还不能直接喂给模型。常见脏数据有三类热门外列表里可能存在重复视频某些视频刚发布不久互动数据为零时间字段是 Unix 时间戳需要转成可读时间。清洗目标是把这些不规则处理成规整的表格。import pandas as pd from pathlib import Path def load_raw_to_frame(raw_path: str) - pd.DataFrame: records [] for line in Path(raw_path).read_text(encodingutf-8).splitlines(): if not line.strip(): continue raw json.loads(line) records.append({ bvid: raw.get(bvid), title: raw.get(title, ), owner_name: raw.get(owner, {}).get(name, ), pub_ts: raw.get(pubdate, 0), pub_time: pd.to_datetime(raw.get(pubdate, 0), units, utcTrue) .tz_convert(Asia/Shanghai), view: raw.get(stat, {}).get(view, 0), danmaku: raw.get(stat, {}).get(danmaku, 0), reply: raw.get(stat, {}).get(reply, 0), favorite: raw.get(stat, {}).get(favorite, 0), coin: raw.get(stat, {}).get(coin, 0), share: raw.get(stat, {}).get(share, 0), like: raw.get(stat, {}).get(like, 0), }) return pd.DataFrame(records) df load_raw_to_frame(storage/raw/hot_videos.jsonl) # 去重bvid 是唯一键 df df.drop_duplicates(subset[bvid], keeplast) # 过滤无效记录视频为空或 bvid 缺失都直接剔除 df df[df[bvid].notna() df[bvid].str.startswith(BV)] # 互动量为空的部分统一切 0 并记录数量便于论文写数据说明 interact_cols [danmaku, reply, favorite, coin, share, like] df[interact_cols] df[interact_cols].fillna(0).astype(int) # 删除播放量为 0 且发布超过 7 天的视频通常是异常记录 df df[~((df[view] 0) (df[pub_ts] pd.Timestamp.now().timestamp() - 7 * 86400))] df.to_csv(storage/cleaned/hot_cleaned.csv, indexFalse, encodingutf-8-sig) print(f清洗完成保留 {len(df)} 条有效记录)这段清洗代码有几个参数和逻辑值得展开。keeplast表示同一条视频重复出现时保留最后一次采集值因为视频的热度指标随时间增长最新抓取的数据更有信息量。astype(int)是隐式截断互动数本身是整数接口返回浮点数的概率极低但统一转类型能避免后续groupby或corr时出现 dtype 不一致的隐患。encodingutf-8-sig是 Windows 场景下的必要设置Excel 打开 CSV 不会乱码。清洗规则的完整记录本身就值得写进论文的“数据预处理”章节评审老师通常关注的不是你清洗了多少行而是你有没有意识到哪些数据不能直接用——这比清洗代码本身更能体现数据研究能力。3.4 B 站数据采集与合规节奏单 IP 下如何把控抓取频率这个话题在论文里容易被忽略但对毕设答辩很关键。B 站热门接口本质上是公共资源作为学术研究可以低频调用但不能把服务端当成数据仓库来拖库。我个人建议把采集控制参数集中定义成常量方便论文方法论部分引用参数推荐值说明单批视频数30~50覆盖热门榜一个页面足够分析请求间隔2.5~5 秒低频稳定避免集中请求单日总请求数≤ 2000达到上限即停止次日再跑超时时间10 秒超过即重试最多重试 3 次重试退避30 秒起步失败后指数退避连续失败则退出这套参数组合跑一个样本周期能拿到几千条独立记录做相关分析和建模绰绰有余。部分同类项目会把频率压到几百毫秒一次短期看起来效率高但一旦触发风控整个数据采集窗口就断了反而耽误论文进度。4. 让视频热度可解释指标标准化、相关性与热度评分数据清洗完接下来的任务是回答研究问题也就是“热门视频为什么热”。这需要从三个角度切入指标之间的关系、综合热度量化、时间维度的影响。4.1 相关性矩阵先看哪两个指标会同时涨跌数据分析项目的第一步通常不是建模而是看相关性。播放量、点赞、投币、收藏、分享这些指标之间存在明显的共生关系比如用户投币前大概率先点赞但收藏的行为逻辑可能不同。import pandas as pd import seaborn as sns import matplotlib.pyplot as plt df pd.read_csv(storage/cleaned/hot_cleaned.csv) model_cols [view, danmaku, reply, favorite, coin, share, like] corr df[model_cols].corr() plt.figure(figsize(10, 8)) sns.heatmap(corr, annotTrue, fmt.2f, cmapRdBu_r, center0, squareTrue, cbar_kws{shrink: 0.8}) plt.title(热门视频指标相关矩阵) plt.tight_layout() plt.savefig(visualization/out/corr_heatmap.png, dpi300)df[model_cols].corr()默认计算 Pearson 相关系数输出结果是 7×7 的对称矩阵。annotTrue会在格子里标注数值方便直接截图放进论文fmt.2f控制保留两位小数。cmapRdBu_r用红蓝色带区分正负相关色调越深代表相关程度越强。解读时重点关注两处。第一view和其他指标的相关系数若普遍在 0.7 以上说明热门视频的互动行为高度同步后续建模中选一两个代表指标即可避免多重共线性。第二若favorite与view的相关性明显低于like与view说明收藏是另一种用户行为可以单独作为“内容价值”维度的代理指标。这个发现写进论文里比罗列数据有说服力得多。4.2 构建“综合热度指数”为什么不能直接算平均分直接对播放量和点赞数做加权平均算热度是很多毕设常见的硬伤。播放量动辄几十万弹幕只有几百量纲差异下加权平均的结果基本被播放量主导其他指标形同虚设。正确做法是先标准化再赋权。import numpy as np def minmax_normalize(series: pd.Series) - pd.Series: Min-Max 标准化到 [0,1] 区间 vmin, vmax series.min(), series.max() if vmax - vmin 0: return pd.Series(0.0, indexseries.index) return (series - vmin) / (vmax - vmin) # 各互动指标标准化后等权相加 weight { view: 0.3, like: 0.2, coin: 0.2, favorite: 0.1, share: 0.1, reply: 0.05, danmaku: 0.05, } score pd.Series(0.0, indexdf.index) for col, w in weight.items(): score w * minmax_normalize(df[col]) df[heat_score] score df.sort_values(heat_score, ascendingFalse, inplaceTrue) print(df[[bvid, title, view, like, heat_score]].head(10))vmax - vmin等于 0 时说明该指标在样本内完全没有变化直接填 0 是稳妥做法避免除零错误。权重设置参考了指标的信息量和业务直觉播放量是热度基本盘权重最高投币比点赞更能代表用户认可度因此coin和like各占 0.2弹幕和回复体现了互动深度但数值波动大权重压低。这个方案的优点是透明可解释论文里可以放公式答辩时能讲清楚每一步。进阶一点的做法是熵值法用指标变异程度自动确定权重避免主观赋权被质疑但也要付出解释成本。毕设阶段我建议先在等权基础上跑通再用熵值法做对比两种结果放一起本身就是一项不错的分析内容。4.3 时间维度的对比热门视频的发布时间与互动率热度不仅取决于视频质量也受发布时间影响。把pub_time拆出小时维度看哪个时段发布的视频更容易获得高互动是数据研究中比较出彩的小发现。df[pub_hour] pd.to_datetime(df[pub_time]).dt.hour df[interact_per_view] ( df[like] df[coin] df[favorite] df[reply] df[danmaku] ) / df[view].clip(lower1) hourly_stats ( df.groupby(pub_hour) .agg( video_count(bvid, count), median_view(view, median), median_interact(interact_per_view, median), ) .reset_index() ) print(hourly_stats.sort_values(median_interact, ascendingFalse).head(8))clip(lower1)把播放量下限设为 1防止新视频播放量为 0 时计算得到无穷大。agg里分别统计了每个小时段内的视频总量、播放量中位数、互动率中位数用中位数而不是平均值是因为热门列表里存在少量爆款数据会显著拉高平均值的代表性。实际分析中我通常会发现下午和晚间时段发布的视频在热门列表中占比更高但互动率未必更高因为那个时段竞争也激烈。这个结论表面简单背后涉及供给和需求双侧的数据逻辑很适合作为论文“数据分析结果”章节的第一个实证案例。4.4 可解释研究让数据研究系统输出 30 秒能讲清的结论数据分析研究系统的最终产出不只是图表还要有几个“一句话结论”。我一般会准备三个固定格式的发现放在论文摘要或答辩 PPT 的显眼位置。研究问题分析方法结论示例口径哪些指标驱动热门相关性矩阵“点赞与播放线性相关最强投币与收藏体现独立价值”如何量化综合热度Min-Max 加权“综合热度评分与单日播放量排名重合度约 70%”发布时间是否有影响小时分组聚合“晚间 18 到 22 时发布视频热门占比明显偏高”示例结论里的具体数字会因采集批次而变化你只需要保留“用什么方法得到什么结论”这个结构。数据研究系统体现的是分析方法和结论的透明性不是造出一个黑盒模型得一个分数。5. 数据可视化与毕设交付的 3 个提效细节到了交付阶段常见做法是论文配图、答辩 PPT、演示系统图表三处各要一套图。如果三个地方分别画图不仅工作量重复还容易因为数据不同步被评委抓住问题。下面三个技巧可以避免这种尴尬。5.1 一次计算多处渲染用同一份中间表出图所有图表一律从analysis/输出的结果表读取论文配图和 ECharts 大屏共用同一份 CSV 或 JSON。具体的做法是在分析层最后导出analysis/out/chart_data.json可视化脚本只消费这个文件。chart_json { corr: corr.round(2).values.tolist(), fields: model_cols, heat_score_top10: ( df.head(10)[[title, heat_score, view]] .to_dict(orientrecords) ), } with open(analysis/out/chart_data.json, w, encodingutf-8) as f: json.dump(chart_json, f, ensure_asciiFalse, indent2)to_dict(orientrecords)输出的记录列表可以直接被 Pyecharts 或 ECharts 读取indent2方便人工检查。这样论文里的表格、答辩 PPT 里的柱状图、演示系统的数据大屏都引用同一份计算结果任何一次数据更新只需重跑分析流程。5.2 中文乱码与字体缺失的通用处理Matplotlib 默认字体不支持中文是可视化环节最常见的报错。plt.title里的中文标题输出成方块论文里不能用反复修改又会浪费时间。import matplotlib matplotlib.rcParams[font.sans-serif] [Microsoft YaHei, SimHei, Arial Unicode MS] matplotlib.rcParams[axes.unicode_minus] False把这两行放在可视化脚本首部axes.unicode_minus设为 False 是为了同时解决负号显示异常。不同操作系统下字体名有差异Windows 通常用Microsoft YaHeimacOS 用Arial Unicode MSLinux 服务器上需要确认是否有中文字体包没有的话提前安装。5.3 数据快照与随机种子让毕设统计结果可复现评审后修改建议最常出现在“结论是否可以复现”这一条。你在 3 月跑的结论5 月答辩前如果想重新演示一遍数据可能已经完全不同采集接口和榜单都变了。解决办法是对采集样本做快照冻结。SNAPSHOT_VERSION 20250601 df.to_csv(fstorage/cleaned/hot_cleaned_{SNAPSHOT_VERSION}.csv, indexFalse, encodingutf-8-sig)论文里写明分析基于哪个快照版本答辩现场演示时指定读取该版本文件不重新拉取数据。分布在论文各章节的图表都在图注中标注“数据来源2025-06-01 采集快照”。这个方法能直接向评审证明研究结论的稳定性也避免演示当天网络或接口异常导致的翻车。数据分析系统的一个重要加分项就在这个细节上——它让你的研究过程和结果都能被验证。本文还有配套的精品资源点击获取