ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Django的B站青少年模式数据分析可视化大屏毕设系统

基于Django的B站青少年模式数据分析可视化大屏毕设系统 先说个实在的这种“Java毕设 Django实现 数据分析 可视化大屏”的题目在各大高校毕业设计选题库里一年能见到几百次。名字看着矛盾Java和Django不是一个生态但实际做下来就明白了——题目由教务系统自动生成时默认挂了Java前缀真正落地时大家基本都是Django Python数据分析全家桶因为Python在数据处理和可视化上比Java生态省太多事了。这篇文章我就把这个项目的完整设计思路、数据模型、前后端关键实现以及实操里最容易翻车的几个坑全部拆开讲给正在做类似选题的同学一条可以直接照抄的路线。这个项目本质上是一套“数据采集 - 数据清洗 - 指标计算 - 可视化展示 - 报告导出”的完整数据链路。Bilibili推出了青少年模式平台方和研究者都需要知道这个模式用没用上、用得怎么样、用户在使用过程中有什么行为特征这就是项目的真实业务价值。换句话说它不是一个空中楼阁的CRUD系统而是能回答“青少年模式推广效果如何”“用户集中在什么时段使用”“哪些内容被青少年群体高频消费”这些具体问题的分析工具。全文含完整前后端代码、说明文档和LW支持调试定制适合拿来当毕设主项目也适合想系统学习Django数据分析应用的人对照练习。1. 项目定位与技术选型思路拆解1.1 这个系统本质要解决什么问题先问一个问题B站青少年模式的数据从哪来其实有两种路径。第一种是走平台官方开放接口能拿到的是有限的视频信息和公开统计数据拿不到真实用户行为第二种是模拟客户端行为做数据采集也就是通过构造HTTP请求获取视频页、搜索页、推荐流里的公开数据再结合一个模拟使用环境产生行为日志。毕设场景下绝大多数同学走的是第二种因为这个系统需要自己造一批“青少年模式下的使用数据”来支撑分析展示也不可能拿到真实平台后台数据。所以这个项目的第一层本质是自己造数 自己算数 自己展示。说得直白一点它就是一个面向青少年模式使用场景的BI看板系统。你要设计一个模拟的数据采集器定时去抓取B站热门内容、分区分布、播放数据同时生成一批模拟的用户使用行为数据使用时段、停留时长、观看分区、是否触发时间提醒等再把这些数据清洗后存入MySQL用Django的ORM做聚合统计最后通过ECharts在Web端展示出趋势、排行、分布等图表。想清楚这一层整个系统的边界就清晰了后面所有模块都是围绕“采集、存储、分析、展示”四个环节展开的。1.2 为什么是 Django 而不是纯 Java 技术栈很多同学拿到题目第一反应是题目写着Java那我是不是要用Spring Boot其实不用纠结题目名称。原因很简单Django自带一个强大的Admin后台、自带ORM和Migration机制、自带模板引擎特别是配合Django REST Framework写API非常快一个没有太多Java基础的Python学习者两周就能把后端跑通。而数据分析场景里Python的pandas和numpy在聚合计算上几乎是无敌的存在一句groupby就能完成Java里要写几十行的统计逻辑。另外Django的ORM在做时间分组、聚合统计时有专门的方法比如TruncDate、TruncHour可以直接在SQL层完成按天、按小时的分组不需要把数据全部拉到内存里挨个处理。这个特性对这个项目来说是决定性的因为青少年模式分析的两个核心维度恰恰就是“按天看活跃趋势”和“按小时看时段分布”。如果用Java写要么写原生SQL派生出复杂的日期格式化语句要么手动循环分桶代码量和出错率都会高很多。1.3 整体架构和模块划分整个项目可以拆成五个模块模块职责关键技术点数据采集模块抓取B站公开视频信息、生成模拟行为日志requests 随机UA 频率控制数据清洗模块去重、补全、格式转换、字段校验pandas Django ORM存储模块用户、日志、视频信息、分析结果落库MySQL Redis缓存分析计算模块指标聚合、趋势计算、排行统计Django ORM pandas可视化展示模块大屏看板、图表交互、报告导出ECharts Django REST openpyxl这五个模块在代码层面是解耦的采集和清洗可以写成独立的Python脚本scripts/collector.py分析计算放在Django的管理命令里management/commands/analyze.py展示部分走REST接口。这样设计的好处是数据采集和分析计算可以分开调度比如采集脚本用crontab每天凌晨跑一次分析命令每天凌晨跑一次Web端永远只读分析结果表查询压力很小大屏渲染也快。2. 核心数据模型与关键分析指标设计2.1 数据表结构是怎么设计的数据表是整个分析系统的地基表设计不好后面所有指标都会算得别扭。我的建议是围绕“用户、行为、内容、汇总”四个方向建表不多不少刚好八张核心表。行为明细表一定要用“一条记录一次行为”的粒度不要图省事存汇总值。比如用户每一次进入青少年模式记一条session_start每看一个视频记一条观看记录。明细粒度虽然存储量大但胜在灵活任何维度的统计都能从明细里聚合出来。如果只存了汇总数据后期想加一个“周末 vs 工作日使用差异”的分析指标你就得重新跑数这个坑我踩过。具体表结构这样设计users存储用户主体脱敏只保留用户ID、年龄段、注册时间、城市、设备类型usage_sessions一次使用会话开始时间、结束时间、时长、进入页面、退出页面watch_records单次观看记录视频ID、视频分区、稿件类型、观看时长、是否看完、是否触发时间提醒videos视频基础信息标题、分区、UP主、播放量、点赞数、投币数、发布时间remind_logs青少年模式弹窗提醒日志提醒类型、用户当时状态、提醒后是否继续使用daily_user_stats每天每用户的汇总活跃天数、日均时长、偏好分区daily_overall_stats全站每日汇总DAU、总时长、峰值时段、平均时长report_records生成报告记录报告时间、统计区间、报告文件路径其中usage_sessions和watch_records是大表必须加联合索引索引字段要覆盖查询时最常用的过滤条件比如(user_id, start_time)和(video_id, watch_date)。没有索引时数据量到五万条以上Django页面版查询就会明显变卡这属于典型的前期偷懒后期加倍还的教训。2.2 关键指标的计算逻辑与ORM实现这个项目的分析指标大概有六个方向每一个都是答辩时重点展示的东西也是你写说明文档时的核心素材日活跃用户DAU按天统计去重用户数。Django的ORM写法是from django.db.models import Count from django.db.models.functions import TruncDate daily_dau ( UsageSession.objects .annotate(dayTruncDate(start_time)) .values(day) .annotate(dauCount(user_id, distinctTrue)) .order_by(day) )这里有个关键细节TruncDate使用的时区是Django的timezone.now()所在时区而不是MySQL的本地时区。如果你系统部署在国内服务器必须在settings.py里把TIME_ZONE设为Asia/Shanghai否则凌晨0点到8点之间产生的数据会被归到前一天统计出来的DAU曲线在每天0点会有一个诡异的下坠。使用时段分布24小时热力按小时分组统计会话开始次数。这里要用TruncHour同时计算每个小时段的活跃占比这个数据最终会渲染成一个24小时热力条形图是可视化大屏里视觉效果最好的一张图。内容偏好TOP10根据watch_records按分区维度统计播放次数、观看总时长。这个指标要同时考虑两个排序口径一个是“播放次数最多”一个是“总观看时长最长”两个口径经常不一致。比如短片区的播放次数可能第一但总时长可能排不过番剧和电影。展示时最好两个指标都给出或者在图表上加一个切换按钮这个细节做好了答辩老师会认为你有产品思维。弹窗提醒漏斗记录提醒弹窗发送次数、点击“继续使用”人数、点击“退出青少年模式”人数、直接关闭弹窗人数。这个指标反映了青少年模式的拦截有效性是整个项目最有分析价值的部分。实现上就是remind_logs表按remind_type分组的条件聚合。会话时长分布把会话时长分成0-5分钟、5-15分钟、15-30分钟、30分钟以上四个桶统计每个桶的会话数占比。这个指标能直观反映是否存在长时间沉迷分桶逻辑在Python里用pandas的cut函数最方便import pandas as pd bins [0, 5, 15, 30, float(inf)] labels [0-5分钟, 5-15分钟, 15-30分钟, 30分钟以上] df[duration_bucket] pd.cut(df[session_minutes], binsbins, labelslabels)按天/按周趋势数据量大了以后按天趋势会有明显的周期性波动周一至周五偏低、周末走高这是正常的学生群体使用特征。分析时要区分工作日和周末分别统计避免在答辩时被问到“为什么周二数据的曲线有个沟”这类问题而答不上来。2.3 指标口径统一这件事有多重要毕设项目走到联调阶段的时候最容易出现的问题不是代码报错而是前端展示的数字和后端Excel报告里的数字对不上。原因基本都是指标口径不统一。比如“活跃用户”这个指标后端有的接口用distinct user_id有的用count(*)那结果就完全不一样。所以从第一天开始就要在项目里定义清楚一份“指标字典”写明每个指标的名称、业务含义、计算公式、数据来源表、更新频率。推荐放在项目根目录的docs/metrics.md里接口备注里也写清楚。我的做法是在Django的每个分析接口的docstring里直接标注指标口径联调时前端照着接口文档核对几乎没有再发生过对不上数的扯皮。3. 前后端联调与可视化大屏实现的实操细节3.1 Django REST 接口的快速搭建后端接口我用的是Django REST FrameworkDRF原因就一句话它把序列化、分页、权限、限流都封装好了毕设级别的接口需求基本不用额外造轮子。整个后端的接口设计是前后端分离的JSON格式一共五组接口/api/v1/overview/顶部概览卡片DAU、平均时长、活跃率、提醒拦截率/api/v1/trend/按天趋势折线/api/v1/hourly/24小时时段分布条形图/api/v1/category/内容分区排行柱状图/api/v1/export/导出分析报告Excel用DRF实现这几组接口核心代码就是ViewSet Serializer。这里有一个经验对于纯查询接口不要写一堆复杂的Serializer嵌套直接用DRF的action装饰器加自定义逻辑更灵活。比如趋势接口直接在视图里调ORM聚合把结果包成字典返回比硬套序列化器省事得多from rest_framework.decorators import api_view from rest_framework.response import Response from django.db.models import Count, Sum from django.db.models.functions import TruncDate api_view([GET]) def trend_view(request): data ( WatchRecord.objects .annotate(dayTruncDate(watch_date)) .values(day) .annotate( play_countCount(id), total_durationSum(watch_duration), ) .order_by(day) ) return Response(list(data))这个接口返回的结构前端ECharts几乎不需要做二次加工直接塞进option.series.data就能渲染。接口设计阶段就和前端约定好“后端做聚合前端做渲染”的原则能省掉大量联调时间。3.2 登录态与 Token 的落地细节很多毕设项目把登录态做成localStorage存Token图省事但实际开发中你会发现两个问题一是XSS攻击可以直接拿到Token二是刷新页面后Token失效的体验很差。我在这个项目里采用的是“Cookie存JWT”的方案具体做法是登录成功后把JWT写入HttpOnly的Cookie这样前端JavaScript拿不到Token安全性上一个档次。Django里设置Cookie的代码非常简单from django.http import JsonResponse import jwt def login_view(request): user authenticate(...) if user: token jwt.encode({user_id: user.id, exp: ...}, settings.SECRET_KEY, algorithmHS256) response JsonResponse({code: 0, msg: ok}) response.set_cookie(token, token, httponlyTrue, max_age86400, samesiteLax) return response这里必须强调samesiteLax如果不设置Chrome浏览器跨域请求时Cookie默认不携带前端大屏接口全部返回401。还有max_age要定成和JWT过期时间一致的86400秒避免Cookie还在而Token过期的情况出现。这个细节在本地联调时经常被忽略等部署到服务器才发现排查半天。3.3 可视化大屏和实时推送怎么做大屏部分用的是ECharts布局是典型的大屏Dashboard风格顶部标题栏左中右三栏底部滚动数据条。图表组件有折线图、柱状图、饼图、热力图、数值卡片。ECharts的配置项比较多我的建议是封装一个通用的chartOption(type, data)函数不同的图表类型各写一个配置模板避免每个页面堆几百行option配置。比图表本身更重要的是数据推送机制。展示页不能在每次刷新时都对后端发一次全量查询而是应该在后端数据更新后主动推送给前端。Django里实现后端到前端的数据推送有两个方案一个是轮询前端每30秒调一次接口另一个是用Django Channels实现WebSocket。毕设项目我建议先上轮询简单可靠等答辩前一周如果精力充足再升级WebSocket。如果确实想用WebSocket展示实时数据的更新效果Django Channels的配置路径是安装channels库在settings.py里配置ASGI_APPLICATION实现一个Consumer当管理命令执行完analyze.py后向Redis频道发送一条消息class DataConsumer(AsyncWebsocketConsumer): async def connect(self): await self.accept() await self.channel_layer.group_add(dashboard, self.channel_name) async def receive(self, text_data): data json.loads(text_data) await self.channel_layer.group_send( dashboard, {type: dashboard.message, data: data}, )前端WebSocket收到消息后直接调用chart.setOption()更新视图。这里要注意的是WebSocket长连接对部署有额外要求需要ASGI服务器不能用简单的runserver所以如果时间紧轮询方案完全够用。4. 实操中的高频踩坑点与排查锦囊4.1 数据采集的合规与频率控制数据采集是整个项目里最容易被低估的环节。一开始我想着多线程高频抓取能多快好省地把数据凑齐结果分分钟被平台的频控策略拦住IP直接限制访问。后来我学乖了在采集脚本里加了三个保险随机User-Agent池、每次请求之前随机休眠0.5 random.random()秒、单次任务最多抓取2000条就收工。实测下来低频小批量的采集策略稳得多一次跑完只需要十几分钟后台日志干干净净。还有一点必须提醒采集的数据只用于学术研究和毕设展示发表论文或项目展示时不要去碰用户隐私数据也别公开抓取接口细节。做毕设的底线是数据脱敏和尊重平台规则。4.2 时区问题引发的统计偏差这是所有数据分析项目共同的坑具体表现是前端折线图的日期坐标和数据库里的实际日期差8小时指向不对。根因就是settings.py里的TIME_ZONE和USE_TZ配置不一致。我的经验是统一采用北京时间TIME_ZONE Asia/ShanghaiUSE_TZ False。前者让Django的时间显示正确后者让MySQL存储和查询都直接用本地时间避免SQL层和ORM层做两次时区转换导致的混乱。如果团队协作时有人习惯用UTC我用过一个更稳妥的方案在模型层定义一个local_time属性用timezone.localtime(value)统一转换后再暴露给接口。这样存储层永远存UTC展示层永远输出北京时间谁也改不乱。4.3 删除操作和查询性能的坑Django的删除操作有一个隐蔽的坑objects.filter(...).delete()默认是级联删除的。在remind_logs和usage_sessions这类有关联外键的表上删一条用户可能连带删掉几十条行为记录。如果只是想清测试数据最好用objects.filter(id__in...).delete()精确控制范围或者用软删除方案——给表加一个is_deleted字段删除就是一次update数据还在。毕设项目里我强烈推荐软删除答辩时可以解释业务上保留原始数据用于审计的考量反而成为加分项。查询性能方面明细表数据量上了几万条之后UsageSession.objects.filter(user_id..., start_time__gte...)这种查询如果没走索引响应时间会从几百毫秒飙升到几秒。解决方案有两个一是给查询频率最高的字段建组合索引二是对于前端大屏的统计接口尽量读daily_overall_stats汇总表不要每次都聚合明细表。我在项目里专门写了一个refresh_stats.py管理命令数据分析后把结果物化到汇总表接口查询毫秒级返回渲染非常流畅。4.4 前端图表卡顿与数据量优化大屏页面上如果塞了八张ECharts图每张图都拉全量数据首次加载会有明显的空白等待期。优化的办法是按需加载——首屏只加载概览卡片和趋势图其他图表等用户点击对应Tab时才发请求并初始化实例。ECharts实例用完要调用dispose()销毁否则切Tab回几次内存就爆了页面白屏这是很多同学查一天都查不出来的内存泄漏问题。另外图表tooltip的触发方式和动画效果在大屏上也有讲究。大数据量折线图建议animation: false否则每一帧都重新做动画CPU占用直接拉满。这些优化做完之后大屏即使加载几万条数据滚动和切换也不会有任何卡顿感。5. 关于这个项目的横向扩展建议主体功能做完之后有余力的话可以考虑加几个提升系统和论文档次的扩展点。我个人觉得最有价值的三个方向是加入报告自动生成功能用openpyxl导出汇总Excel包含所有指标图表加入权限分级管理管理员/普通用户/访客三种角色以及加入异常使用预警功能当某用户的单日使用时长超过阈值时自动生成预警记录。报告导出这块别看简单但答辩时老师几乎必问。我用openpyxl封装了一个导出工具逻辑是把所有指标字典转成DataFramedf.to_excel()直接落盘再压成zip包通过HTTP响应返回给前端下载。前后端联调下载功能时注意文件名一定要做URL编码否则浏览器下载中文文件名会乱码。权限分级管理建议用Django自带的Group模型把接口权限和菜单权限绑定到角色上管理员可以看到全量数据和预警信息普通用户只能看概览。这部分的实现思路在说明文档和LW里都要详细写论文查重和答辩时是明晃晃的技术亮点。最后给一个做毕设的人最实用的建议项目不要做完才写文档而是边做边写。数据模型设计好先写进LW接口联调好就截几张图分析报告生成了就顺手存一份模板。等到论文阶段你会发现所有能用的素材都是平时攒下来的熬夜赶工最大的坑从来不是代码而是文档和演示素材的临时拼凑。踩过几次坑之后我现在做任何数据类项目都习惯先定数据字典和指标口径再动手写代码走完这套流程这个B站青少年模式分析系统从零到一基本不会卡在哪个环节上。
返回列表