《沉默的真 相》的舆情分析及文本挖掘(一)——以微博、爱奇艺弹幕、bilibili为例

本文从社交平台(以新浪微博为例)和视频平台(以爱奇艺、哔哩哔哩为例)两个角度对网络剧《沉默的真相》进行网络舆情和弹幕的文本挖掘分析。同时基于原著小说《长夜难明》绘制了主要角色的人物关系图,并对该商品在京东某店的评论进行了情感分析处理。基于以上对网络剧《沉默的真相》进行了全方位理解,洞察观众的关注点,为其高分评价做出了合理解释。

1.引言

爱奇艺“迷雾剧场”推出的悬疑短剧《隐秘的角落》,改编自紫金陈的小说《长夜难明》。
由于字数限制,分两篇文章展示。

2.数据来源与研究方法

2.1. 数据来源

本文采用新浪微博数据作为数据源进行分析。本文选取的时间段为2020年9月15日18时—27日24时共计12天,利用八爪鱼工具以“#沉默的真相#”为关键词抓取原创微博数据,每条数据包含微博用户名、博文内容、发文时间、转发数、评论数、点赞数、发布设备7个字段,经过数据清洗得到可用数据40875条。
视频网站弹幕功能的出现正好促进了用户的实时互动性,即用户可以发表和交流自己的看法通过弹幕。爱奇艺是《沉默的真相》的独播平台,因此其弹幕数据是极具代表性的。截止2020年11月11日,利用Python爬虫采集了全集共12集的弹幕数据241554条,每条数据包括集数、用户名、弹幕ID、弹幕内容、出现时间、点赞数6个字段。
哔哩哔哩,设定B站关键词为“沉默的真相”,截止2020年11月11日,去除涉及版权原因等不正常数据、空值后得到534条相关视频数据,每条数据包含标题、视频时长、发布日期、发布时间、总播放数、总弹幕数、硬币、收藏数、点赞数、分享数、视频标签、发布者、投稿数、粉丝数14个字段。
**原著小说商品*选择销量最高,即浦睿文化京东自营店的《长夜难明》,对其商品评论进行采集,得到好、中、差评各504条、24条、21条,会员名称、会员级别、评价星级、评价内容、日期时间5个字段。

2.2. 技术路线

图2.1 本文的技术路线图

图2.1 本文的技术路线图
.

本文主要分为三个板块,网剧《沉默的真相》基于社交平台(新浪微博)的舆情分析、在视频平台(爱奇艺、哔哩哔哩)的数据分析,以及最后的关于原著小说《长夜难明》的文本挖掘。大部分都遵循以下四个步骤(见图2.1),从爬虫获取数据、数据预处理、可视化分析到最后的文本挖掘。

3. 影视作品《沉默的真相》在社交平台的数据分析结果

3.1. 总体关注情况分析

3.1.1. 确定重要时间节点

截止11.06,通过爬取官方微博号——“网剧沉默的真相”,去除64条无效微博后共得到257条博文内容,其转发、评论、点赞总量分别高达65万+、7万、76万+。通过统计所有博文的评论数和转发数,可以明显看出6个重要的时间节点(见图3.1),总体可以划分为宣传阶段(6.8-6.9.15)、播出阶段(9.16-9.27)、回落阶段(9.28-10.17),其中7、8月份没有任何发博记录。在宣传阶段的转发量都是极其高的,本文主要对9.15日及播出阶段的数据进行分析。
本文主要分析9.15-9.27公众对于《沉默的真相》的关注点及其关注情况,在此基础上将其划分为定档期和播出期,对应到舆情发展阶段,并对**#沉默的真相#话题下的所有原创微博**进行了统计(见表3.1),为尽量避免水军的影响,该表中的对数据的去重依据是博文内容完全重复,接下来针对微博平台上的分析都是基于该表。
图3.1 官方微博“网剧沉默的真相”微博数据分析结果

图3.1 官方微博“网剧沉默的真相”微博数据分析结果
.

表3.1 根据重要时间节点划分微博舆情的发展阶段

表3.1 根据重要时间节点划分微博舆情的发展阶段
.

3.1.2. 微博上的关注度走势

本文选择的时间段为该剧的定档期和播出期(2020.9.15-2020.9.27),来观察这段时间内该剧在微博平台上的信息量走势。从微博博文数量走势图(见图3.2)来看,在网剧播出前的关注量较少,而在播出期间的关注度(有关该剧的网络信息量,这里只考虑#沉默的真相#话题中的原创微博内容)陡然上升,在播出的第一天就出现明显上升,并在9.20达到峰值;在剧播出后期,信息量逐渐平稳;播出结束后,逐渐减少,呈下降趋势,这在图1.1也有所展现。
图3.2  9.15-9.27#沉默的真相#微博博文数量走势图

图3.2 9.15-9.27#沉默的真相#微博博文数量走势图
.

3.2. 微博平台关于《沉默的真相》的关注点

将爬取的相关微博数据接着进行中文分词处理,TF-IDF词频统计及词云可视化及文本聚类、LDA主题模型分析,来获取微博平台上公众对《沉默的真相》的关注点。
首先进行数据预处理,前文已完成对重复值、缺失值的清洗,接下来需要对文本数据进行中文分词处理,这里我使用jieba实现中文分词,在分词前首先对中文文本进行了同义词替换,同时添加自定义词典、去除停用词(部分重要停用词和替换词见表3.2)。

在这里插入图片描述

表3.2 部分重要停用词和替换词
.

3.2.1. TF-IDF词频统计及WordCloud可视化分析

在进行数据预处理和中文分词处理后进行TF-IDF词频统计,定档期和播出期TF-IDF词频前20见图3.7,说明观众对剧集内容的讨论开始加重,而不是宣传阶段仅仅因为明星效应的缘故去讨论该话题。
在这里插入图片描述

图3.7 定档期与播出期TF-IDF高频关键词对比
.

单看TF-IDF排名图中并不容易对比出两个时期的差异,这里我又通过PyEcharts来分别绘制两个阶段的WordCloud(图3.8),这里考虑到对比差异,因此去掉江阳、白宇,词云图中展示了TOP100的关键词,且可以将其重要性归一化地展现。
在这里插入图片描述

图3.8 定档期(左)与播出期(右)的词云图
.

# 微博数据的词云图
from pyecharts import options as opts
from pyecharts.charts import WordCloud
from pyecharts.globals import SymbolType
import pandas as pd
# 数据
wb = pd.read_excel(r'D:LearningLPythonbigDataClass_2020Fallpaper_weibo9.16开播-9.27结束_TFIDF关键词前100.xlsx')
data_wb = list(zip(wb['词语'],wb['重要性']))
# 渲染图
def wordcloud_base() -> WordCloud:
    c = (
        WordCloud()
        .add("", data_wb, word_size_range=[20, 85], shape='triangle-forward')  # SymbolType.ROUND_RECT
        .set_global_opts(title_opts=opts.TitleOpts(title='9.16-9.27 WordCloud词云'))
    )
    return c
# 生成图
wordcloud_base().render('9.16-9.27词云图.html')

3.2.2. 文本聚类

(1)K-Means聚类
选择定档期的全部数据,以及播出期的随机10000条数据进行K-Means分析,预设n=3时的输出图像如图3.9所示。可以观察到K-Means的聚类效果并不是很好,且简单的聚类是无法进行深入分析的,需要结合具体数据集进行分析,但其解释性始终不是很好。但是实际的数据分析会引入类标签或注释,因此我们引入主题关键词聚类和LDA主题模型分析,这有助于理解文本挖掘和主题分析。
在这里插入图片描述

(2)层次聚类
本文选用Ward方法,即沃德方差最小化算法进行凝聚层析聚类,由于这里的主题词太多,所以这里采用jieba分词提取每条微博数据(对应一行数据)的Top100特征词,再存储至txt文件中进行层次聚类分析。通过不断地调节关键性参数以更好地展示聚类结果,我需要调节min_df用于删除不经常出现的术语,以及max_df用于删除过于频繁出现的术语,最终设置定档期为[40,200],播出期为[500,2000],最终分别得到31个词、61个词的聚类结果如图3.10,3.11所示。

import pandas as pd
import jieba
import jieba.analyse
import matplotlib.pyplot as plt
from pylab import mpl
from collections import Counter
from sklearn.metrics.pairwise import cosine_similarity
from scipy.cluster.hierarchy import ward, dendrogram
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer

mpl.rcParams['font.sans-serif'] = ['SimHei']

#------------------------------  第一步 获取TOP100 ------------------------------
#------------------------------ 第二步 中文分词过滤 ------------------------------
#------------------------------ 第三步 相相关计算 ------------------------------ 
text = open('C-key.txt', encoding='utf-8').read()
list1 = text.split("n")

mytext_list = list1
count_vec = CountVectorizer(min_df=500, max_df=2000)
xx1 = count_vec.fit_transform(list1).toarray()
word = count_vec.get_feature_names() 
print("word feature length: {}".format(len(word)))
print(word)
print(xx1.shape)
print(xx1[0])
titles = word
#------------------------------ 第四步 相似度计算 ------------------------------ 
df = pd.DataFrame(xx1)
print(df.corr())
print(df.corr('spearman'))
print(df.corr('kendall'))

dist = df.corr()
print(dist)
print(type(dist))
print(dist.shape)

#------------------------------ 第五步 可视化分析 ------------------------------ 
# define the linkage_matrix using ward clustering pre-computed distances
linkage_matrix = ward(dist)
fig, ax = plt.subplots(figsize=(15, 20)) # set size
ax = dendrogram(linkage_matrix, orientation="right", labels=titles);

# how plot with tight layout
plt.xticks(fontsize=30)
plt.yticks(fontsize=20)
plt.tight_layout() 

# save figure as ward_clusters
plt.savefig('9.16-9.27 Tree_word4.png', dpi=200)

在这里插入图片描述

播出期可以提取出6个小主题,从上至下分别是原著小说(紫金陈、隐秘的角落、还原、原著);演员(廖凡、张超、宁理);评价(不错、值得安利、细节、三线并行);广告植入;升华主题(信念、光明、希望、付出代价);情感(眼泪、好哭、暴哭、压抑)。总的来说,定档期主要是对其播出平台和时间的宣传以及原著粉丝、主演粉丝对剧集的期待;而播出期主要是对剧集的正面评价、主旨理解、情感输出等。

3.2.3. LDA主题模型

LDA主题分布分析需要设置不同的主题值,本文指定抽取3个主题中的前20个高频词汇,对于播出期,去掉关键词“白宇、江阳”,设定

λ

=

0.88

lambda=0.88

λ=0.88时可视化效果如图3.13所示。此时困惑度为534.498,困惑度较定档期低些,说明聚类的效果稍好。从结果来看,3个主题是比较清晰的,第1主题代表网剧的定档时间为9月16日晚20点,第2主题为公众对网剧的期待、喜欢,第3主题为“今晚一定追剧”。

# 微博信息-LDA主题模型
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
import sys
import codecs
import importlib
importlib.reload(sys)
#---------------------  第一步 读取数据(已分词)  ----------------------
corpus = []
# 读取预料 一行预料为一个文档
for line in open('9.15定档-9.16开播_分词.txt', 'r', encoding='utf-8').readlines():
    corpus.append(line.strip())        
#-----------------------  第二步 计算TF-IDF值  ----------------------- 
# 设置特征数
n_features = 500
tf_vectorizer = TfidfVectorizer(strip_accents = 'unicode',
                                max_features=n_features,
                                stop_words=['沉默','white','真相','我要','想到','看到','看起来',
                                            '可以','微博','比较','这里','视频','真的','一个',
                                            '自己','两集','同事','沉真','老师','没有','时候',
                                            '为了','他们','什么','全文','收起','我们','知道',
                                            '不是','就是','这样','有人','怎么','这个','那个',
                                            '这部','有点','那么','白宇','by','下三土','江阳',
                                            '微博','微博','微博','微博',],
                                max_df = 0.8,
                                min_df = 0.002) #去除文档内出现几率过大或过小的词汇
tf = tf_vectorizer.fit_transform(corpus)
#-------------------------  第三步 LDA分析  ------------------------ 
from sklearn.decomposition import LatentDirichletAllocation
# 设置主题数
n_topics = 3

lda = LatentDirichletAllocation(n_components=n_topics,
                                max_iter=100,
                                learning_method='online',
                                learning_offset=50,
                                random_state=0)
lda.fit(tf)

# 显示主题数 model.topic_word_
print(lda.components_)
# 几个主题就是几行 多少个关键词就是几列 
print(lda.components_.shape)                         

# 计算困惑度
print(u'困惑度:')
print(lda.perplexity(tf,sub_sampling = False))

# 主题-关键词分布
def print_top_words(model, tf_feature_names, n_top_words):
    for topic_idx,topic in enumerate(model.components_):  # lda.component相当于model.topic_word_
        print('Topic #%d:' % topic_idx)
        print(' '.join([tf_feature_names[i] for i in topic.argsort()[:-n_top_words-1:-1]]))
        print("")

# 定义好函数之后 暂定每个主题输出前20个关键词
n_top_words = 20
tf_feature_names = tf_vectorizer.get_feature_names()
# 调用函数
print_top_words(lda, tf_feature_names, n_top_words)
#------------------------  第四步 可视化分析  ------------------------- 
import pyLDAvis
import pyLDAvis.sklearn
data = pyLDAvis.sklearn.prepare(lda,tf,tf_vectorizer)
#print(data)
#显示图形
pyLDAvis.show(data)

在这里插入图片描述

在这里插入图片描述

图3.13 播出期的LDA主题模型分析结果
.

4. 影视作品《沉默的真相》在视频平台的数据分析结果

4.1. 爱奇艺视频网站的弹幕数据分析

4.1.1. 弹幕的总体趋势

利用Python爬虫,首先寻找获取每集视频的tvid参数,进一步发起请求,获取爱奇艺12集的弹幕评论信息分别保存到excel文档,爬取到共90032个用户发送的241554条弹幕。

#爱奇异 获取弹幕
import requests
import pandas as pd
import zlib
import re
import time

def get_aiqiyi_danmu(tvid):
    """
    功能:给定tvid,获取爱奇艺一集的弹幕评论信息
    """
    # 建立空df
    df_all = pd.DataFrame()
    # 初始page_num
    page_num = 1
    while True:
        # 打印进度
        print(f'正在获取第{page_num}页的弹幕数据')

        try:
            # 获取URL
            url = f'https://cmts.iqiyi.com/bullet/{str(tvid)[-4:-2]}/{str(tvid)[-2:]}/{str(tvid)}_300_{page_num}.z'

            # 添加headers
            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'
            }
            # 发起请求
            try:
                r = requests.get(url, headers=headers, timeout=3)
            except Exception as e:
                print(e)
                r = requests.get(url, headers=headers, timeout=3)
            # 转换为arrry
            zarray = bytearray(r.content)
            # 解压字符串
            xml = zlib.decompress(zarray, 15 + 32).decode('utf-8')
            # 用户名
            name = re.findall('<name>(.*?)</name>', xml)
            # 评论ID
            contentId = re.findall('<contentId>(.*?)</contentId>', xml)
            # 评论信息
            content = re.findall('<content>(.*?)</content>', xml)
            # 展示时间
            showTime = re.findall('<showTime>(.*?)</showTime>', xml)
            # 点赞次数
            likeCount = re.findall('<likeCount>(.*?)</likeCount>', xml)
            # 保存数据
            df_one = pd.DataFrame({
                'name': name,
                'contentId': contentId,
                'content': content,
                'showTime': showTime,
                'likeCount': likeCount
            })
            # 循环追加
            df_all = df_all.append(df_one, ignore_index=True)
            # 休眠一秒
            time.sleep(1)
            # 页数+1
            page_num += 1
        except Exception as e:
            print(e)
            break
    return df_all
# 抓包获取视频tvid
tvid_list = [8928607238794800, 1633711019810300, 8520304822314600,
             2168755640240300, 8992780506613300, 2406998896996700,
             6095357464723600, 1664193322029900, 4568835856548000,
             2473578662358100, 3822019452341300, 2827333801193900]
episodes_list = ['第一集 张超强闯地铁站', '第二集 张晓倩收到匿名信', '第三集 张晓倩收到匿名信',
                 '第四集 严良再访李静', '第五集 江阳接连受到恐吓', '第六集 江阳朱伟开始走访',
                 '第七集 严良知晓爆炸原理', '第八集 侯贵平案实情曝光', '第九集 江阳得到重要线索',
                 '第十集 江阳遭污蔑索取贿赂', '第十一集 严良找到匿名寄信人', '第十二集 江阳死因揭秘']

# 循环获取所有集数据
for tvid, episodes in zip(tvid_list, episodes_list):
    print(tvid, episodes)
    # 获取数据
    df = get_aiqiyi_danmu(tvid=tvid)
    # 插入列
    df.insert(0, 'episodes', episodes)
    # 导出数据
    df.to_excel(f'aiqiyi_{episodes}.xlsx')

4.1.2. 绘制角色词云图

首先制作角色词典(见表4.1),接下来通过提取与主角相关的弹幕,即包含了姓名的关键字,来分别绘制江阳、朱伟、严良、张超、侯贵平的角色词云图(见图4.4),其中白宇塑造的正义形象江阳,提及频率远高于其他角色。
在这里插入图片描述
通过观察五个主角的词云图,可以发现一些有趣的现象。第一,不同角色对应的感情色彩是不同的,例如江阳是哭泣(&#128557)、严良是笑哭(&#128514)、加入廖凡后是点赞(&#128077)。第二,明显观察到与其他网剧相关联的地方,例如严良词云图出现大量《隐秘的角落》中的角色,朱朝阳、普、/秦昊等,因为《隐秘的角落》中也同样出现了名为“严良”的角色;侯贵平词云图中也出现《人民的名义》角色侯亮平,很可能是因为名字相仿而已。第三,只有张超词云图中李丰田的比重几乎与角色名称重要性一样大,“李丰田”是网络剧《无证之罪》中登场的虚拟角色,“人狠话不多”也是对其的特有评价。第四,紫金陈“社会派”推理作品三部曲有《无证之罪》、《坏小孩》、《长夜难明》,分别对应于网剧《无证之罪》、《隐秘的角落》、《沉默的真相》,这三部剧很可能拥有共同的影迷和粉丝,具有很强的关联性。
在这里插入图片描述

4.2. 哔哩哔哩平台上的相关视频分析

首先爬取得到哔哩哔哩534条相关视频数据,观察到总播放数、总弹幕数、硬币、收藏数、点赞数、分享数这6个字段中既包含有“182.0万”也有“354”两种不一致的表示方法;视频时长中也有“01:09”和“01:06:11”两种方式,数据质量并不高。因此我进行了数据转换消除异构数据的影响,例如将“182.0万”改为“1820000”,将“01:09”改为“00:01:09”。
观察到变量总播放数、总弹幕数、硬币、收藏数、点赞数、分享数以及与UP主的投稿数、粉丝数可能存在某种关联,因此本文主要采用皮尔逊Pearson相关系数来度量连续变量之间的线性相关强度;同时利用seaborn.heatmap生成热力图(见图4.7),颜色越深表示相关性越强。可以明显看出投稿数与其他变量的相关性都不是很强,都没有超过0.4;收藏数与硬币的相关性达到最高,为0.96,与B站的“一键三连”机制,即同时“点赞、投币、收藏”有一定的关系;总播放数与除投稿数的6个变量相关性都很高,都超过了0.8。

# 相关系数矩阵
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pandas import Series, DataFrame

df_train = pd.read_excel(r'D:LearningLPythonbigDataClass_2020Fallpaper_aiyiqibilibili_视频采集_清洗后.xlsx', header=0, skiprows=0)
df_train.info()  # 显示数据信息

t = np.around(df_train.corr(), decimals=4)  # 这里是将矩阵结果保留4位小数
tt = df_train.corr()  # 默认保留6位小数
mm = df_train['总播放数'].corr(df_train['粉丝数'])  # 进行两列之间的相关性分析
print(mm)
oo = df_train[['总播放数', '总弹幕数', '硬币', '收藏数', '分享数', '粉丝数']].corr()  # 计算指定多列相关系数
print(oo)
plt.subplots(figsize=(11,15))  # 设置画面大小
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题,负号正常显示
plt.title('变量相关系数 - 热图n', fontsize=18)  # 添加图表标题“变量相关系数 - 热图”,fontsize=18 字体大小 可省略
plt.title('变量相关系数 - 热图n', fontsize=18)
sns.heatmap(t, annot=True, vmax=1, vmin=0, xticklabels=True, yticklabels=True, square=True, cmap="YlGnBu",
            linewidths=0.05, linecolor='white', mask=t < 0.8)  # mask=t < 0.8等价于mask=(t < 0.8)
plt.show()
plt.title('bilibili变量相关系数热图n', fontsize=18)
sns.heatmap(t, annot=True, vmax=1, vmin=0, xticklabels=True, yticklabels=True, square=True, cmap="YlGnBu",
            linewidths=0.05, linecolor='white', mask=None)

plt.savefig('./bilibili热图.png')
plt.show()

在这里插入图片描述

由于篇幅限制,关于本剧的原著小说《长夜难明》的数据分析结果将在下一篇文章中进行展示。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>