项目地址 https://github.com/Adydio/DataAnalysis_CloudMusic

网易云音乐评论数据分析

1.背景与目标

今年四月底,各大网友“册封”华语乐坛“新四大天王”。这四位分别是华晨宇,王琳凯,Capper和姜云升。值得注意的是,这一次“新四大天王”的评选是具有一定审丑性质的。以本文即将分析的Capper的作品《雪distance》为例,这首歌歌词质量低、审美价值低,却在近期爆火。我认为这些作品的爆火,与歌手在场下的“迷惑言论”和近期对于这些歌手或者这些作品的负面爆料有关,并且我拟用自己获取的数据更好地说明这一点。

网易云音乐是一款火爆的听歌软件,聚集了大量年轻受众。值得一提的是,相比于QQ音乐等一流听歌软件,网易云音乐的评论数往往是前者的好几倍,我认为这其中的原因是微妙而复杂的。作为一位“村龄”五年的网易云音乐深度用户,我认为网易云爆火原因之一就是它善于营造“氛围”。如今的年轻人物质丰富但也面临着前所未有的压力。情感的压抑,生活的不顺让他们诉诸歌曲,寻求慰藉。而网易云音乐创造的“云村”,“抱一抱”等小功能,还有简约不简单的页面设计,小众但精华的曲库,精准的每日推送以及走心的听歌报告给了年轻人最好的答案。而我此次作业的目标,就是结合时事分析华语乐坛“新四大天王”爆火的原因,发掘数据背后的一些规律并且给出自己的评价和解释,再通过自己收集的数据,结合网易云音乐软件本身的特点,探究网易云音乐评论的特点并且探讨网易云音乐大环境的变化。

2.获取数据

运用python,调用网易云音乐评论的API,整理数据得到Excel文件。

《雪Distance》的处理是在pre-processing.ipynb中完成的。这是调用API后再用自己编写的函数整合复杂的嵌套json文件得到的结果,代码是经过高度简化的,原理上利用我的这些代码可以获取网易云音乐任何一首歌曲的所有评论。

部分来源于课程群的数据如stopwords.txt,comments.txt,hot_comments.txt。我只选用stopwords.txt并且基于实际情况进行了大量改动。原因在于这些数据大多过于老旧,评论几乎都是2019年之前的数据,而且部分歌曲如周杰伦的歌曲早就下架。对于分析“热评”性质有较大的干扰。为了得到更精准的数据,我还是使用自己爬取的数据。具体爬取过程见AcquireComments.ipynb,里面有详细的API信息和自己编写的函数,原理上可以用这些方法获取任意一个歌单的所有歌曲详细信息。

3.数据分析

3.1 《雪Distance》歌曲评论全面分析

3.1.0 数据读取与预处理

我们读取歌手Capper的新作《雪Distance》的评论。

xd <- read.table(file = "xue_distance_final.csv",header = TRUE,encoding = 'UTF-8',sep = ",",comment.char = "",quote = "",fill = TRUE, allowEscapes = TRUE) # 读入数据
xd <- xd[,-11]
xd <- xd[,-1]
xd <- xd[,-1]
xd <- xd[,-1]# 删除前三列和最后一列,这是在调用api之后再处理多出来的
summary(xd)
##     userid           commentid           username           content         
##  Length:85502       Length:85502       Length:85502       Length:85502      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##    timeStr           likedcount             ip           
##  Length:85502       Length:85502       Length:85502      
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character
变量解释
变量名 含义
userid 发表评论者的id
commentid 留言的编号
username 发表评论者的昵称
content 评论内容
timeStr 发表评论的日期
likedcount 点赞次数
ip ip属地

变量的类型有的不正确,需要修改:

xd[,"timeStr"] <- as.Date(xd[,"timeStr"])
xd[,"userid"] <- as.numeric(xd[,"userid"])
xd[,"likedcount"] <- as.numeric(xd[,"likedcount"])
xd[,"commentid"] <- as.numeric(xd[,"commentid"])
cat("去除NA前的评论数",nrow(xd))
## 去除NA前的评论数 85502
xd <- na.omit(xd)
cat("\n去除NA后的评论数",nrow(xd))
## 
## 去除NA后的评论数 84941

因为爬数据过程中,userid难免会带上奇怪的符号,而样本量足够大,我们索性直接去掉含有NA的行,对结果影响不大。

3.1.1 评论个数随时间的分布

为了研究歌词的性质,我们先从个例开始研究。《雪Distance》是时下讨论度非常高的一首rap歌曲,这首歌于2023年3月1日在网易云音乐上发布,而在接下来的两个月中,这首歌先是被曝有重大抄袭嫌疑,再就是在4月9日歌手Capper在沈阳巡演时发表攻击性极强的言论引发网民不满,这在另一种程度上促进了该歌曲进一步“出圈”爆火,因此,我们先来看看评论数量随时间的分布情况。

library(ggplot2)
comment_distribution_time <- ggplot(data = xd, aes(x=timeStr)) + geom_histogram(aes(y=..density..), color = "black", fill="white", binwidth = 1) + 
 geom_density(alpha=0.2, fill="green") + labs(x = "时间", y = "评论数") + ggtitle("评论数量随时间的分布") + theme(plot.title = element_text(hjust = 0.5))
comment_distribution_time

这个结果能反映一些事实。首先,歌曲在前20天的表现足以说明歌手Capper的热度之高。在新歌发布的前四天就有上万条评论的迅速累积,以及在3月4日单日评论近万,这是非常少见的,可见歌手本身是具有大量的粉丝群体的。在前四天热度短暂高潮后,随着越来越多的人涌入歌曲,外加歌曲已经积累了两万条评论的基础,该歌曲涉嫌抄袭也引发讨论,这一切导致在20天左右歌曲热度再次上升,最后热度渐渐饱和,每天的评论数稳定增长,而每日的增长数有明显下行趋势。这仿佛是每一首流行歌曲的归宿,除非有特殊的事件发生,才能再一次将歌曲的热度唤醒。而我们看到,在四月中旬,歌曲的热度奇迹般回升,歌曲迎来了又一次热度的小高潮。我们观察到歌曲每日评论数的“鞍点”就在4月10日左右,正好对应歌手Capper极具争议的演唱会时间4月9日。为了进一步展现此次事件与歌曲热度的相关性,我将继续分析歌曲评论的群体变化以及评论内容的情感变化。

3.1.2 初见端倪,揭秘“狂热粉丝”

我们开始着眼于观察评论群体的分布。众所周知,在各大音乐软件都存在着多多少少的“饭圈”现象。死忠粉刷评论这件事情也是喜闻乐见了。在数以万计的评论中,必然会出现狂热的粉丝刷评论的现象。我们来观察狂热粉丝的数量以及他们活跃时间的分布,即寻找发表多次评论的用户。

xd_user <- table(xd$userid) # 将所有的评论按userid统计频数
xd_user <- sort(xd_user, decreasing = TRUE)
xd_user[1:35]
## 
## 8455015649 3963628262 8430449562 5114176147 1930671410 7890401962  566213081 
##        129        114        100         95         93         93         84 
##  541063310 1366699586  622107542 1923260533 4945096989 5111163569 3327610983 
##         79         79         75         75         71         71         70 
##  625745696 1681335566 8473380483 1956659800 2131321920 8460446466 1419626544 
##         67         66         66         60         56         53         52 
##  430208557 3255555684 8471492830 1557313181 1922260450 8469828249 1983875970 
##         51         51         51         48         48         48         47 
## 8132241849 8537369422  541975316 1507307204 1750245473 7923187383 1854718965 
##         47         47         46         46         46         46         45

我们发现死忠粉数量惊人之多!如果将发10条评论以上的评论者定义为“狂热粉丝”,我们来计算一下狂热粉丝的评论有多少:

fans <- xd_user[xd_user[]>=10]
ans <- 0
for (i in 1:length(fans)) {
  ans <- ans + fans[i]
}
cat("狂热粉丝贡献的评论数:",ans)
## 狂热粉丝贡献的评论数: 11328

这是一个很大的数!要知道绝大多数流行歌曲的评论数不过寥寥数百上千,这个数目无疑是惊人的。此外我们再看看这些死忠粉都发了些什么:

for (i in 1:5) {
  print(xd[xd[,"userid"]==as.numeric(labels(xd_user)[[1]][i]),"content"][1:10])
}
##  [1] "见一面好不好 我站远点"                 
##  [2] "有无数个瞬间我还是很想你"              
##  [3] "后来我们的关系不删除 不打扰 熟悉且陌生"
##  [4] "晚安之后 你在睡觉 我在想你"            
##  [5] "我理解 她比我漂亮"                     
##  [6] "起雾了 看不清你"                       
##  [7] "很少想起你了 挚爱"                     
##  [8] "因为相信你不会离开我 所以我是小闹 小作"
##  [9] "睡不着的凌晨回不去的我们"              
## [10] "来日方长我们带着诚意慢慢来"            
##  [1] "那些委屈难过像耳光一样抽得我哑口无言."        
##  [2] "你要站在我的位置你就知道我的感受."            
##  [3] "等到我们终于懂得站在彼此的角度看待问题时."    
##  [4] "或许你会遇到更好的人可我除了你看谁都差点意思."
##  [5] "我无法回答那些尖锐的话除了眼泪就是沉默."      
##  [6] "你说我不懂于是我心千斤重."                    
##  [7] "见一面吧我站远点看一眼就好."                  
##  [8] "不想揭穿不代表我看不透."                      
##  [9] "好像大家都不太珍惜对自己好的人."              
## [10] "2023忘掉关于你的所有."                        
##  [1] "盆儿爱你?"                                                                                                                                                                                             
##  [2] "capper不是我对象我真的#恶心#头晕#面色苍白#出汗#腹痛#血压下降#休克#昏迷#体温增高#浑身无力发冷#全身酸痛#没有食欲#昏昏欲睡#腹泻#呕吐#眼睛酸胀#痛关节疼痛#咳嗽#咳痰#胸痛#恶寒#头痛[快哭了][快哭了][快哭了]"
##  [3] "capper一定要快乐!!!听见了吗!"                                                                                                                                                                      
##  [4] "capper不是我对象我真的#恶心#头晕#面色苍白#出汗#腹痛#血压下降#休克#昏迷#体温增高#浑身无力发冷#全身酸痛#没有食欲#昏昏欲睡#腹泻#呕吐#眼睛酸胀#痛关节疼痛#咳嗽#咳痰#胸痛#恶寒#头痛[快哭了][快哭了][快哭了]"
##  [5] "capper是我对象我就#舒坦#头不昏了#眼不花了#千里眼#一秒逃离#胸口无不适#精神振奋#顺风耳#食欲大增#好奇心#发疯#不再克制#脚步轻盈#温柔#暖心#宁静#热爱生活#清风徐来#来去之间[舔屏][舔屏][舔屏][流泪]"         
##  [6] "capper是我对象我就#舒坦#头不昏了#眼不花了#千里眼#一秒逃离#胸口无不适#精神振奋#顺风耳#食欲大增#好奇心#发疯#不再克制#脚步轻盈#温柔#暖心#宁静#热爱生活#清风徐来#来去之间[舔屏][舔屏][舔屏][流泪]"         
##  [7] "capper一定要快乐!!!听见了吗!"                                                                                                                                                                      
##  [8] "capper是我对象我就#舒坦#头不昏了#眼不花了#千里眼#一秒逃离#胸口无不适#精神振奋#顺风耳#食欲大增#好奇心#发疯#不再克制#脚步轻盈#温柔#暖心#宁静#热爱生活#清风徐来#来去之间[舔屏][舔屏][舔屏][流泪]"         
##  [9] "capper不是我对象我真的#恶心#头晕#面色苍白#出汗#腹痛#血压下降#休克#昏迷#体温增高#浑身无力发冷#全身酸痛#没有食欲#昏昏欲睡#腹泻#呕吐#眼睛酸胀#痛关节疼痛#咳嗽#咳痰#胸痛#恶寒#头痛[快哭了][快哭了][快哭了]"
## [10] "capper不是我对象我真的#恶心#头晕#面色苍白#出汗#腹痛#血压下降#休克#昏迷#体温增高#浑身无力发冷#全身酸痛#没有食欲#昏昏欲睡#腹泻#呕吐#眼睛酸胀#痛关节疼痛#咳嗽#咳痰#胸痛#恶寒#头痛[快哭了][快哭了][快哭了]"
##  [1] "哥是你力气大还是大象力气大啊" "哥是你力气大还是大象力气大啊"
##  [3] "哥是你力气大还是大象力气大啊" "哥是你力气大还是大象力气大啊"
##  [5] "哥是你力气大还是大象力气大啊" "哥是你力气大还是大象力气大啊"
##  [7] "哥是你力气大还是大象力气大啊" "哥是你力气大还是大象力气大啊"
##  [9] "哥是你力气大还是大象力气大啊" "哥是你力气大还是大象力气大啊"
##  [1] "你不会偷偷下班了吧"                  "不要偷偷自己下班 要记得给大家说再见"
##  [3] "不要偷偷自己下班 要记得给大家说再见" "不要偷偷自己下班 要记得给大家说再见"
##  [5] "不要偷偷自己下班 要记得给大家说再见" "不要偷偷自己下班 要记得给大家说再见"
##  [7] "下班了要说一声呀"                    "能不能给发个颜文字"                 
##  [9] "能不能给发个颜文字"                  "能不能给发个颜文字"

评论里有大量意义不明并且重复的言论。狂热粉丝贡献了数以万计的评论,可见歌曲评论区的水分之多。我们知道,在网易云音乐里面,对于所谓“网红歌曲”,评论过万的歌曲是一个分水岭,短时间内过万就表示这首歌彻彻底底的“红了”,从而能够吸引更多的听众,形成正反馈效应,在短时间内还可以继续收割一部分听众。而一些真正有内涵的歌曲,“去饭圈化”的好歌曲,评论数增长往往不是快餐式的急剧增长,而是随着口碑的积累缓慢爬升。

评论数排名前五的狂热粉丝们给我们展示了评论区“水评论”的众生相。既有疯狂输出情绪的,又有脑残支援歌手的饭圈文案,还有一些无意义的灌水评论。从这里就可以看出,在狂热粉丝的加持下,这首《雪Distance》的热度一定程度上是被“炒起来的”,也侧面说明了这首歌的“网红“性质早已逾越了歌曲本身的性质。

接下来,我们感兴趣的是,这些狂热粉丝都活跃在什么时候?这些评论一般在何时出现呢?这或许能反映一些问题。

fans_id <- as.numeric(labels(fans)[[1]]) #狂热粉丝的id
fans_comments <- xd[xd[,"userid"] %in% fans_id,] #狂热评论的切片
fanscomments_distribution_time <- ggplot(data = fans_comments, aes(x=timeStr)) + geom_histogram(aes(y=..density..), color = "black", fill="white", binwidth = 1) + 
 geom_density(alpha=0.2, fill="red") + labs(x = "时间", y = "评论数") + ggtitle("狂热粉丝评论数量随时间的分布") + theme(plot.title = element_text(hjust = 0.5))
fanscomments_distribution_time

结果令人惊讶!灌水评论几乎集中于3月5日,几乎可以断定在这一天有系统的“水军”刷评论的行为,而后面的大量的“狂热粉丝”更有可能是路人性质的,并不是以刷评论为动机的。我们回顾一下先前打印的狂热粉丝的评论内容,除了第一位粉丝发的内容与情感有关,其余粉丝的内容评论毫无价值可言,甚至有大量重复。考察一下他们发布的日期,

xd[xd[,"userid"]==as.numeric(labels(xd_user)[[1]][1]),"timeStr"][10]

[1] “2023-05-02”

xd[xd[,"userid"]==as.numeric(labels(xd_user)[[1]][2]),"timeStr"][10]

[1] “2023-05-06”

xd[xd[,"userid"]==as.numeric(labels(xd_user)[[1]][3]),"timeStr"][10]

[1] “2023-03-05”

xd[xd[,"userid"]==as.numeric(labels(xd_user)[[1]][4]),"timeStr"][10]

[1] “2023-03-05”

xd[xd[,"userid"]==as.numeric(labels(xd_user)[[1]][5]),"timeStr"][10]

[1] “2023-03-05”

march5th_comments <- table(xd$timeStr)["2023-03-05"]
march5th_fans_comments <- table(fans_comments$timeStr)["2023-03-05"]
cat("3月5日评论总数为",march5th_comments,",3月5日水军评论总数为",march5th_fans_comments)

3月5日评论总数为 8405 ,3月5日水军评论总数为 5349 发现无意义评论更多聚集在3月5日。可以断定,这一天的评论绝大多数是“水军出击”给刷出来的。这对于歌曲热度提升有着推波助澜的作用,但这种现象对于“饭圈化”尚不严重的网易云音乐来说,无疑是相当可悲的。

3.1.3 歌曲风评研究之评论情感变化

我们将目光转向歌曲的“风评”变化。换言之,接下来我们即将研究评论的关键词和情感的变化。一个无法反驳的事实是,这首《雪Distance》的风评如今是非常烂的,我们在意的是导致此般现象的转折点在哪里,导致这种变化的原因是什么。首先,我们研究评论的情感变化。

在研究以下问题之前,我们先删除掉这些重复的灌水评论。

我们使用百度的paddlepaddle预训练模型,用模型判断用户评论信息的情感态度,分析消极和积极的占比。在EmotionAnalysis.ipynb中,我们得到了含有用户情感态度标签的文件xue_emotion.csv。值得说明的是,该模型对于一串字符会给出相应的正向、负向情感概率。由于评论里含有大量如“丁真”“泰裤辣”“依托答辩”等负面感情倾向的网络用语或影响判断,这里我采用的方法是,当且仅当正向概率大于一个不小于0.5的参数\(p\)时,才认定这是一个正向情绪的评论。

我们接下来读取xue_emotion.csv,该文件是取\(p=0.6\)运行得到的,并且严谨起见,如果一个用户评论数不少于10条,他的评论将被移除。

xd1 <- read.table(file = "xue_emotion.csv",header = TRUE,encoding = 'UTF-8',sep = ",",comment.char = "",quote = "",fill = TRUE, allowEscapes = TRUE)
xd1 <- xd1[-which(xd1[,'userid'] %in% fans_id),]
xd1 <- xd1[,-1]
xd1[,"timeStr"] <- as.Date(xd1[,"timeStr"])
xd1[,"userid"] <- as.numeric(xd1[,"userid"])
xd1[,"new_sentiment_label"] <- as.numeric(xd1[,"new_sentiment_label"])
xd1 <- na.omit(xd1)
emotion_distribution <- aggregate(xd1[,"new_sentiment_label"], by = xd1["timeStr"], mean)
summary(emotion_distribution)
##     timeStr                 x         
##  Min.   :2023-03-01   Min.   :0.3997  
##  1st Qu.:2023-03-18   1st Qu.:0.4574  
##  Median :2023-04-04   Median :0.4919  
##  Mean   :2023-04-04   Mean   :0.4912  
##  3rd Qu.:2023-04-21   3rd Qu.:0.5223  
##  Max.   :2023-05-09   Max.   :0.5737
emotion_distribution_figure <- ggplot(data = emotion_distribution, aes(x = timeStr, y = x)) + geom_point(colour = "#FF4500") + geom_smooth(colour = "#6495ED") + labs(x = "时间", y = "正向情感指标") + ggtitle("正向情感指标数量随时间的分布(p=0.6)") + theme(plot.title = element_text(hjust = 0.5)) + ylim(0.38,0.6)
emotion_distribution_figure

从图中我们看出,评论在歌曲发布初期好评如潮,可是后面的负面评论越来越多。同样地,我们再分别命参数\(p=0.5,0.55,0.65,0.7\)做同样的操作,得到数据框df50,df55,df65,df70。由于同样操作过多,洗数据代码略去不显示。

xd_ <- rbind(xd50,xd55,xd65,xd70)
xd_ <- aggregate(xd_[,"new_sentiment_label"], by = c(xd_["timeStr"], xd_["p"]), mean)
summary(xd_)
##     timeStr                 p                x         
##  Min.   :2023-03-01   Min.   :0.5000   Min.   :0.2747  
##  1st Qu.:2023-03-18   1st Qu.:0.5375   1st Qu.:0.4331  
##  Median :2023-04-04   Median :0.6000   Median :0.4836  
##  Mean   :2023-04-04   Mean   :0.6000   Mean   :0.4801  
##  3rd Qu.:2023-04-22   3rd Qu.:0.6625   3rd Qu.:0.5360  
##  Max.   :2023-05-09   Max.   :0.7000   Max.   :0.6458
ggplot(data = xd_,aes(x = timeStr, y = x, colour = factor(p))) + geom_point() + coord_cartesian() + facet_wrap(~ p) + theme_bw() + geom_smooth(colour = "#6495ED") + labs(x = "时间", y = "正向情感指标") + ggtitle("正向情感指标数量随时间的分布") + theme(plot.title = element_text(hjust = 0.5)) + ylim(0.28,0.6) 

图表的意义是十分清晰的。无论参数\(p\)如何选择,评论的感情分布都是现在三周之内上涨,再逐日降低。这样的原因有两点,首先,随着这首歌发布时间的推移,越来越多的网友发现这首《雪Distance》有着抄袭的嫌疑,在歌曲本身热度的加持下,涉嫌抄袭这件事情会引来各路网友前来留下评论;第二点就是歌手Capper在4月9日极具争议的巡演,他的引战言论吸引了更多的路人,为歌曲带来了更多的(负面的)热度。从图中也看到,四月中旬之后,歌曲的风评不但没有达到饱和,还有雪上加霜之势。

下面,我们继续从评论本身的特点,来探究歌曲的风评转变。

3.1.4 歌曲风评研究之关键词

我们首先关注评论的词云,了解大致有哪些关键词。在这里我们再次利用百度开发的基于Python的LAC分词模型。生成云图的文件参见WordCloud.ipynb,其中LAC起分词作用,而wordcloud库负责生成云图。为了贴合网易云音乐的样式,背景被设定为其logo的形状:

这张词云图可谓是包罗万象了。我们看到还是有很多正面评价如“喜欢”“好听”,可见这首歌曲旋律本身还是有可圈可点之处的。我们还观察到很多与歌词相关的内容,如“雪”“冷”“桥牌”等,这一类评论时常出现,说明听众沉浸其中,与歌词产生了共鸣。还有一类关键词引人注目,便是各种负面词汇,有结合时事的,如“抄袭”“泰裤辣”(时下热梗);也有拿热门负面词汇含沙射影的,如“电子烟”“丁真”“答辩(大便)”等词竟然有着很高的出场频率,这说明这首歌也引来了不少骂声,而这种批判既是隐晦的,又是随波逐流的。换言之,这首歌招来外界的骂声,这些骂声仿佛都是一致的,发表负面评论的人(也称作“黑子”)他们骂这首歌的“骂法”近乎都是一致的,无非是“抄袭”“丁真”“答辩”此类词汇,而我们却鲜见对这首歌客观、冷静的批判,如“歌词写的不好”“唱腔矫揉造作”等等。这种现象我认为不是好的现象,当这首歌成为热点,越来越多的路人关注这件事情,他们做的竟然是“跟风黑”“无脑喷”,而不是认真听完、客观地发表他们的见解,这很大程度证实了网易云音乐的很多用户,很容易被外界“带节奏”,很喜欢去评论区“冲塔”“看乐子”。

当然,暖心的是,词云背后还有一些网易云音乐“特有的”词汇,如“想你”“幸福”“想你”“希望”等。这些词汇脱离了歌曲本身,生动地诠释了网易云音乐的评论区独有的“树洞”属性。随着评论的累积,对于评论区,其内容往往会脱离歌曲本身,反而化身年轻人释放情感、互相交流、抚慰的场所。评论区的这种特色让网易云音乐无法被任何其他的网站取代,我们也可以看到,在网易云音乐的评论区,每一个角落都有正能量的存在。

在这之外,我们还可以观察三月份和四月份的评论关键词,可以看出听众明显的情绪变化: