我用Python分析5W+视频数据,看看谁才是最咕UP主

通过对B站动画区57407条视频数据的爬取与分析,揭示UP主用户行为特征、热门题材分布及影响播放量的关键因素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前情回顾:
1、爬取bilibili热门视频信息(简易版)
2、爬取bilibili热门视频信息(复杂版)

一、数据介绍及预处理

I. 数据介绍

本次共爬取57407条bilibili动画区播放量破万视频数据信息,时间跨度为2020年1月1日~2020年12月31日,主要字段如下(其中,粉丝数以2021年1月1日为准):

字段名含义
Up_erUP主名
title视频名称
pubdate视频发布时间
aid视频专属ID
midUP主专属ID
video_time视频时长/秒
likes点赞数
coins硬币数
favorites收藏数
shares分享数
views播放量
comments评论数
danmu弹幕数
fans粉丝数
type所在分区(MAD-AMV、MMD、短片-手书-配音、手办-模玩)

分区介绍
MAD是利用既存的素材(一般为动画或CG图),加以修改,剪接等“二次创作”并配乐而制作成的影片。
AMV为动画音乐视频(Anime Music Video,简称AMV)是由一个或几个动画组成,并搭配一首歌曲的短片
MMD全称为MikuMikuDance,是由樋口优开发的一款免费的3D动画制作软件,3D的二次元作品通常都是MMD

爬取注意:
1、在使用简易版爬取时,time_from和time_to的最长时间间隔为3个月
2、简易版爬取会有频次限制,不过IP封禁时间较短,约1个小时
3、UP主粉丝爬取的API地址为https://api.bilibili.com/x/relation/stat?vmid=
+该UP主的mid

II. 数据预处理

首先,读入数据,查看数据总缺失值

import pandas as pd
import os
os.chdir('C:/users/dell/Desktop/')
data = pd.read_csv('bilibili.csv',parse_dates=['pubdate'],encoding='gb18030') #parse_dates将pubdate解析为日期格式
data.isnull.sum()

从结果来看,Up_er存在20条缺失数据,likes、coins等字段缺失36条数据。

其次,删除缺失值,并查看数值各字段基本情况

data1 =  data.dropna()
pd.set_option('display.float_format','{:,.0f}'.format) #不显示科学计数
numeric_variable = ['video_time','likes','coins','favorites','shares','views','comments','danmu','fans']
data1[numeric_variable].describe()

现有57351条数据,共删除56条缺失信息。此外,上图还显示了各数值变量的五数分布。以播放量为例,最低视频播放量刚刚达到入选标准,而最高视频播放量将近2800万。

二、UP主用户行为分析

在这个流量的时代,UP主通过发布视频来获取流量,那么选择一个合适的发布时间,视频播放量会不会更高呢?在此,我统计了一天和一星期中各时间段发布的视频数及平均播放量如下所示。

I. 视频发布时间分布特征
  • 一天中各时间段视频发布数与平均播放量
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
plt.style.use('ggplot')
plt.rcParams['font.sans-serif']=['kaiti']
data1 = data1.set_index('pubdate')   #resample函数需要日期型字段作为索引
data1['count'] = 1
hour_resample1 = data1['count'].resample('H').sum().to_frame() #按小时对视频个数采样
hour_resample2 = data1['views'].resample('H').mean().to_frame() #按小时对视频平均播放量采样
hour_resample = pd.concat([hour_resample1, hour_resample2], axis=1)
hour_resample.index = hour_resample.index.map(lambda x: str(x).split(' ')[1]) #分离出小时数据
pd.set_option('display.precision', 0)
hour_group = hour_resample.reset_index().groupby(
    'pubdate').agg({'count': np.sum, 'views': np.mean}) #按小时聚合

def plot_line_bar(data, y1_max, y1_range, y2_max, y2_range):  #双轴组合图
    x = data.index
    y1 = data['count']
    y2 = data['views']
    f, ax1 = plt.subplots(figsize=(10, 5))
    ax1.bar(x, y1, color='#348498', label='发布视频数')
    ax2 = ax1.twinx()  # 双轴图关键
    ax2.grid(False)  # 去掉第二条线的网格
    ax2.plot(x, y2, label='平均播放量', color='#F75940', marker='o')
    ax1.set_yticks(np.arange(0, y1_max, y1_range)) #设置主坐标轴范围
    ax1.set_ylabel('发布视频数')  
    ax2.set_yticks(np.arange(0, y2_max, y2_range))   #设置次坐标轴范围
    ax2.set_ylabel('平均播放量')
    for tl in ax1.get_xticklabels():  # 旋转横轴标签
        tl.set_rotation(90)
    ax1.legend(loc=2)
    ax2.legend(loc=1)  # loc1代表放置右上方
    plt.title('2020年B站动画区视频发布时间及平均播放量分布')
    plt.show()

if __name__ == '__main__':
    plot_line_bar(hour_group, 6000, 1000, 250000, 50000)

在这里插入图片描述
视频发布数的角度来看,有3个高峰期,分别为凌晨12点至1点中午12点至1点傍晚6点至7点。其中,凌晨发布视频数高达4000+,但在这时间段发布视频,观众相对较少,且到第二天又会有新的视频涌现出来,视频不容易被推送到首页,故导致平均播放量不够理想。

平均播放量的角度来看,高峰期主要集中在上午11点至12点。这段时间刚好是大家即将干饭的时候,摸鱼的心情早已按捺不住,打开B站刷刷视频,各类视频播放量较为可观。UP主若想要收获较高的播放量,可考虑在这段时间发布视频。

  • 一周中各时间段视频发布数与平均播放量
data1['count']=1
data1['weekday']=data1.index.weekday
pd.set_option('display.precision',0)
weekday_group = data1.groupby('weekday').agg({'views':np.mean,'count':'count'})

if __name__=='__main__':
	plot_line_bar(weekday_group, 15000, 3000, 250000, 50000)

在这里插入图片描述
从上图来看,大多UP主选择周四、周五发布视频,且这两天的平均播放量也稍高于其他天数。

II. 各区UP主平均更新周期

看完UP主平常发布视频时间分布后,再来看看动画区哪些UP主更新最频繁,哪些UP主最咕咕咕?

def caculate_period(x):  #计算各UP主平均更新周期
    if len(x) >= 10:  #过滤发布视频数低于10的UP主
    	 #相邻发布日期相减获取相隔天数(x.days也可以,但对于更新周期小于1天博主误差较大)
        diff_time = x.iloc[:, 2].diff().dropna().map(lambda x: round((x.days*24*3600+x.seconds)/3600/24,2)) 
        return np.mean(diff_time)
    else:
        return 0

up_period = data1.groupby(['Up_er', 'type']).apply(caculate_period).to_frame().reset_index()
up_period = up_period[up_period[0] > 0]

def get_five(x):
    return x.iloc[:5, :]

def get_uper(up_period, booler):
    update_group = up_period.sort_values(by=0, ascending=booler)
    update_group.columns = ['UP主', '分区', '平均更新周期/天']
    update_group = update_group.groupby('分区').apply(get_five)
    return update_group   

if __name__=='__main__':
    update_head = get_uper(up_period,True)   #返回最勤UP主
    update_tail = get_uper(up_period,False)  #返回最咕UP主
  • 各区最勤UP主
    在这里插入图片描述

从上图可知,短片-手书-配音区的最勤UP主平均更新周期最短,约半天即可更新一次视频。其中,更新最快的UP主为嘿基尼,平均0.26天(6.24小时)更新一次视频;其次,则是手办-模玩区更新周期较短,平均约一天半;而MAD-AMVMMV由于制作难度较高,最勤的UP主也需要约2天半才能更新一次视频。

  • 各区最咕UP主
    在这里插入图片描述

从上图来看,MMD区最咕UP主的平均更新周期相对较短,约为33天;而短片-手书-配音区和手办-模玩区最咕UP主的平均更新周期相差几乎一致,约为36天MAD-AMV区最咕UP主平均更新周期最长,约为40天

三、最热题材分布

动画区的UP主喜欢用哪些番剧素材进行二次创作呢?

统计规则:视频标题中出现番剧则计数1次

考虑某些视频标题是直接以剧中人物命名,为更为精确地统计,除番剧本身名称外,为每个番剧设置若干搜索关键词,形式如下:

巨人|利威尔|三笠
咒术回战|五条悟|宿傩

import csv 
from itertools import islice
import pandas as pd
import numpy as np
writer = pd.ExcelWriter('热门番剧总信息.xls')
fanjus = [line.strip() for line in open('fanju.txt',encoding='utf-8').readlines()]    #搜索关键词文件
hot_fanju = []
for fanju in fanjus:
    key_words = fanju.split('|')
    df = []
    csv_file=open('bilibili.csv',encoding='gb18030')    
    csv_reader_lines = csv.reader(csv_file) 
    for line in islice(csv_reader_lines,1,None):
        if any(words in line[1] for words in key_words):
            df.append(line)
    df1 = pd.DataFrame(df,columns=['Up_er','title','pubdate','aid','mid','video_time',
                                  'likes','coins','favorites','shares','views','comments','danmu','fans','tpye'])
    df1.to_excel(writer,sheet_name = key_words[0],index=False)
    views_sum = df1['views'].apply(np.int).sum()
    hot_fanju.append([key_words[0],len(df1),views_sum])
    print('{}总播放量为:{}'.format(key_words[0],str(views_sum/10000)+'万'))
hot_fanju = pd.DataFrame(hot_fanju,columns=['番剧','投稿数','播放量'])
hot_fanju.to_excel('热门番剧投稿数及总播放量.xlsx',encoding='gb18030',index=False)
writer.save()
writer.close()
csv_file.close()

注:以下数据仅代表B站动画区番剧热度,并不代表全站,且数据的准确性很大程度上取决于搜索关键词的设置。
在这里插入图片描述
从上图来看,JOJO无疑是2020年动画区UP主最为青睐的题材!凭借着1069个视频投稿数及1.5亿的总播放量,位居动画区热门题材榜首。其次,作为2019年最火新番《鬼灭之刃》依旧保持着较高的热度,以515个视频投稿数及7185.6W的总播放量分别位居投稿数第二位和总播放量第三位。与此同时,作为2020年续作系列中突出的《进击的巨人》,以较低的视频投稿数,视频总播放量超过了《凹凸世界》及《海贼王》。此外,2020年10月新番《咒术回战》也取得较为不错的成绩,以2969.2W总播放量位居第七位。

四、影响播放量因素探索

对于一个UP主来说,为了提高播放量可谓时绞尽脑汁。那哪些因素会影响播放量的高低呢?

I. 视频时长与播放量关系探索

首先,来看看是不是视频越长,播放量约高呢?我绘制了4种不同播放量下的视频时长分布图,如下图所示

data = pd.read_csv('bilibili.csv',parse_dates=['pubdate'],encoding='gb18030')
data1 =  data.dropna()
views = [100000,500000,1000000,10000000]
colors = ["#5bd1d7","#348498","#004d61","#ff502f"]
plt.figure(figsize=(10,5))
for view,color in zip(fans,colors):
    video_time = data1[(data1['video_time']<=1200)&(data1['views']>=view)]['video_time']
    sns.kdeplot(video_time,label='播放量>'+str(int(view/10000))+'万',color=color)
plt.legend(frameon=False)
plt.title('不同播放量下的视频时长分布')
plt.xlabel('视频时长/秒')
plt.ylabel('核密度值')
plt.show()

在这里插入图片描述
从图中,我们可以知道大多数视频的时长都在200秒以内,而对于播放量大于1000万的视频中,也有视频时长超过10分钟的,但频率较低,可忽略。此外,我们还观察到四种颜色的曲线有逐渐向右移动的趋势,表明当视频时长小于200秒时,适度增加视频时长,会提高一定的播放量!

II. 粉丝数与播放量关系探索

考虑到本次搜集的粉丝数是以2021年1月1日为基准,由于UP主的粉丝数可能因为某个视频而暴涨,不能以此刻的粉丝数与以往的视频数据进行分析。在此,以各大UP主最近更新的视频播放量与粉丝数进行组合分析。

data = pd.read_csv('bilibili.csv',parse_dates=['pubdate'],encoding='gb18030')
data1 =  data.dropna()
data1 = data1.sort_values(by='pubdate',ascending=False)  #以pubdate逆序排列
def get_first(x):
    return x.iloc[0,:]
df1 = data1.groupby('Up_er').apply(get_first)

def fans_label(x):
    if x<100000:
        return '<10万粉'
    elif 100000<=x<500000:
        return '10-50万粉'
    elif 500000<=x<1000000:
        return '50-100万粉'
    elif 1000000<=x<=5000000:
        return '100-500万粉'
    else:
        return '>500万粉'
df1['fans'] = df1['fans'].apply(fans_label)
df1['views'] =np.log(df1['views'])  #为箱线图更加美观,对播放量取对数

plt.figure(figsize=(10,5))
plt.rcParams['font.sans-serif']=['kaiti']
sns.boxplot(x='fans',y='views',data=df1,
            order=['<10万粉','10-50万粉','50-100万粉','100-500万粉','>500万粉'],palette='Set3',)
plt.xlabel('类型',labelpad=10)
plt.ylabel('播放量(取对数)')
plt.title('不同类型UP主视频播放量分布')
plt.show()

在这里插入图片描述
从箱线图可知,随着粉丝基数的增加,不同类型的UP主视频平均播放量在逐渐增大,这一点与常识相符。但此图较为有趣的一点是:10万粉以下的箱子出现了很多的异常值,表明10万粉以下的UP主创作了非常多的爆火视频!,一方面是反映出新人UP主本身具有高创造力,能够抓住观众的胃口;另一方面也反映出B站对新人友好的推送机制,鼓舞新人创作。(只要视频质量过关,B站会经常在首页推送一些新人视频)

III. 岭回归分析

以上均是描述统计分析,以下就展开定量分析来探索影响播放量的因素。由于数据涉及粉丝数,仍以每个UP主最近更新的视频信息为基础数据。在回归之前,我们先来了解各变量与播放量的相关关系。

import seaborn as sns
import pandas as pd
data = pd.read_csv('bilibili.csv',parse_dates=['pubdate'],encoding='gb18030')
data1 =  data.dropna()
data1 = data1.sort_index(ascending=False)
def get_first(x):
    return x.iloc[0,:]
up_group = data1.groupby('Up_er').apply(get_first)

pd.set_option('display.float_format',lambda x:'.2%f'%x)
plt.figure(figsize=(10,5))
corr_variable = ['video_time','likes','coins','favorites','shares','views','comments','danmu','fans']
sns.heatmap(up_group[corr_variable].corr(),cmap='YlOrRd',annot=True)
plt.title('各数值变量相关系数热力图')
plt.show()

在这里插入图片描述
与播放量呈现较高正相关性的变量有点赞数、硬币数、收藏数、分享数。考虑到三连信息之间可能会存在多重共线性,故选用岭回归来探究播放量的影响因素。首先,需要确定岭回归的重要参数alpha

1. 以可视化方式初步确定参数alpha

from sklearn import preprocessing
from sklearn.linear_model import Ridge, RidgeCV
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False  #避免负号显示异常
plt.style.use('ggplot')

#首先对原数据标准化
numeric_variable = ['video_time','likes','coins','favorites','shares','views','comments','danmu','fans']
up_group_scale = pd.DataFrame(preprocessing.scale(up_group[numeric_variable]),columns=numeric_variable)
y = up_group_scale['views']
X = up_group_scale[['video_time','likes', 'coins', 'favorites','shares', 'comments', 'danmu', 'fans']]

alphas = 10**np.linspace(-3, 3, 100)
ridge_cofficients = []
for alpha in alphas:
    ridge = Ridge(alpha=alpha,  normalize=True)
    ridge.fit(X, y)
    ridge_cofficients.append(ridge.coef_)

plt.plot(alphas, ridge_cofficients)
plt.xscale('log')
plt.axis('tight')
plt.title('alpha系数与岭回归系数的关系')
plt.xlabel('Log Alpha')
plt.ylabel('Cofficients')
plt.show()

在这里插入图片描述
从上图可知,alpha在100附近时,所有自变量系数趋于稳定,接下来以交叉验证进一步确诊最佳alpha

2. 交叉验证选取最佳alpha

alphas = 10**np.linspace(-3,3,100)
rr_scaled = RidgeCV(alphas = alphas, cv =5, normalize = False)
rr_scaled.fit(X, y)
rr_scaled.alpha_

在这里插入图片描述
从结果可知,最佳alpha为123

3. 回归结果

from regressors import stats 
reg_Ridge = Ridge(alpha=123,normalize=False)
reg_Ridge.fit(X,y)
stats.summary(reg_Ridge,X,y)

在这里插入图片描述
从回归结果来看,只有变量video_time未通过显著性检验,点赞数对视频播放量影响程度最大。而粉丝数的回归系数为负,这也验证了之前箱线图的结果,可解释为由于B站的首页推广机制,较低的粉丝有着较高的播放量。

展望与思考

考虑到篇幅问题,本文还有一些有待完善的地方:

1、在爬取字段方面,其实还可以获取到视频投稿时间,这样就可以计算出平均审核时间。结合本次分析结论,为UP主选个最佳发布时间也能提供一定的依据(前提是本事视频质量过关的情况下)。另外,更进一步,可以尝试用可视化的方式具体呈现出审核时间和视频时长之间究竟呈现出怎样的关系等等

2、coins回归系数为负,模型有待完善。考虑硬币数与播放量之间的关系可知:硬币数多,则播放量高;但播放量高了之后,硬币数自然也会多,说明此时二者是相互影响的,这也是回归分析容易忽略的内生性问题!后续可考虑寻找工具变量解决。

以上就是本次分享的全部内容~

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值