
既然要做“基于大数据的Bilibili青少年模式使用情况的数据分析系统”这种毕设项目我得先跟你说句实在话这题出得挺巧的。既有技术深度可以挖又有社会话题可以做文章答辩的时候比较好讲。B站本身就是年轻人聚集地青少年模式又是平台治理的热门方向把这两个结合起来做数据分析导师不会觉得你在水反而会觉得你对热点有敏感度。下面我把这个题目从选型、设计到落地的完整思路以及源码、论文、部署文档这些交付物的写法一次性都给你捋清楚。1. 这个毕设到底在做什么1.1 题目拆解与核心需求定位先把题目拆开看“基于大数据”、“Bilibili青少年模式”、“使用情况”、“数据分析系统”这四个词串起来核心任务就一句话爬取B站青少年模式下的公开内容数据清洗之后做多维度的统计分析和可视化展示最后交付一个可运行的系统。很多同学见到“大数据”三个字就慌以为要搭集群、配Hadoop、天天跟Spark打交道。其实本科阶段的毕设重点在于“完整的技术链路”而不是“多大规模的数据量”。你只要能证明自己掌握了从采集、存储、清洗、分析到展示的全流程数据量在十万条级别其实就足够了。真要你把千亿级数据跑在集群上你实验室那几台机器也撑不住所以别自己吓自己。这个题目还有一个隐含优势B站的数据是结构化程度很高的视频信息、UP主信息、播放数据都有明确的字段定义不像文本情感分析那样需要消耗大量时间在预处理上。你拿到手的原始数据基本是JSON结构解析之后可以直接入库这对后续的分析环节省了太大力气。1.2 为什么这个题目在答辩时有天然优势我见过太多毕设题目什么“基于SpringBoot的图书管理系统”、“基于Java的酒店预订系统”这种题目的通病是技术含量看着低、扩展点少。你的题目不一样它有四个明显的答辩加分项第一数据来源是真实且动态的。B站的内容每天在更新你的系统跑一次得到的结果可能就不同,这就意味着你在演示的时候可以现场展示数据采集的过程说服力很强。第二分析维度有社会意义。青少年模式使用情况这本身是一个内容治理命题你可以做时间维度上的内容曝光趋势分析也可以做不同分区的资源分布对比这些结论有现实参考价值评委愿意听。第三技术栈覆盖面广。爬虫、数据处理、数据库、可视化、前后端每一层都有东西可以讲论文也能写出章节感。第四可扩展性强。如果评委问“你还能做什么”你可以说加上时间序列预测、内容分类模型、用户画像等等显得有前瞻性。2. 技术选型与整体架构设计2.1 技术栈选型为什么我建议用Python全家桶技术选型这件事原则上就一条用你最有把握的而不是最新最炫的。B站相关的大数据毕设项目Python全家桶是绝对的主流原因很简单爬虫阶段Requests请求库 BeautifulSoup/JSON解析语法简单出现问题也好调试。Scrapy虽然功能强但学习成本高考虑到本科生毕设周期不建议自己给自己加戏。数据清洗Pandas是无可替代的处理表格型数据的一套API从读取到过滤、聚合、去重、采样覆盖全部日常操作。数据分析基础的统计分析用Pandas就够如果展示分析深度可以叠加NumPy做数值计算。可视化首选ECharts图表类型丰富、交互性好、起视觉效果好评审老师看着会觉得很专业。PyECharts可以让你用Python代码直接生成HTML片段接入Flask之后动态返回图表配置比前端手写JS省力得多。后端框架Flask轻量、理解成本低一个py文件就能启动服务做毕设级的数据展示后端绰绰有余。数据库MySQL主流、稳定、BI工具和可视化库都有现成连接驱动论文里写出来也好看。这套组合在毕设中最大的好处是出图快、调试方便。你写了一个Pandas聚合函数马上能在Jupyter里跑出结果确认没问题再搬到Flask里每一层都不需要来回切换语言思维。2.2 系统分层采集、存储、分析、展示四层架构整个系统按功能可以拆成四个层次这也是数据类项目通用的分层方式写论文的时候直接拿来做系统架构图层次功能技术选型产物数据采集层爬取B站公开接口数据Python Requests 多线程原始JSON文件数据存储层结构化存储采到的数据MySQL或SQLite视频表、分区表、指标表数据分析层清洗、聚合、统计、关联分析Pandas NumPy图表JSON数据、指标结果数据展示层Web界面查看分析结果Flask ECharts可视化大屏页面这样分层的好处是每一层都可以独立测试。比如存储层出问题了你直接用Navicat查表就能定位不需要从前端一路排查下去分析层算法想调整你单独跑一个脚本就行不用动Web服务。2.3 功能模块划分与数据流走向系统的功能模块大致分为五个数据采集模块、数据管理模块、分析计算模块、可视化展示模块、系统管理模块。数据流是这样的采集脚本通过B站公开接口拿到视频元数据和分区信息先落到本地JSON备份然后清洗去重再写入MySQL。后端服务从数据库读数据交给分析引擎做聚合计算结果以JSON格式传给前端页面前端通过ECharts渲染成图表。这里有一个容易被忽略的点清洗后的数据应该单独落一份到表里不要覆盖原始数据。为什么因为你后期如果要调整清洗策略或者发现某个字段解析有误原始数据还在重新清洗一遍就行。我见过好几个同学把原始数据直接覆盖掉后面论文里要补数据口径的时候找不到源头非常狼狈。3. 数据模型设计与数据库表单设计3.1 核心数据表结构设计数据库设计是整个项目的地基表建得合理后面分析的时候SQL都不知道省多少事。以视频信息表为例核心字段如下CREATE TABLE video_info ( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, bvid VARCHAR(64) NOT NULL COMMENT B站视频唯一标识, title VARCHAR(512) NOT NULL COMMENT 视频标题, tid INT NOT NULL COMMENT 分区ID, tname VARCHAR(64) NOT NULL COMMENT 分区名称, owner_name VARCHAR(128) NOT NULL DEFAULT COMMENT UP主昵称, pub_time DATETIME NOT NULL COMMENT 发布时间, duration INT NOT NULL DEFAULT 0 COMMENT 视频时长秒, view_count INT NOT NULL DEFAULT 0 COMMENT 播放量, danmaku_count INT NOT NULL DEFAULT 0 COMMENT 弹幕数, like_count INT NOT NULL DEFAULT 0 COMMENT 点赞数, coin_count INT NOT NULL DEFAULT 0 COMMENT 投币数, favorite_count INT NOT NULL DEFAULT 0 COMMENT 收藏数, share_count INT NOT NULL DEFAULT 0 COMMENT 分享数, reply_count INT NOT NULL DEFAULT 0 COMMENT 评论数, is_teenager_mode TINYINT NOT NULL DEFAULT 0 COMMENT 是否青少年模式可见, PRIMARY KEY (id), UNIQUE KEY uk_bvid (bvid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT视频信息表;注意到两个设计细节一个是bvid设置了唯一索引。同一视频在采集过程中可能被重复请求到没有唯一索引的话去重就得靠程序判断效率低且容易出错。有了唯一索引插入时用INSERT IGNORE就可以自动忽略重复数据。另一个是is_teenager_mode字段。这个字段的设计思路是在爬取时分别请求青少年模式入口和普通模式入口同一个视频如果在两种模式下都出现了就标记为1这样就为后续做模式对比分析留好了钩子。3.2 采集任务与数据字典管理除了业务数据表系统里还应该有一张采集任务表记录每次采集的执行时间、采集范围、任务状态、成功和失败数量。这张表的作用有两个一是论文里写“系统的数据更新机制”的时候有据可依二是定位爬虫问题的时候可以快速查到是哪个批次的数据出了问题。CREATE TABLE task_record ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, task_name VARCHAR(128) NOT NULL COMMENT 任务名称, task_type TINYINT NOT NULL DEFAULT 0 COMMENT 采集类型0-分区1-关键词搜索2-U主投稿, start_time DATETIME DEFAULT NULL, end_time DATETIME DEFAULT NULL, total_count INT DEFAULT 0 COMMENT 请求总数, success_count INT DEFAULT 0 COMMENT 成功数, failed_count INT DEFAULT 0 COMMENT 失败数, status TINYINT DEFAULT 0 COMMENT 状态0-运行中1-完成2-异常, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;数据字典这块建议在论文附录里放一张字段说明表把每个字段的含义、类型、来源、取值逻辑写清楚。论文评阅老师有时候不会仔细看你的代码但一定会看数据字典——这是判断你系统设计是否规范的重要依据。4. 数据采集实现B站公开接口的接入与调优4.1 采集入口怎么找分区分页与检索接口B站的数据采集优先推荐使用官方开放接口而不是直接解析网页HTML。网页解析有两个痛处一是B站页面是动态渲染的有些数据藏在JS接口里你得开着浏览器开发者工具一个一个找二是网页结构经常微调今天能用的Selector明天可能就失效了。推荐的做法是直接调用B站的内容分区列表接口。以科技分区为例通过浏览器开发者工具请求https://api.bilibili.com/x/web-interface/newlist带上rid参数分区ID、pn参数页码、ps参数每页数量以及必要的Cookie返回的JSON里就带了视频列表的完整信息包括bvid、title、pub_time、duration、owner_name等核心字段。import requests def fetch_video_list(rid: int, page_num: int 1, page_size: int 20, cookies: str ) - list: url https://api.bilibili.com/x/web-interface/newlist headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.bilibili.com/, } params { rid: rid, pn: page_num, ps: page_size, } # cookies参数建议动态维护避免过期 if cookies: headers[Cookie] cookies resp requests.get(url, headersheaders, paramsparams, timeout10) resp.raise_for_status() data resp.json() if data.get(code) 0 and data.get(data): return data[data].get(archives) or [] return []这里提醒一下接口只返回当前分类下的“新投稿”按时间排序的数据如果你需要按播放量排序可以考虑热门列表接口/x/web-interface/popular。来得及的话建议两个接口都采采集的时候记录数据来源分析的时候还可以对比“热门内容”和“新内容”在青少年模式下的差异。4.2 新增数据采集增量采集如果需要采集动态新增的数据追加采集增量更新是很常见的方式。在实现上可以利用B站搜索接口索引最近更新视频或者定期对已收藏UP主的主页进行扫描。这里给出一种最直接的增量方案按更新时间刷新列表。具体思路是每次采集前先查询数据库里最新的视频pub_time然后从那个时间节点开始向后翻页采集遇到时间早于游标的记录就停止。这种方式实现起来比较简单伪代码如下def incremental_fetch(rid, latest_time): page 1 while True: videos fetch_video_list(rid, page_numpage) if not videos: break oldest_time min(v.get(pub_time_as) for v in videos) if oldest_time latest_time: videos [v for v in videos if v.get(pub_time_as) latest_time] save_to_db(videos) break save_to_db(videos) page 1 time.sleep(1)增量采集的意义不仅是省流量还能让系统日志里显示“本次新增XX条数据”在毕业答辩现场演示的时候这个数字是动态的观看体验很像真实的运营后台。4.3 反爬思路与请求频控的取舍说到B站爬虫很多人第一反应是“会被封”。其实B站对普通频率的请求是比较宽容的毕设项目不是生产环境不需要追求速度只需要保证稳定采集。我这里有两个原则第一单线程优先频控放宽。你不需要用多线程把采集速度提高到每秒几十个请求——数据量也就几万条按单线程每秒1个请求的速度两三个小时也能采完。把time.sleep()设置在0.5~1.5秒之间加一点随机抖动就非常安全了。第二Collection与Cookie策略。B站部分接口对未登录和已登录的返回内容会有差异。建议准备一个账号的Cookie放在配置里请求的时候带上能访问到的数据更全。不要用高频的账号直接跑万一触发风控换个账号再试即可。4.4 异常重试与日志埋点采集过程最怕的是跑到一半抛异常程序停了你还不知道停在哪。所以要实现两个机制一是单条请求异常重试连续失败超过3次就跳过记录失败原因二是本地日志输出每完成一个分区、翻到第几页都把进度打印出来。import time import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) def fetch_with_retry(url, params, headers, max_retry3): for attempt in range(1, max_retry 1): try: resp requests.get(url, paramsparams, headersheaders, timeout10) if resp.status_code 200: return resp.json() except Exception as e: logging.error(f请求异常: {e}, 第{attempt}次重试) time.sleep(2 * attempt) return None这一套做下来你在论文中可以写“系统具备健壮的容错机制”答辩的时候也能答上来“程序挂了怎么办”这类问题。5. 数据清洗与特征工程建设5.1 数据清洗的三个核心环节采集下来的原始数据不能直接用至少要经过三个步骤第一步字段抽取与类型转换。接口返回的JSON里嵌套层级很深比如owner字段是个对象里面才有name。直接用Pandas读JSON会发现列层次混乱所以前期先拍平只抽自己需要的字段。import pandas as pd def transform_archive(item: dict) - dict: return { bvid: item.get(bvid), title: item.get(title), tid: item.get(tid), tname: item.get(tname), owner_name: item.get(owner, {}).get(name), pub_time: pd.to_datetime(item.get(pub_date, None), units, errorscoerce), duration: item.get(duration, 0), view: item.get(stat, {}).get(view, 0), like: item.get(stat, {}).get(like, 0), coin: item.get(stat, {}).get(coin, 0), favorite: item.get(stat, {}).get(favorite, 0), share: item.get(stat, {}).get(share, 0), reply: item.get(stat, {}).get(reply, 0), danmaku: item.get(stat, {}).get(danmaku, 0), }第二步去重与全角半角统一。标题里可能混入全角逗号、空格等字符直接str.strip()处理一遍。bvid重复的直接drop_duplicates()。第三步异常值过滤。播放量为0、发布时间在1970年附近之类的数据大概率是占位数据需要过滤掉。这一步会让数据量缩水但留下来的数据才是有分析价值的。5.2 青少年模式识别怎么判断“可见性”“青少年模式使用情况”这个题眼核心是你要能识别出一个视频在青少年模式下是否可见。做法是这样的B站有一部分视频在青少年模式下会被限制本质上是内容白名单/黑名单机制。你可以构造两套请求——一套不带青少年模式参数请求普通列表一套通过B站的青少年模式首页接口请求受限列表然后对比两者的bvid集合。对比逻辑同时出现在两个集合中普通可见 青少年可见只出现在普通集合中青少年模式不可见这个“可见/不可见”的二元标记是整个系统的核心特征。后续分析哪些分区、哪些类型的视频更容易被限制、各分区青少年内容覆盖率是多少都依赖这个标签。5.3 新增统计特征互动率、完播率估算、内容时长分层除了原始字段建议构造几个有分析价值的衍生特征。这些特征在论文里可以单独成为一章节展示你做了“特征工程”的工作。互动率的计算公式video[interact_rate] ( (video[like_count] video[coin_count] video[favorite_count] video[reply_count]) / (video[view_count] 1) # 1防止除零 )这个指标衡量的是“每播放一次会带来多少互动”比单纯看播放量更能反映内容质量。时长分层可以做离散化小于60秒的算短视频60到600秒的算中视频大于600秒的算长视频。为什么这个分层有用因为在分析B站青少年模式的内容生态时时长的分布能反映内容形态的偏向比如知识区可能中长视频偏多娱乐区短视频偏多。这种结论在论文里一写就是“发现”特别适合拿来当分析章节的小标题。6. 数据分析维度与可视化大屏设计6.1 分析维度的设计方案数据分析这件事不怕维度多就怕没逻辑。建议按照“基础描述—对比分析—趋势分析—关联探索”四个层次组织维度基础描述统计青少年模式下可见视频的总数、日均新增量、播放量均值中位数、互动量Top10榜单。分区对比分析不同分区的视频数量占比、可见率对比、平均播放量对比、互动率对比。这个维度是整个系统最出彩的因为用堆叠柱状图和雷达图展示分区生态结构差异视觉效果特别直观。时间趋势分析按周/月聚合新增视频数量、播放量走势判断青少年内容供给的波动情况。这里要注意区分发布时间和采集时间别把两个时间字段搞混了。内容特征分析时长分布直方图、标题词频TopN用jieba分词、UP主发布频率TopN等丰富分析维度。6.2 可视化图表与ECharts的接入细节可视化大屏建议设计成上下左右布局顶部系统标题 数据总览关键指标四个Statistic Card左侧分区可用率的横状条形图中间播放量/互动量日趋势的折线图 时长分布直方图右侧可见性对比的饼图 热门视频Top10排行榜底部数据更新日志滚动列表ECharts接入的常规姿势是通过PyECharts生成HTML片段再嵌入Flask渲染的页面中。比如做一个热门视频Top10的柱状图from pyecharts import options as opts from pyecharts.charts import Bar def generate_top10_bar(data): bar ( Bar() .add_xaxis(data[title].tolist()) .add_yaxis(播放量, data[view_count].tolist()) .set_global_opts( title_optsopts.TitleOpts(title青少年可见视频播放量TOP10), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)), ) ) return bar.render_embed()拿到render_embed()返回的HTML代码后用|safe过滤器插到Flask模板里即可一行搞定不需要前端跟JS折腾。6.3 分析结果如何体现在论文里论文的数据分析章节不能只贴一堆图表关键是把“数据结论”写出来。比如我的项目里跑出来一个结论知识区在青少年模式可见比例约为80%而舞蹈区只有20%互动率排名第一的内容类型不是知识类而是动画类的MAD/AMV作品。这种结论必须配上数据支撑加上一句“这说明青少年模式在供给内容类型上存在显著的分区差异建议优化调节机制”——这就是你的论文里“建议”章节的天然素材根本不用苦想怎么写。7. 系统部署与交付源码、论文、部署文档的安排7.1 本地/Docker部署的两种方式一类部署方式是本地直接运行。因为技术栈是FlaskMySQL这样的轻量组合对机器的要求很低。你需要做的事情包括安装Python 3.9、MySQL 8.0执行requirements.txt安装依赖初始化数据库表结构运行主程序。把这四步写清楚部署文档就完成了60%。注意requirements.txt一定要固定版本flask3.0.3 requests2.32.3 pandas2.2.3 pymysql1.4.6 pyecharts2.0.5另一类是Docker部署。如果你熟悉Docker用docker build把它打包成镜像在论文里可以写“系统支持容器化一键部署”。不过需要提醒的是打包Docker镜像的时候PyECharts生成的图表文件路径要处理好别让容器里找不到模板。7.2 部署文档的标准结构与一套可复用的框架好的部署文档不是“操作手册”而是“从零到一的还原步骤”。建议按这个顺序写项目背景与系统架构简介让读者理解你在部署什么环境准备清单软件版本、硬件建议、端口占用检查数据库初始化建库SQL脚本路径、字符集设置应用启动步骤前端和后端如果有分开的分别写清楚配置项说明数据库连接、Cookie、采集参数常见问题排查端口被占用、数据库乱码、依赖安装失败部署文档一定要自己照着走一遍我就是吃过亏的。当年我给别人写部署文档写完直接发出去结果对方运行不起来我远程一看原来我没写“将.env.example复制为.env”。这种低级错误会让你的文档可信度大打折扣。7.3 源码目录结构与代码规范源码的目录结构建议按功能模块划分清晰project/ ├── app.py # Flask应用入口 ├── config.py # 全局配置 ├── requirements.txt # 依赖清单 ├── modules/ │ ├── crawler/ # 采集模块 │ ├── analyzer/ # 分析模块 │ ├── storage/ # 数据库操作 │ └── visualizer/ # 可视化生成 ├── templates/ # 前端模板 ├── static/ # 静态资源 ├── docs/ # 部署文档、接口说明 └── data/ # 原始数据与备份代码命名方面没有硬性要求但建议函数名用动词开头fetch_video_listsave_to_dbgenerate_report一眼就能看懂。论文附录里贴核心代码的时候最好配上注释导师一般都会翻附录把注释写全了印象分能高不少。7.4 论文结构安排与摘要写作建议最后提一下论文。毕业设计论文的结构建议用经典七章式第一章 绪论背景、意义、国内外研究现状 第二章 相关技术介绍B站开放接口、Flask、ECharts、MySQL等 第三章 需求分析与系统设计总体架构、功能模块、数据库设计 第四章 系统实现每个模块的关键代码与实现过程 第五章 系统测试与结果分析功能测试、数据采集结果展示、分析结果说明 第六章 总结与展望写论文的时候最容易犯的毛病是写成代码说明书大段大段贴代码。正确的做法是代码贴关键片段然后用文字说明设计思想和实现逻辑。比如贴一段Pandas聚合代码下面解释“这里用了groupby按分区聚合计算每个分区的可见视频总量和可见率为分区对比分析提供数据基础”——论文是要看你“为什么这样写”不是看代码本身。8. 常见问题与避坑指南8.1 爬取阶段的高频问题问题一返回数据是重定向的HTML。这个基本是Cookie失效了。重新登录账号把新Cookie更新到配置文件里就可以了。如果是接口要求WBI签名就直接关掉这一层限制调用基础接口别走带签名的接口。问题二采集过程中IP被临时限制。不用慌一般等一段时间就解封了。建议采集脚本里加入钉钉/邮件通知机制一旦触发限制立刻发通知你就能及时调速别把一个批次的采集任务跑废了。8.2 数据入库的编码与连接问题MySQL的字符集一定要在连接字符串里明确指定UTF-8DB_CONFIG { host: localhost, port: 3306, user: root, password: your_password, database: bilibili_project, charset: utf8mb4, }另一个常见Bug是Pandas的DataFrame.to_sql写入MySQL时如果主键冲突会导致整个事务终止。我的处理方法是先把新数据pandas.DataFrame写入临时表然后用一条INSERT IGNORE完成去重写入。8.3 可视化和前端展示的边界情况最常出问题的环节是图表数据为空时报错。比如你筛选了某个日期范围结果数据库里没有数据ECharts会显示空白。这里建议在后端接口加一层默认值处理if df.empty: return {status: empty, message: 该时间范围内暂无数据} return {status: success, data: json.loads(df.to_json(orientrecords))}这样就算数据为空前端也能弹出提示而不是白屏演示的时候不会在大屏幕上露怯。8.4 毕设答辩高频提问实录提前准备一下这些问题的回答答辩现场会从容很多“你的系统数据量有多大为什么不需要Hadoop” 回答数据量在十万级以下单机MySQL完全能处理Hadoop适合PB级数据本项目重点在分析链路的完整性不在于数据规模。“青少年模式的判定机制你是如何实现的” 回答通过对比两种模式的接口返回内容集合生成可见性标签具体逻辑是取并集和差集。“如果数据量翻倍你的系统瓶颈在哪里怎么优化” 回答瓶颈在爬虫请求等待时间和MySQL单表查询。优化方向是引入消息队列削峰、换用ClickHouse做列式存储、用Redis做缓存。“这个系统的社会价值是什么” 回答能够为平台治理提供数据参考帮助了解青少年模式内容供给情况和潜在缺口也能帮助家长和公众监督互联网平台的内容过滤质量。9. 一点个人体会做这个项目的过程中我最深刻的感受是大数据项目不是比谁工具用得高级而是比谁能把一个真实场景里的问题拆解得足够清晰。B站青少年模式这个切口选下来数据获取有难度但不至于做不动分析维度有深度但不需要堆算法展示效果天生输出视觉冲击力我从选题到完成系统部署前后大概花了五周其中一半时间花在数据采集和清洗上后两周基本就是流畅地出分析、出图表、写论文。至于交付物源码、论文、部署文档、讲解PPT这几样东西最强的搭配组合是“源码能跑通部署文档能复现论文能自圆其说”。你不需要把系统做到生产级的完美但至少自己要走通两遍全流程。我第一次部署到一台全新虚拟机上的时候就发现漏了两个系统依赖当场在部署文档里补上这种自我校验的习惯推荐你们也养成。最后分享一个小技巧做这种毕设项目全程用Git管理自己的代码每天提交一次。论文里的开发日志章节直接把git log导出比凭空回忆要准确得多。答辩的时候你甚至可以翻出最早的一次提交指给评委看“这个系统是从一条采集脚本一步步长成现在这样的”——这种真实的项目演进过程比任何包装出来的话术都更有说服力。