由于网络剧《沉默的真相》改编自原著《长夜难明》,其主要角色没有太大变动,但仍有微小差异,例如小说中的“乐乐”在剧中名为“小树”,重新构建同名词典(见表5.1)在此基础上本文构建原著小说中的主要人物社交网络图谱,这里对小说全文使用jieba.cut精准文字模式,只检索指定人名。角色中的连接信息由是否在同一段出现来确定,有则边的值+1,最终得到28个节点(人物名称以及出现次数)和231条人物关系统计边。
最终的人物关系是在Gephi中绘制,其是一个开源的复杂网络数据可视化软件,可用于对链路分析、社交网络分析、生物网络分析等进行探索分析。通过Python把数据处理成Gephi可接受的csv格式,然后再进行绘制。这里我使用Fruchterman Reingold布局,是基于再次改进的弹性模型提出了FR算法。最终得到人物关系图见图5.1。
import os
import time
import jieba
import codecs
import chardet
import jieba.posseg as pseg
content_path= r"D:\Learning\LPython\bigDataClass_2020Fall\paper_TheLongNight\Character-interaction-visualization-master\长夜难明.txt"
dict_path = r"D:\Learning\LPython\bigDataClass_2020Fall\paper_TheLongNight\Character-interaction-visualization-master\人名.txt"
'''
核心业务代码是 self.gephi_node_name 和 self.gephi_edge 用于处理检查统计的人物
其中 gephi_cofig.gephi_node_name 会统计人物出现的次数,并且生成给 gephi_config.gephi_edege 处理所需要的 _lineNames
'''
class gephi_config:
def __init__(self,content_path,dict_path,name_mode = True):
start = time.time()
# 获取py文件所在的path
self._src = os.path.split(os.path.realpath(__file__))[0]
self.local_file_path()
# 确认生成文件的名称
now = self.get_time()
dirname = os.path.dirname(self._content_path)
content_name = os.path.splitext(os.path.split(self._content_path)[1])[0]
self._dst_node = os.path.join(dirname,(content_name +'gephi_node-' + now +'.csv'))
self._dst_edge = os.path.join(dirname,(content_name +'gephi_edge-' + now +'.csv'))
print('生成的节点文件是:{} \n生成的连接文件是:{}'.format(self._dst_node[-19:],self._dst_edge[-19:]))
# 初始化状态、列表、指点对象
self._name_mode = name_mode # 确定使用那种模式
self._lineNames= [] # 姓名检索列表
self._names = {} # 姓名字典
self._relationships = {} # 关系字典
# 读取self.dict_name内容并且构建出jieba 需要的文本。
with codecs.open(self._dict_path,'r',self.content_coding(self._dict_path)) as f:
self._name_list = f.read().split('\r\n')
list_name = self._name_list
list_name[-1] = list_name[-1] + ' 10 nr\r\n'
self._jieba_dict = ' 10 nr\r\n'.join(list_name)
# 进入处理流程
self.do_first()
# 打印处理时间
end = time.time()
usetime = end - start
if usetime > 60.0:
use_min = usetime // 60
use_sec = int(usetime - (use_min*60))
print('time cost {}m {}s.'.format(use_min,use_sec))
else:
print('time cost {}s.'.format(int(usetime)))
# 本地文件判断
def local_file_path(self):
self._local_txt_status = False
local_txt_path_list = []
for i in os.listdir(self._src):
if os.path.splitext(i)[1] != '.txt':
continue
local_txt_path_list.append(os.path.join(self._src,i))
self._local_txt_status = True
# 如果存在 .txt 的文件,通过文件大小判断「小说」与「小说人物」文件
if self._local_txt_status:
for txt_path in local_txt_path_list:
if os.path.getsize(txt_path) > 10000: # 通过文件大小判断那个是主体文件
self._content_path = txt_path # 小说的路径确定
print('小说文件是 {}'.format(os.path.split(self._content_path)[1]))
else:
self._dict_path = txt_path # 小说角色文件路径确定
print('人物文件是 {}'.format(os.path.split(self._dict_path)[1]))
print("get local txt file path.")
#生成时分秒格式的str
def get_time(self):
ISOTIMEFORMAT = r'%H-%M'
now = time.strftime(ISOTIMEFORMAT, time.localtime())
return now
#确认文件编码
def content_coding(self,path):
#print(path)
with open(path,'rb') as f:
a = chardet.detect(f.read())
if a['encoding'] == 'gb2312' or a['encoding'] == 'GB2312':
coding_type = 'utf8'
elif a['encoding'] == 'utf-8':
coding_type = a['encoding']
elif a['encoding'] == 'UTF-8-SIG':
coding_type = a['encoding']
else:
print("无法确认编码")
return coding_type
def Synonymous_names(self):
Synonymous_dict = {'小江': '江阳', '江检': '江阳',
'阿雪': '朱伟', '平康白雪': '朱伟','雪哥': '朱伟','小雪': '朱伟',
'张老师': '张超',
'黄毛': '岳军' , '小板凳': '岳军',
'陈法医': '陈明章', '老陈': '陈明章',
'铁民': '赵铁民',
'高厅': '高栋',
'孙总': '孙红运',
'爱可': '吴爱可'}
print('synoymous :\n{}'.format(Synonymous_dict))
return Synonymous_dict
# 使用精准文字模式,只检索指定人名
def gephi_node_name(self):
print('start process node')
Synonymous_dict = self.Synonymous_names()
name_set = set(self._jieba_dict.split(' 10 nr\r\n')) # 将jieba所需要文件样式切割成由任务名组成的 name_set
jieba.load_userdict(self._dict_path)
with codecs.open(self._content_path, "r",self.content_coding(self._content_path)) as f:
for lines in f.readlines():
poss = jieba.cut(lines) # 由于我已经指定了名字,所以没有使用jieba.posseg.cut 方法进行分词,而是使用了 jieba.cut.
self._lineNames.append([])
for w in poss:
if w not in name_set: # 如果关键字不在 name_set 中存在就跳过
continue
if Synonymous_dict.get(w): # 如果存在重名,这里是True 判断,所以就算没有重名(synonymous_dict)为空也没事。这里完全是因为红楼梦里面 贾宝玉 宝玉 林黛玉 黛玉 而生
w = Synonymous_dict[w]
self._lineNames[-1].append(w)
if self._names.get(w) is None:
self._names[w] = 0
self._relationships[w] = {}
self._names[w] += 1
print('gephi node done!')
return self._lineNames
# 获得角色间连接信息
def gephi_edge(self):
print("start to process edge")
for line in self._lineNames: # 对于每一段
for name1 in line:
for name2 in line: # 每段中的任意两个人
if name1 == name2:
continue
if self._relationships[name1].get(name2) is None: # 若两人尚未同时出现则新建项
self._relationships[name1][name2]= 1
else:
self._relationships[name1][name2] = self._relationships[name1][name2]+ 1
print('gephi edge done!')
return self._relationships
# 将处理的信息保存为csv文件
def save_node_and_edge(self):
with codecs.open(self._dst_node, "a+", "utf-8") as f:
f.write("Id,Label,Weight\r\n")
for name, times in self._names.items():
f.write(name + "," + name + "," + str(times) + "\r\n")
with codecs.open(self._dst_edge, "a+", "utf-8") as f:
f.write("Source,Target,Weight\r\n")
relationships = self.gephi_edge()
for name, edges in relationships.items():
for v, w in edges.items():
if w > 3:
f.write(name + "," + v + "," + str(w) + "\r\n")
# 处理流程
def do_first(self):
if self._name_mode :
self.gephi_node_name()
else:
self.gephi_node()
self.save_node_and_edge()
if __name__ == '__main__':
print('开始处理文件,稍微等一等鸭')
a = gephi_config(content_path,dict_path,True)
print('任务完成,别喝可乐啦!')
在京东平台上选择《长夜难明》销量最高的商品对其评论数据进行了采集,辅助分析原著小说,在所采集到的所有评论信息中2016年、2017年、2018年分别仅有1、19、7条,就不再对比。截止2020.10.26,图5.2显示了2019-2020年商品评论数量的变化趋势,可以看出在2020年10月原著小说的评论数量突然增长,与9月份其影视作品的播出有一定的关联。
通过绘制好评与中差评的词云图(见图5.3)我们可以看出给差评的用户对其不满的主要是怀疑盗版、店家改价、没货等的不满;从好评可以看到“白宇”、“网剧”、“隐秘的角落”等都是受网剧的影响才来购买书籍的。
SnowNLP是一个python写的类库,可以方便的处理中文文本内容。这里主要利用snownlp库中的情感判断来判断买家评论信息的情感,API返回值为正情感(积极情感)概率,接近1表示正情感,接近0表示负情感(消极情感)。本文中买家的评论信息可以用作情感分析的数据集,但由于全网评价条目过多,我们创建一个规则:
正面情绪概率≥0.75 称之为积极情绪,对应好评;
正面情绪概率<0.45 称之为消极情绪,对应差评;
正面情绪概率<0.75 & ≥0.45 称之为平和情绪,对应中评;
首先分别获取好评、中评、差评数据的txt文档,利用sentiment 情感分析模块经过计算后输出测试数据,将其与真实标签对比后(见表5.2)可以看到总体准确率在66.120%,其中,差评准确率最高,中评准确率最低。通过观察后发现,许多用户在评价内容中表现出消极的情绪,但仍然给出好评或中评的评价,是造成准确率低的主要原因。
from pathlib import Path
import pandas as pd
# 获取当前目录下 有好评 中评 差评数据的txt
p = Path(r'D:\Learning\LPython\bigDataClass_2020Fall\paper_jingdong')
review_txt = list(p.glob('**/*.txt'))
all_data = pd.DataFrame()
for item in review_txt:
emotion = item.stem # 获取文件名 除后缀的部分
with Path(item).open(mode='r',encoding='UTF-8') as f:
con = f.read().split('\n')
data = pd.DataFrame({'评论内容': con, '标签': [emotion] * len(con)})
all_data = all_data.append(data)
all_data.to_excel('评论数据.xlsx', index=False)
from snownlp import SnowNLP
import pandas as pd
import re
# 读取数据
df = pd.read_excel('评论数据.xlsx', encoding='utf-8')
# print(df.info())
# 去掉空值的行
df = df.dropna(axis=0)
content = df['评论内容']
# 去除一些无用的字符 只提取出中文出来
#content = [' '.join(re.findall('[\u4e00-\u9fa5]+', item, re.S)) for item in content]
try:
scores = [SnowNLP(i).sentiments for i in content]
except:
print("被除数为零报错")
emotions = []
for i in scores:
if i >= 0.75:
emotions.append('好评')
elif 0.45 <= i < 0.75:
emotions.append('中评')
else:
emotions.append('差评')
df['情感分数'] = scores
df['情感'] = emotions
df.to_excel('NLP测试后数据.xlsx')
# 对比准确度
import pandas as pd
df = pd.read_excel('NLP测试后数据.xlsx')
# 看准确率 通过Snownlp情感打分 设置梯度得出的情感 好评 中评 差评 与实际标签相比较
data = df[df['标签'] == df['情感']]
print('总体准确率为:{:.3%}'.format(len(data) / len(df)))
data_good = df[24:527][df['标签'] == df['情感']]
print('好评准确率为:{:.3%}'.format(len(data_good) / len(df[24:527])))
data_mid = df[0:23][df['标签'] == df['情感']]
print('中评准确率为:{:.3%}'.format(len(data_mid) / len(df[0:23])))
data_bad = df[528:549][df['标签'] == df['情感']]
print('差评准确率为:{:.3%}'.format(len(data_bad) / len(df[528:549])))
同样是来自爱奇艺针对悬疑短剧的“迷雾剧场”,《沉默的真相》播出当天在豆瓣评分就高达8.8分,随着这部连续剧的播出,该剧口碑更是势不可挡,一路走高,播出六集后,豆瓣评分冲到了9.2分,成功超越了它的前浪《隐秘的角落》。这种高开高走的趋势在国产剧里是非常罕见的。
从本文的分析结果我们发现几个比较有趣的现象:第一,明星作为信息传播的重要节点起到的关键性作用,不论是在社交平台微博还是爱奇艺的弹幕数据都可以看出主人公江阳的饰演者白宇在其出现频率远高于其他演员。第二,网络剧《沉默的真相》的观众有不小的比例是原著小说《长夜难明》的粉丝,且评价了网剧对原著的还原度较高。第三,从爱奇艺的弹幕数据可以明显看到与其他剧集的联动,紫金陈的悬疑剧三部曲被提及的次数也不在少数,《沉默的真相》作为第三部,其播放量的成功很可能受到第二部《隐秘的角落》的正向促进作用,相对地,这两部的成功也会带动第一部《无证之罪》的播放量。第四,观众对于该剧的情绪主要有三类:对情节、转场、镜头等的喜爱和赞美;对悲剧人物的惋惜和悲愤;以及对广告植入的不满,但用心的制作和品质还是奠定了它的高质量。第五,由小说改编的影视作品播出往往会影响原著小说的阅读量和购买量,从京东某店10月份的商品评价就可以看出。