尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Django构建B站青少年模式数据分析系统:采集到可视化全流程

发布时间:2026/9/29 16:55:54

资讯中心
01
ARTICLE

Django构建B站青少年模式数据分析系统:采集到可视化全流程

Django构建B站青少年模式数据分析系统:采集到可视化全流程
刚带完一届毕设正好有几个学生不约而同选了“数据分析系统 带视频属性的平台”这类题目。其中一个做的是B站青少年模式使用情况分析用的就是Django全家桶。这个题目看起来不复杂但真正落地的过程中踩坑的地方比想象中多得多。今天把整套系统的设计思路、核心实现和调试过程完整复盘一遍给正在做类似题目的同学一个可以直接抄作业的参考。先说结论这种系统的本质是把“用户行为数据”变成“可决策的信息”。换句话说数据采集只是基础存储、聚合、可视化才是拉开差距的地方。1. 项目背景与需求拆解1.1 这个题目到底在解决什么问题B站作为国内主流的视频社区青少年模式一直是平台侧重点推广的功能。但青少年模式上了之后实际使用情况怎么样内容分类的占比如何哪些时段使用最频繁学习内容和娱乐内容的消耗比例是多少这些问题家长关心、研究者也关心却缺少一个直观、可量化的分析工具。所以这套系统的目标很明确围绕B站青少年模式下的使用行为数据构建一个从“数据采集”到“存储管理”再到“分析可视化”的完整闭环。用户既可以是家长也可以是研究者甚至可以是平台运营的模拟演示。整个系统交付的是一个后台管理界面、一组分析报表、一套可视化大屏以及完整的设计文档和源码。1.2 系统边界与功能规划拿到这个题目后第一件事不是写代码而是把功能边界划清楚。毕设最忌讳的就是“什么都要做”最后什么都做不深。我建议学生把系统划成四个模块数据采集管理模块负责导入或录入青少年模式下的使用记录支持批量导入和单条新增。考虑到合规性不建议写爬虫硬抓可以用模拟数据生成器 用户授权上报的方式来完成演示闭环。数据存储模块基于Django ORM设计数据表存储用户信息、使用记录、内容分类等核心实体。分析引擎模块利用Django聚合查询 Pandas二次计算完成使用时长、活跃时段、内容偏好等维度的统计。可视化展示模块用ECharts在Web端呈现图表包括折线图、饼图、热力图等按日、周、月维度切换。这四个模块串起来就是一条完整的流水线。每一步都只做好自己的事模块之间通过数据模型和接口衔接。1.3 技术选型为什么是Django而非其他框架很多学生纠结要不要用Spring Boot或者直接写原生PHP。我个人的经验是这类“管理后台 数据分析 可视化”的题目Django有巨大的先天优势。第一Django内置Admin后台意味着“数据管理界面”几乎是白送的。你只需要定义好模型Admin就能自动生成增删改查页面省掉大量CRUD开发时间腾出的精力可以全部放到分析逻辑和图表展示上。第二Django的ORM封装得很完整聚合查询能力很强。values().annotate()这套链式操作写复杂统计比原生SQL更直观而且能直接返回Queryset供模板渲染或JsonResponse输出。第三Django的项目结构天然适合毕设文档撰写。models.py对应数据库设计章节views.py对应功能实现章节templates对应前端展示章节。每一层都清清楚楚论文也好写。当然Django不是没有缺点。比如默认的ORM在超大规模数据下性能一般但对于毕业设计量级几千到几万条记录完全够用。另外一个短板是前端体验不如前后端分离方案灵活但我们用render ECharts的组合就能很好地弥补。2. 系统架构与数据库设计2.1 整体架构从数据采集到可视化的一条链路整个系统的架构可以概括为“三层两线”。“三层”指数据层、业务层、展示层“两线”指数据流和指令流。数据层负责存储所有原始数据使用MySQL作为生产环境的数据库开发阶段用SQLite过渡。业务层包含两大块一是Django自带的Admin管理二是自定义的视图函数来处理统计接口。展示层则是模板页面和ECharts图表。数据流向是这样的原始数据经过清洗和整理后写入数据库业务层通过ORM读取并进行聚合计算计算结果通过JSON接口输出到前端前端ECharts接收JSON后渲染图表。整个过程在用户眼里就是“打开网页、看到图表”但实际上每一步都有明确的职责边界。这里有一个容易被忽视的设计决策聚合计算放在数据库层还是Python层。我们的经验是基础的分组统计用ORM中的annotate比如按分类统计播放次数、按小时统计活跃度而需要复杂加权、环比计算的部分用Pandas处理更灵活。两者结合的方案性能和表达力都能兼顾。2.2 数据模型设计用Django ORM定义核心实体数据模型是整个系统的地基。经过三轮迭代后我最终推荐学生保留三张核心表用户档案表、使用记录表、内容分类表。下面直接给出可以落地的模型代码。from django.db import models class UserProfile(models.Model): 用户档案表用于记录脱敏后的用户基础信息 nickname models.CharField(max_length64, verbose_name昵称) age_group models.CharField(max_length16, verbose_name年龄段) region models.CharField(max_length32, blankTrue, verbose_name所在地区) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Meta: verbose_name 用户档案 verbose_name_plural verbose_name def __str__(self): return f{self.nickname}-{self.age_group} class Category(models.Model): 内容分类表对应B站的主要内容分区 name models.CharField(max_length32, uniqueTrue, verbose_name分类名称) code models.CharField(max_length16, uniqueTrue, verbose_name分类代码) description models.TextField(blankTrue, verbose_name分类描述) class Meta: verbose_name 内容分类 verbose_name_plural verbose_name def __str__(self): return self.name class UsageRecord(models.Model): 使用记录表核心事实表每条记录代表一次内容消费行为 user models.ForeignKey(UserProfile, on_deletemodels.CASCADE, verbose_name用户) category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name内容分类) content_id models.CharField(max_length64, verbose_name内容ID) content_title models.CharField(max_length255, verbose_name内容标题) start_time models.DateTimeField(verbose_name开始时间) duration models.IntegerField(verbose_name观看时长(秒)) device_type models.CharField(max_length16, verbose_name设备类型) class Meta: verbose_name 使用记录 verbose_name_plural verbose_name indexes [ models.Index(fields[start_time], nameidx_start_time), models.Index(fields[user, start_time], nameidx_user_time), ] def __str__(self): return f{self.user.nickname}-{self.content_title}三个模型的关系很清晰UsageRecord通过外键引用UserProfile和Category。这里有两个设计细节值得注意。一是Category用了PROTECT而不是默认的CASCADE。原因是分类是分析维度如果某个分类下还有使用记录贸然删除分类会导致历史数据失真。PROTECT可以强制要求先清理关联记录防止误操作。二是UsageRecord表增加了复合索引(user, start_time)。这个索引专门为“按用户查时间段”的高频查询服务。在实际测试中同样的查询语句没有索引时耗时大约1.2秒加索引后降到200毫秒以内体感差距非常明显。2.3 数据采集的合规思路与模拟数据方案数据采集是所有数据分析系统里最敏感的一环。很多学生第一反应就是写爬虫我在这里必须提醒一句直接爬取B站的数据在合规性上风险很高而且动态加载、反爬机制会消耗大量时间反而冲淡了核心的“数据分析”主题。比较稳妥的做法是用“模拟数据 授权上报”双轨制模拟数据生成器用Python的random和datetime库按照真实的观看习惯生成一批伪数据写入数据库。这样既能完整验证分析逻辑又不需要承担任何合规风险。授权上报通道在Django Admin后台设定“新增记录”的入口家长或测试用户可以通过表单主动提交使用情况数据经过脱敏处理后入库。模拟数据生成器的核心在于“拟真”而非“随机”。真实的观看行为是一个典型的长尾分布热门分类占大头冷门分类只有零星记录。如果均匀随机生成分析图表会很怪异一眼就能看出是假的。所以生成逻辑要引入权重比如知识类权重0.25动画类权重0.22游戏类权重0.18剩下的分散在其他分类。import random from datetime import datetime, timedelta from .models import Category, UserProfile, UsageRecord def generate_mock_data(days30, users_per_day5): categories list(Category.objects.all()) weights [0.25, 0.22, 0.18, 0.12, 0.10, 0.08, 0.05] users list(UserProfile.objects.all()) end datetime.now().replace(minute0, second0, microsecond0) start end - timedelta(daysdays) while start end: for _ in range(users_per_day): user random.choice(users) category random.choices(categories, weightsweights)[0] start_time start timedelta( minutesrandom.randint(0, 1439) ) duration random.randint(120, 3600) UsageRecord.objects.create( useruser, categorycategory, content_idfBV{random.randint(10000, 99999)}, content_titlef模拟内容-{random.randint(1000, 9999)}, start_timestart_time, durationduration, device_typerandom.choice([Android, iOS, Web]) ) start timedelta(days1)这段代码实现了三个目标一是按天迭代生成保证每天都有数据二是通过weights参数控制分类分布三是观看时长保持在2分钟到1小时之间符合真实场景。执行一次可以生成约150条记录足够支撑图表展示。3. 核心功能实现与代码走读3.1 基于Django Admin的后台管理与权限控制Django Admin是这个项目中最容易出彩、也最容易被忽略的模块。很多教程只教注册模型但真正做项目时Admin的配置可以让数据管理效率翻倍。使用记录表的Admin配置我推荐按下面的方式写from django.contrib import admin from .models import UserProfile, Category, UsageRecord admin.register(UsageRecord) class UsageRecordAdmin(admin.ModelAdmin): list_display (user, category, content_title, start_time, duration, device_type) list_filter (category, device_type, start_time) search_fields (content_title, user__nickname) date_hierarchy start_time list_per_page 20list_filter按分类和设备过滤search_fields支持通过标题和用户昵称搜索date_hierarchy直接在页面顶部生成时间层级筛选器。这几个配置加起来不到十行但后台的可用性提升了一个档次演示的时候效果特别好。权限控制方面Django自带的用户表已经具备完整的认证体系。建议新建一个analyst分组只授予使用记录的查看权限不给删除和修改权限防止演示时误操作清空数据。这个设计在论文里也可以作为“信息安全”章节的素材。3.2 数据分析引擎ORM聚合 Pandas二次加工分析引擎是整个系统的灵魂。在设计时我坚持一个原则能用ORM完成的聚合就用ORMORM表达不了的复杂计算再交给Pandas避免出现“一段代码绕来绕去只为取一个平均数”的可读性灾难。先看最基础的分组聚合。比如统计每天的使用时长和观看次数from django.db.models import Sum, Count from django.db.models.functions import TruncDate def get_daily_stats(days7): records ( UsageRecord.objects .filter(start_time__gtetimezone.now() - timedelta(daysdays)) .annotate(dayTruncDate(start_time)) .values(day) .annotate( total_durationSum(duration), view_countCount(id) ) .order_by(day) ) return list(records)这里用了TruncDate函数它会把DateTimeField截断为Date类型然后按照天分组。Sum计算总时长Count计算数量。结果是一个字典列表直接json.dumps后就可以交给前端。再复杂一点的需求比如计算“不同年龄段的用户偏好差异”单纯靠ORM写会非常啰嗦需要先values()导出数据再用Pandas处理。import pandas as pd def get_age_category_preference(): qs ( UsageRecord.objects .values(user__age_group, category__name) .annotate(total_durationSum(duration)) ) df pd.DataFrame.from_records(qs) pivot df.pivot_table( indexuser__age_group, columnscategory__name, valuestotal_duration, aggfuncsum, fill_value0 ) # 归一化处理让不同年龄段之间可比 pivot pivot.div(pivot.sum(axis1), axis0) return pivot.to_dict()pivot_table生成的是一个透视表行列分别是年龄段和分类值是观看总时长。归一化之后每一行加起来等于1表示“某年龄段用户在不同分类上消耗的时长占比”。这种数据格式特别适合堆叠柱状图。这里有几个容易踩的坑提前告诉大家from_records传入的必须是可迭代的字典序列Django的Queryset转成list()后可以直接用。fill_value0一定要加否则空值在后续转换中会变成NaN导致前端报错。把DataFrame转成dict时如果列名太长会成为dict的key前端取值时要注意大小写。3.3 可视化大屏ECharts接入与异步刷新可视化是毕业设计答辩时的门面做得好看可以直接加印象分。我选择ECharts的原因很简单中文文档全、图表类型丰富、不用前端框架也能用得起来。在Django中接入ECharts有两种思路一种是直接把数据渲染在模板中另一种是通过JSON接口异步获取。毕设项目中推荐后者因为异步加载在答辩演示时可以展示“接口返回数据—前端渲染”的完整流程更能体现系统工程性。前端模板的核心结构div idchart-duration stylewidth: 100%; height: 400px;/div script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script script const chartDuration echarts.init(document.getElementById(chart-duration)); fetch(/api/daily-stats/?days7) .then(res res.json()) .then(data { chartDuration.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(item item.day) }, yAxis: { type: value, name: 观看时长(小时) }, series: [{ name: 使用时长, type: bar, data: data.map(item (item.total_duration / 3600).toFixed(2)) }] }); }) .catch(err console.error(数据加载失败:, err)); /script对应的Django接口视图非常简洁from django.http import JsonResponse from django.views.decorators.http import require_GET require_GET def api_daily_stats(request): days int(request.GET.get(days, 7)) stats get_daily_stats(days) return JsonResponse(stats, safeFalse)这里的关键点是data.map(item item.total_duration / 3600)数据库存的是秒图表展示时转换成小时否则Y轴的数值会大得离谱显得很不专业。在异步刷新方面我加了一个定时器每30秒重新请求一次接口并更新图表。这模拟了“数据实时更新”的效果也顺便验证了接口的稳定性。实际答辩时可以打开两个窗口一个窗口往后台手动添加记录另一个窗口看图表自动跳变效果非常直观。4. 部署调试与交付细节4.1 从零搭建Django环境与项目骨架很多学生卡在了第一步的环境搭建上。这里给出一个亲测稳定的操作序列跟着做基本不会出问题。# 创建虚拟环境 python -m venv venv # 激活虚拟环境Windows/Linux venv\Scripts\activate # Windows source venv/bin/activate # Linux/macOS # 安装依赖 pip install django4.2 mysqlclient celery pandas requests # 创建项目和应用 django-admin startproject bilibili_analysis python manage.py startapp data_analysis项目创建后需要手动在settings.py中注册应用并配置MySQL连接开发阶段可以直接用SQLite等需要演示再切MySQL。还有一个容易忽略的步骤——在settings.py中配置静态文件路径STATIC_URL /static/ STATICFILES_DIRS [ BASE_DIR / static ]如果不配置STATICFILES_DIRSECharts的本地文件、自定义CSS和JS都可能加载不出来页面图表直接空白。创建完项目骨架后按照下面的顺序执行保证项目能跑起来python manage.py makemigrations python manage.py migrate python manage.py createsuperuser python manage.py runserver如果到这里一切正常说明基础环境已经通了。剩下的就是写业务代码这部分在前面已经详细拆解过。4.2 远程调试的几种实用姿势项目交付时需要远程调试特别是帮别人配置环境或排查问题。远程调试做得熟练可以大幅减少沟通成本。我通常用三种方式第一种是Django自带的runserver配合内网穿透工具把本地的8000端口暴露出去。这种方式适合临时演示但要注意ALLOWED_HOSTS必须改成[*]或者填入对应域名否则Django会拒绝请求报错信息是DisallowedHost。第二种是SSH隧道转发。如果你有云服务器可以在本机执行ssh -R 8000:localhost:8000 useryour-server这样服务器上的8000端口会转发到本地对方的浏览器只需要访问服务器IP就能看到本地的Django页面。这个方式最稳不依赖于第三方工具而且走的是标准SSH加密通道。第三种是日志联动排查。远程调试时我习惯开启Django的DEBUG日志并把sql查询写到控制台python manage.py runserver --verbosity 3配合--verbosity 3Django会打印出每次SQL查询的语句和执行时间。这样即使看不到对方的操作过程也能通过日志推断问题出在数据库查询还是模板渲染。4.3 高频问题排查实录做完整套系统后我归纳总结了以下几个出现频率最高的问题附上排查思路和解决方案可以直接对照参考。问题现象可能原因解决方案Admin后台点击“保存”后报IntegrityError外键关联数据不一致检查Category是否清空重建确认关联表数据完整图表一直转圈加载不出来前端接口路径错误或JSON格式问题浏览器F12查看Network请求状态直接访问接口URL检查返回内容时间统计比实际少8个小时Django时区未正确设置USE_TZ True时在settings.py中配置TIME_ZONE Asia/Shanghaipython manage.py runserver之后静态文件404未配置STATICFILES_DIRS补充静态文件路径重启服务并清理浏览器缓存分析报表数值非常大/非常小单位不一致确认数据入库时统一用秒展示层再转换单位使用ECharts时控制台报zrender is undefined引入方式错误使用官方CDN完整版echarts.min.js不要拆分模块时区问题是最隐蔽的一个坑。Django默认USE_TZ True数据库里存的是UTC时间如果你在本地直接查询看到的start_time会比北京时间少8小时。这个在开发初期不显眼但一旦做“按小时分布”的统计图表上会出现整体的偏移看起来活跃时段全错了。解决办法有两个如果项目只在中国境内使用直接USE_TZ False让Django使用本地时间如果必须保留UTC存储则在前端转成东八区展示const localTime new Date(utcTime).toLocaleString(zh-CN, { timeZone: Asia/Shanghai });还有一个容易被忽视的细节是runserver在DEBUGFalse时不会提供静态文件服务。如果部署到服务器后页面样式全部丢失优先检查DEBUG配置和collectstatic操作。最后分享一点个人实操中的体会做了这些年的毕设辅导我最大的感受是很多学生不是不会写代码而是太早开始写代码。拿到题目后如果愿意花一到两天把数据模型和模块边界画清楚后面写代码能省一半的时间。具体到今天这个题目最出彩的部分永远是“分析视角”。技术层面大家都会用Django、会用ECharts但为什么有人能拿优秀毕设有人只能勉强通过差别就在于是不是能提出有意思的统计维度。比如“青少年模式下的学习类内容占比趋势”、“周末和工作日的使用峰值对比”、“不同年龄段的内容偏好差异”这些维度在答辩时一抛出来评委直观感受到的就是——这个人不止是做了个管理系统而是在真正分析问题。另外提醒一点如果是纯演示环境模拟数据一定要做得够真实。我见过太多论文里的截图数据分布一眼假被评委追问两句就露馅了。数据生成器多花半小时调参带来的答辩收益远超你的想象。如果你正在做类似的毕业设计希望这篇复盘能帮你在架构设计和模块实现上少走弯路。下一步可以考虑加入预测功能比如基于历史数据的时间序列分析或者引入Celery做定时统计任务让整个系统的完成度再上一个台阶。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。