0 前言
🔥这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩 毕业设计 大数据B站数据分析可视化系统
🥇学长这里给一个题目综合评分(每项满分5分)
难度系数:3分
工作量:3分
创新点:4分
🧿 项目分享:见文末!
1 项目运行效果
视频效果:
毕业设计 大数据B站数据分析可视化系统
2 设计原理
以下做一个简单的demo作为讲解,是非常基础的数据挖掘可视化过程,demo过于基础仅作大致原理解释和介绍,就不放在最后的工程里了,网上很多类似的案例,想学习的同学自己模仿。
为了便于后续的数据的使用,我们从老师规定的三种可视化方案中选择了pycharts方案,因而后端也就选择了Flask,一个使用Python编写的轻量级 Web应用框架。
对于数据的抓取,则选取了Urllib这一python内置的HTTP请求库来进行抓取。
对于前端,我们采用了flexible使用rem自适应布局,使用jQuery Ajax对图表进行实时的更新。
对于数据处理,主要使用到了python的一些内置库,除此之外,对于综合评分分析中,使用到了灰色关联度分析与主成分分析,用到了sklearn库,详细的内容在可视化部分进行详解。
数据处理方案
本次数据来源于哔哩哔哩排行榜,服务器后台中每5min对排行榜数据爬取,通过jQuery Ajax+flask实时更新到网页上。
以下是数据爬取过程:
本次爬虫教程使用requests第三方库,一个强大的基于urllib3的第三方库。
首先分析哔哩哔哩排行榜的源码:
不难发现榜单都在 < li >标签中,所以可以先找出该全部标签,然后再详细分析里面的信息。
可以看出,大厂的网站写的还是很棒的,很有体系,可以说bilibili甚至很适合新手来练手爬虫。
因为比较条理且为静态网页,直接使用requests,遍历li并对其进行find操作,找到对应标签内的内容,存储即可完成热榜爬取。
此时部分数据需进行一定的处理,但均较为简单,转换格式以及去除空格、‘\n’等。
但哔哩哔哩排行榜的内容只包括排名,视频名称、播放量、弹幕数、综合得分、作者、链接,并没有更加重要的投币、点赞、转发和收藏等关键信息。所以还需要对每一个页面内部进行爬取。
后续代码中的info_Page(bv)对此进行了实现,bv代表的是哔哩哔哩每个视频对应的唯一的bv号,使用此bv添加网站后缀即可完成info页面的访问,在info页面中,依旧十分易于爬取。
但此时爬取速度过快时会触发其反爬策略,且较难处理,故后续采用其提供接口获取内部详细数据。使用 + bv号形式获取数据,后续只需进行格式处理即可完成。
最后数据保存入bilibili.txt文件中,数据的运用及处理在各可视化案例中详细介绍。
爬虫部分到此结束:
附 爬虫代码:
import requests
from bs4 import BeautifulSoup
import xlwt
import time
import urllib3
import requests
import json
# 爬取B站热榜排行
# 格式解析,[0-当前排名,1-视频标题,2-播放数目,3-弹幕数量,4-综合得分,5-作者,6-视频地址,7-时长,8-评论数,9-收藏数,10-投币数,11-分享数,12-点赞数]
# 格式化
def whitespace(st):
st = st.replace('\n', '')
st = st.strip()
st = st.replace(' ', '')
return st
# 详情页
def info_Page(bv):
url = 'http://api.bilibili.com/x/web-interface/view?bvid=' + bv
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
} # 请求头,模拟浏览器的运行
urllib3.disable_warnings() # 从urllib3中消除警告
response = requests.get(url, headers=headers)
content = json.loads(response.text)
# 很迷,获取到的是str字符串 需要解析成json数据
statue_code = content.get('code')# print(statue_code)
if statue_code == 0:
duration = content['data']['duration'] # 时长
reply = content['data']['stat']['reply'] # 评论
favorite = content['data']['stat']['favorite'] # 收藏
coin = content['data']['stat']['coin'] # 投币
share = content['data']['stat']['share'] # 分享
like = content['data']['stat']['like'] # 点赞
return duration,reply,favorite,coin,share,like
while(True):
url = 'https://www.bilibili.com/v/popular/rank/all'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'}
rank = requests.get(url, headers=headers) # 请求页面
soup = BeautifulSoup(rank.text, 'lxml')
all_rank = soup.find_all('li', class_='rank-item')
num = 0
lst=[]
for i in all_rank:
record = []
rank_num = i.find('div', class_='num').text # 获取排名
info = i.find('div', class_='info') # 筛选出视频详细信息的标签
href = info.find('a', class_='title').attrs['href'] # 获取链接
title = info.find('a', class_='title').text # 获取标题
play_num = info.find('i', class_='b-icon play').parent.text # 获取播放量
view_num = info.find('i', class_='b-icon view').parent.text # 获取弹幕数
author = info.find('i', class_='b-icon author').parent.text # 获取作者名
scores = info.find('div', class_='pts').find('div').text # 获取综合得分
# 播放,弹幕,作者
play_num = whitespace(play_num)
view_num = whitespace(view_num)
author = whitespace(author)
bv = href.split('/')[-1]
duration,reply,favorite, coin,share,like = info_Page(bv)
record.append(rank_num)
record.append(title)
record.append(play_num)
record.append(view_num)
record.append(scores)
record.append(author)
record.append(href)
record.append(duration)
record.append(reply)
record.append(favorite)
record.append(coin)
record.append(share)
record.append(like)
num += 1
lst.append(record)
# 爬取的数据存入文件,避免多次爬取且提高响应速度
with open('./bilibili.txt', 'w',encoding='utf-8') as f:
for line in lst:
for i in line:
f.write(str(i)+',')
f.write('\n')
time.sleep(300)
#print(lst[0])
可视化呈现方案
综合得分计算指标
哔哩哔哩综合得分,是视频是否能排上排行榜的依据,若能知道其规则,对于视频内容的倾向,up主是否需要请求“一键三连”,观众们需不需要吝啬手中的币,是有很大的价值的,所以在此首先进行综合得分计算指标的分析及其可视化,此处采取灰色关联度分析(Grey Relation Analysis,GRA)来进行数据的处理,
GRA是一种多因素统计分析的方法。简单来讲,就是在一个灰色系统中,我们想要了解其中某个我们所关注的某个项目受其他的因素影响的相对强弱,本项目中,就是说:我们假设B站综合得分可能是与播放、评论、收藏、投币、分享、点赞几个因素相关的,那么我们想知道综合得分与这几个因素中的哪个相对来说更有关系,而哪个因素相对关系弱一点,把这些因素排个序,得到一个分析结果,我们就可以知道哔哩哔哩综合得分,与因素中的哪些更相关,因而也就可以看出观众的一键三连的作用以及up主们更应该求的是赞、币亦或是其他。
首先是要确定子母序列,母是结果,子是影响因子,那么,毫无疑问,综合得分就是母,其他均为影响因子。将其分别存入mom_以及son_中,代码如下:
with open('./bilibili.txt', 'r+',encoding='utf-8') as f1:
lst2=[]
for line in f1.readlines():
lst2.append(line.split(','))
mom_ = [int(i[4]) for i in lst2[0:50:]]
view = []
reply = []
favorite = []
coin = []
share = []
like = []
for i in lst2[0:50]:
view.append(float(i[2].strip("万"))*10000)
reply.append(int(i[8]))
favorite.append(int(i[9]))
coin.append(int(i[10]))
share.append(int(i[11]))
like.append(int(i[12]))
son_ = [view,reply,favorite,coin,share,like]
然后要对数据进行预处理,因为我们的这些要素是不同质的东西的指标,因此可能会有的数字很大有的数字很小,但是这并不是由于它们内禀的性质决定的,而只是由于量纲不同导致的,因此我们需要对它们进行无量纲化。这个操作一般在数据处理领域叫做归一化(normalization),也就是减少数据的绝对数值的差异,将它们统一到近似的范围内,然后重点关注其变化和趋势。按公式归一化即可。
mom_ = np.array(mom_)
son_ = np.array(son_)
son_ = son_.T / son_.mean(axis=1)
mom_ = mom_/mom_.mean()
for i in range(son_.shape[1]):
son_[:,i] = abs(son_[:,i]-mom_.T)
Mmin = son_.min()
Mmax = son_.max()
cors = (Mmin + 0.5*Mmax)/(son_+0.5*Mmax)
Mmean = cors.mean(axis = 0)
最终结果:B站综合得分与播放、评论、收藏、投币、分享、点赞几个因素都具有很强的相关性。
为了体现这一结果,我们采用了关系图来进行可视化:
由于灰色关联度分析的权重均较为接近,但哔哩哔哩综合得分的公式也并没有公布,所以无从证实其真实性,下图为网传数据进行的关系图可视化,均仅供参考:
附: 灰色关联度分析及可视化
# 灰色关联度分析版本
import numpy as np
from pyecharts import options as opts
from pyecharts.charts import Graph
with open('./bilibili.txt', 'r+',encoding='utf-8') as f1:
lst2=[]
for line in f1.readlines():
lst2.append(line.split(','))
mom_ = [int(i[4]) for i in lst2[0:50:]]
view = []
reply = []
favorite = []
coin = []
share = []
like = []
for i in lst2[0:50]:
view.append(float(i[2].strip("万"))*10000)
reply.append(int(i[8]))
favorite.append(int(i[9]))
coin.append(int(i[10]))
share.append(int(i[11]))
like.append(int(i[12]))
son_ = [view,reply,favorite,coin,share,like]
mom_ = np.array(mom_)
son_ = np.array(son_)
son_ = son_.T / son_.mean(axis=1)
mom_ = mom_/mom_.mean()
for i in range(son_.shape[1]):
son_[:,i] = abs(son_[:,i]-mom_.T)
Mmin = son_.min()
Mmax = son_.max()
cors = (Mmin + 0.5*Mmax)/(son_+0.5*Mmax)
Mmean = cors.mean(axis = 0)
# 为便于观察 扩大40倍
nodes = [
{"name": "播放", "symbolSize": Mmean[0]*40},
{"name": "评论", "symbolSize": Mmean[1]*40},
{"name": "收藏", "symbolSize": Mmean[2]*40},
{"name": "投币", "symbolSize": Mmean[3]*40},
{"name": "分享", "symbolSize": Mmean[4]*40},
{"name": "点赞", "symbolSize": Mmean[5]*40},
]
links = []
for i in nodes:
for j in nodes:
links.append({"source": i.get("name"), "target": j.get("name")})
c = (
Graph()
.add("", nodes, links, repulsion=8000)
.set_global_opts(title_opts=opts.TitleOpts(title="综合得分计算指标"))
)
c.render_notebook()
综合得分漏斗图
通过综合得分漏斗图可以看出,除了前三名视频以外,其他的视频差距都不是很大,其它的视频的宽度差距不大,4-20名的差距基本很小,这也说明了其名次十分焦灼,变化也比较快。
推测前三名视频是由于挂在了榜上,所以导致其经常被推送已经看排行榜的人观看,即便此视频的标题等对其可能并没有很大的吸引力。
而后续的视频,可能游客就只会对其感兴趣的视频来进行浏览,不同兴趣的用户分别点击不同的视频,
也就导致了后续视频的差距很小,十分焦灼。在此部分,作者的标题与粉丝基数可能会对名次产生较大的影响。
在分析的过程中,我发现很多的视频标题中都带有感叹号与问号等字符,或许可以对一段时间内的排行榜进行数据的采集,然后进行标题的分析,或许也能找到一些吸引游客关注的方案(一定程度内,拒绝标题党)。
附 :
from pyecharts import options as opts
from pyecharts.charts import Funnel
data_fun = [[i[5],int(i[4])] for i in lst[0:20]]
# 创建 Funnel 对象
funnel_demo = (
Funnel(init_opts=opts.InitOpts(
width= '800px',
height='700px',
page_title='page',
))
.add("", data_fun, sort_='descending')
.set_global_opts(title_opts=opts.TitleOpts(title=""),legend_opts=opts.LegendOpts(is_show = False))
.set_series_opts(label_opts=opts.LabelOpts(is_show= True,
position = "right",
#font_size = 12,
))
)
funnel_demo.render_notebook()
游客画像
哔哩哔哩游客画像分析,因为课程设计的时间关系,本次爬虫中主要对于排行榜视频进行爬取,以及部分排行榜作者的信息,所有并没有太多的游客的信息,本部分可视化的信息来自于我的哔哩哔哩后台数据,具有很大的个人倾向,而且数据规模也比较小。
我的视频以游戏视频为主,受众也的确多为16-25岁之间的人群,但很难以置信的是在35-40岁之间还有很多的受众,这对于视频博主来说更改自己的视频策略是非常重要的。
在游客的性别方面,也十分的出乎意料,我的游戏视频并没有明显的性别倾向,但女性观众的比例竟然都大于了3/4,这也是很难以置信的,以后也可以根据自己的受众来合理的制作视频。
对于视频观看途径,毫无悬念的Andrioid占据上风,紧随其后的是PC端与iPhone端,而站外播放几乎没有。
附:
from pyecharts import options as opts
from pyecharts.charts import Pie
c = (
Pie()
.add(
"",
[list(z) for z in zip(["Andrioid端", "H5端", "PC端", "站外端", "iPhone端"], [60, 0, 23, 0, 17])],
center=["30%", "30%"],
radius=['15%', '30%'],
)
.add(
"",
[list(z) for z in zip(["16-25岁", "0-16岁","25-40岁","40岁以上",], [44, 23,21,13])],
center=["70%", "30%"],
radius=['15%', '30%'],
)
.add(
"",
[list(z) for z in zip(["男性观众", "女性观众"], [24, 76])],
center=["30%", "75%"],
radius=['15%', '30%'],
)
.set_colors(["#8be09c","#ffc573","#5ddfff","#ff9db5"])
.set_global_opts(
title_opts=opts.TitleOpts(title="游客画像"),
legend_opts=opts.LegendOpts(
is_show = False
),
)
)
c.render_notebook()
完成度
对于排行榜上的视频,视频的完成度均是特别高的,大多都处于很高的程度,但一些多p的较长的录播类型的视频播放完成度会有些稍低,如第七名。另外较短的视频的完成度均特别高,如第九名。推测视频的完成度也会对视频的综合得分有部分影响,
因为第九名这一视频的其他数据并非是特别突出,但仍在排行榜前列。后面的灰色关联度分析因为视频完成度比较难以无纲量化,并没有对其进行分析,是一缺憾。但哔哩哔哩对播放完成度统计并计算也正体现当前时代的趋势,快餐文化的盛行。
但哔哩哔哩的很多博主还是很令人高兴的,并没有在快文化盛行的今天选择以快文化来吸引流量,还是选择了高质量,这一部分在视频时长与综合得分处进行详解。
附:
from pyecharts import options as opts
from pyecharts.charts import Bar, Grid, Line
Line_Bar_Grid = []
for i in lst[0:10:]:
Line_Bar_Grid.append(int(i[7]))
x_data = ["第{}名".format(i) for i in range(1, 11)]
bar = (
Bar()
.add_xaxis(x_data)
.add_yaxis(
"视频时长",
[i for i in Line_Bar_Grid],
yaxis_index=0,
color="#d14a61",)
.set_global_opts(
legend_opts=opts.LegendOpts(is_show = True, pos_left = '30%',),
yaxis_opts=opts.AxisOpts(
name="视频时长",
position="right",
axisline_opts=opts.AxisLineOpts(
),
axislabel_opts=opts.LabelOpts(formatter="{value}s"),
),)
)
line = (
Line()
.add_xaxis(x_data)
.add_yaxis(
"播放完成度",
[i for i in range percentage_num],
yaxis_index=2,
color="#675bba",
label_opts=opts.LabelOpts(is_show=False),
)
.set_global_opts(legend_opts=opts.LegendOpts(is_show = True, pos_right = '30%',))
)
grid = (
Grid()
.add(bar, grid_opts=opts.GridOpts())
.add(line, grid_opts=opts.GridOpts())
)
grid.render_notebook()
三连
本处数据对排行榜100条数据每10条取其综合得分平均值与点赞投币收藏平均值,来计算综合得分与其余三者的变化趋势,可以显而易见的看出,综合得分与其三者之间存在着明显的线性相关性,这也对我们后续的灰色关联度分析给出了提示,
由于采用取平均值的形式,所以基本没有出现投币大于点赞也就是“币比赞多”的情况,但在点赞投币收藏比例图中对此进行了体现。
可以看出,整体来说点赞大于投币大于收藏,并非是需要获得的投币是最少的,收藏反而是最少的,收藏的视频一般都是可以二次观看的为主,收藏的数量较少应当就是这种原因导致的。
附:
import pyecharts.options as opts
from pyecharts.charts import Line
line_Thread = []
for i in lst[0:80:10]:
play = float(i[2].strip("万"))
line_Thread.append([int(i[4]),int(play*10000),int(i[12]),int(i[8]),int(i[9]),int(i[10])]) # 综合得分,播放,点赞,评论,收藏,投币
print(line_Thread)
print([i[1] for i in line_Thread])
c = (
Line()
.add_xaxis(['1','10','20','30','40','50','60','70',])
#.add_yaxis("播放", [i[1] for i in line_Thread]) # 播放数目
.add_yaxis("点赞", [i[2] for i in line_Thread])
#.add_yaxis("评论", [i[3] for i in line_Thread]) # 评论数过少
.add_yaxis("收藏", [i[4] for i in line_Thread])
.add_yaxis("投币", [i[5] for i in line_Thread])
.set_global_opts(#title_opts=opts.TitleOpts(title="综合得分与评论点赞投币收藏趋势"),
yaxis_opts=opts.AxisOpts(name="综合得分",name_location="center", name_gap=70),
xaxis_opts=opts.AxisOpts(name="排行榜名次",name_location="center"))
)
c.render_notebook()
排行榜点赞、投币、收藏与白嫖的比例
分析前排行榜前20条视频的点赞、投币、收藏与白嫖的比例,可以明显的看出,即便是这么高质量的视频,大多数的人还是选择了白嫖 = =
具体查看每个图表的标题或内容可知,投币大于点赞即“币比赞多”是很少见的情况,常出现于爱心救助,
比如救助流浪狗,流浪猫,等能引起用户共鸣与感动的视频,除此之外另一种“币比赞多”的情况多出现于视频质量极高,制作难度极高,视频质量极高,让人直接把币拱手相让类型的,比如本次榜单上的mad混剪,e3d,时间重映射,发光抖动,放射光线(评论区说的)工程难度极高。
附:
from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.commons.utils import JsCode
b_pie=[]
for i in lst[0:12]:
play = float(i[2].strip("万"))
like = int(i[12])
coin = int(i[10])
favorite = int(i[9])
b_pie.append([i[1],[("白嫖",play*10000 - like - coin - favorite),("点赞",like),("投币",coin),("收藏",favorite)]])
x=10
y=25
pie_demo = (Pie())
for i in b_pie:
x_term = str(x)+'%'
y_term = str(y)+'%'
#print(x_term,y_term)
pie_demo.add(i[0], i[1],center=[x_term,y_term],radius='16%',)
x+=16
if(x>=100):
x=10
y+=50
#pie_demo.set_global_opts(title_opts=opts.TitleOpts(title="白嫖数量"))
pie_demo.render_notebook()
分析
本处的视频分析逻辑并不是很严谨,因为所有的数据均为排行榜视频数据,视频的综合得分均比较高,时长的分析应当有更加大规模的数据来进行测试与处理。但实时的对排行榜数据进行分析也能看出一些趋向。
最终可以得知,视频时长与视频的受欢迎程度是有关系的,在8min到13min附近的视频最为受欢迎,这是很令人意外的,bilibili优质视频的时长都没有太短,值得up主们考量。
篇幅有限,更多详细设计见设计论文
3 最后
项目包含内容
🧿 项目分享:见文末!