gephi和python_python+nlp+Gephi 分析电视剧【人民的名义】

最近和舍友看完了去年大火的电视剧【人民的名义】,看完觉得里面的人物关系很有意思,决定对其分析分析,也顺便测试一下早前使用过的一些模型(例如word2vec)效果是否能达到预期。

1.获取数据

进行分析之前首先需要获取剧情的文本,因为没有看过小说,为了尽量接近自己和大部分人所熟悉的剧情,这里爬取从百度百科上的每一集的剧情,针对已更新的剧情文本进行分析。利用python的urllib2(python3.3后改为urllib.request)和BeautifulSoup包可以很快的爬下剧情文本,保存为rmdmy.txt文档,顺便将出现的人物名字也一起爬下来,后面进行预处理和分析中涉及到的分词、实体属性对齐和社交网络分析等都将会用到。

# -*- coding: utf-8 -*-

"""

@author: wangyao

"""

#改变默认工作路径

import os

os.chdir(r"C:\Users\wangyao\Desktop\人民的名义")

##爬取百度百科剧情

import urllib.request

from bs4 import BeautifulSoup

import re

import pandas as pd

url = "https://baike.baidu.com/item/%E4%BA%BA%E6%B0%91%E7%9A%84%E5%90%8D%E4%B9%89/17545218"

import sys

import importlib

importlib.reload(sys)

response = urllib.request.urlopen(url)

con = response.read()

#使用beautifulsoup中的html解析器

cont = BeautifulSoup(con,"html.parser")

content = cont.find_all('ul',{'id':'dramaSerialList'})

content = str(content)

##去掉HTML标签

content1 = re.sub(r']+>','',content)

f = open('rmdmy.txt','w',encoding= 'utf-8') #直接用open打开会报错,需要指定编码方式

f.write(content1)

f.close()

#爬取名字

f = open('rmdmy_name.txt','a',encoding= 'utf-8')

name_content = cont.find_all("dl",attrs={"class": "info"})

for i in name_content:

name_d = i.get_text().strip().split(u'\n')[0]

name = name_d.split(u'\xa0')[2]

#加decode()byte和str才能相加

f.write(name.encode('utf-8').decode()+'\n')

f.close()

文本文件如下所示:

rmdmy.txt

rmdmy_name.txt

2.文本预处理

将剧情爬下来后需要对文本进行预处理,主要包括分句、分词、去掉一些特殊符号和停用词、实体对齐和属性对齐等。如果一个人可能在剧中有不同的名字,这时候就需要进行统一。为了尽量正确的切分一些固定名称,需要导入自定义词典,主要包含一些人名、地名和组织名称等(例如这里需要加入侯亮平,汉东省,汉东大学,山水集团,大风厂等等)。此外,在提取文本特征时需要去掉一些停用词,以提高分析的准确度。经过一系列处理后得到比较干净的文本分词结果,然后就可以在此基础上进行深入的分析。

#文本预处理

import jieba

jieba.load_userdict('rmdmy_dict.txt')#自定义词典

stopword = [line.strip() for line in open('StopwordsCN.txt',encoding= 'utf-8').readlines()] #简体中文停用词

fr = open('rmdmy.txt','r',encoding= 'utf-8')

con = [fr.readlines()]

'''

分词,并去掉特殊字符、词语

'''

fw = open('rmdmy_content.txt','w',encoding= 'utf-8')

for i in con[0]:

#if len(i.decode('utf-8'))<=10:

if len(i)<=10:

pass

else:

w1 = i.split("。")#按句号分句

for j in w1:

w2 = re.sub(r',|。|?|:|“|”|!','',j.strip())#去掉特殊字符

#w1 = re.sub(name1,name2,w1) #实体对齐

w3 = list(jieba.cut(w2))#分词

w4 = [w for w in w3 if w not in stopword]#去掉停用词

outstr = ''

for word in w4:

outstr +=word

outstr +=' '

fw.write(outstr.strip().encode('utf-8').decode())

fw.write('\n')

fw.close()

预处理结果:

rmdmy_content.txt

3.人物出现频次和社交网络关系

先看一下剧中出场次数较多的关键人物有哪些,根据之前爬下来的名字列表,统计其在文本中出现的次数,通过matplotlib包画出出现次数最多的10个关键人物如图所示,可以发现侯亮平出现次数最多,共483次,其次李达康出现了226次,再次是高育良出现了211次,祁同伟202次。整部剧穿插的几大事件都在围绕着这四个人展开,而沙瑞金虽然也是贯穿全剧的重要人物,出场次数也很多(148次),但是大部分事件里都是出来打酱油的,所以次数还低于大风厂事件的蔡成功。

#人物出场次数统计

import numpy as np

import scipy as sp

import matplotlib.pyplot as plt

import matplotlib.font_manager as fm

font_yahei_consolas = fm.FontProperties(fname = 'simsun.ttc')

#引入字体,否则文字显示不出来

#python 字符串转列表 list 出现\ufeff的解决方法(网上)

##文件内容 lisi

#lock = open("lock_info.txt", "r+",encoding="utf-8")

#lock_line = lock.readline()

#lock_list = lock_line.split(",")

#print(lock_list)

#

#y = lock_line.encode('utf-8').decode('utf-8-sig')

#print(y)

#

##打印结果如下

#['\ufefflisi']

#lisi

#自己测试,在notepad++上把编码从UTF-8编码模式改成UTF-8无BOM编码模式,ufeeff就会消失

with open('rmdmy_dict.txt',encoding= 'utf-8') as f1:

data1 = f1.readlines()

with open('rmdmy_content.txt',encoding= 'utf-8') as f2:

data2 = f2.read()

#匹配词典里的名字和剧本内容里的该名字出现的次数

count = []

for name in data1:

count.append([name.strip(),data2.count(name.strip())])

count1 = []

for i in count:

if i not in count1:

count1.append(i)

count = count1

count.sort(key = lambda x:x[1])

ay,ax = plt.subplots()

numbers = [x[1] for x in count[-10:]]

names = [x[0] for x in count[-10:]]

ax.barh(range(10),numbers,color=['peru','coral'],align = 'center')

ax.set_title('人物出场次数',fontsize = 14,fontproperties = font_yahei_consolas)

ax.set_yticks(range(10))

ax.set_yticklabels(names,fontsize = 14,fontproperties = font_yahei_consolas)

plt.show()

人物出场频次条形图:

人物出场次数

再来看看剧中人物的社交关系情况,采用以句为单位来进行分析(有时候也以段落为单位来识别人物关系,但采集的文本每集只有一个段落,所以不适用),即如果两个人同时出现在一句话中,那说明他们之间肯定有某种联系。因此可以得到他们的社交网络关系。通过求得的共现矩阵,使用Gephi画出下面的社交网络关系图,图中边的粗细代表关系的密切程度,边越粗表示两人的关系越密切,而名字的大小可以表示为该人的社交人脉强弱情况。

#匹配词典里的名字和剧本内容里的该名字出现的次数

count = []

for name in data1:

count.append([name.strip(),data2.count(name.strip())])

count1 = []

for i in count:

if i not in count1:

count1.append(i)

count = count1

count.sort(key = lambda x:x[1])

ay,ax = plt.subplots()

numbers = [x[1] for x in count[-10:]]

names = [x[0] for x in count[-10:]]

ax.barh(range(10),numbers,color=['peru','coral'],align = 'center')

ax.set_title('人物出场次数',fontsize = 14,fontproperties = font_yahei_consolas)

ax.set_yticks(range(10))

ax.set_yticklabels(names,fontsize = 14,fontproperties = font_yahei_consolas)

#社交网络关系(共现矩阵)

f2 = open('rmdmy_content.txt','r',encoding= 'utf-8')

word = f2.readlines()

name = data1

#name = data1[1:]

#总人数

wordcount = len(name)

#初始化128*128值全为0的共现矩阵

cormatrix = [[0 for col in range(wordcount)] for row in range(wordcount)]

#遍历矩阵行和列

for colindex in range(wordcount):

for rowindex in range(wordcount):

cornum = 0

#如果两个人名字在同一句话里出现,那么共现矩阵中两个人对应的值加1

for originline in word:

if name[colindex].strip() in originline and name[rowindex].strip() in originline:

cornum += 1

cormatrix[colindex][rowindex] = cornum

cor_matrix = np.matrix(cormatrix)

for i in range(len(name)):

cor_matrix[i,i] = 0

social_cor_matrix = pd.DataFrame(cor_matrix, index = name,columns = name)

#把共现矩阵存进excel

social_cor_matrix.to_csv('social_cor_matrix.csv')

social_contact = pd.DataFrame(columns = ['name1','name2','frequency'])

#共现频率

for i in range(0,len(name)):

for j in range(0,len(name)):

if i 0:

social_contact.loc[len(social_contact),'name1'] = name[i]

social_contact.loc[len(social_contact)-1,'name2'] = name[j]

social_contact.loc[len(social_contact)-1,'frequency'] = cormatrix[i][j]

social_contact.to_excel('social_contact.xlsx',index = False)

社交情况:

社交网络

4.走进‘大风厂’事件

接下来重点探索一下我比较感兴趣的“大风厂”事件,先通过关键字抓取出相关剧情,然后使用python的wordcloud包生成词云,wordcloud可以导入图片自定义词云的形状,非常方便,但是需要注意中文编码和字体的问题,否则生成的词云会显示成乱码。

#大风厂(感兴趣的关键字,可能需要加入到词典中)

text = []

#遍历每句话

for line in word:

if '大风厂' in line:

text.append(line)

#词频统计

dict_dz = {}

for i in text:

dz1 = i.split(' ')

for w in dz1:

w1 = w.strip()

if dict_dz.__contains__(w1) :

dict_dz[w1] += 1

else:

dict_dz[w1] = 1

#生成text

text1 = ''

for i in text:

dz2 = i.split(' ')

for w in dz2:

text1 =text1 +' '+ w

#生成词云图

from wordcloud import WordCloud,STOPWORDS,ImageColorGenerator

#读取背景图片信息保存为array

background_Image = plt.imread('c1.jpg')

font = 'msyh.TTF'

#设置字体格式路径,不然显示不了中文,

#可以改成r'C:\Windows\Fonts\xxxx.ttf'来切换其他字体,这边我们把文件放在默认文件夹中。

#选择已经有的字体,根据词频否则生成图片的时候会报错:OSError: cannot open resource

wc = WordCloud(background_color = 'white',mask = background_Image,

max_words = 2000,stopwords = STOPWORDS,font_path = font,

max_font_size = 80,random_state = 42,scale = 1.5).generate(text1)

#这种方法和上面直接generate()的结果相同,但是传入的数据格式不同,

#这个函数传入的是要给字典,key(键)为词,value(值)为出现频率。

#wc.generate_from_frequencies(dict_dz)

#根据图片生成词云颜色,这里选择显眼的颜色

#如果需要黑白灰的词云颜色就把'#'删除

#image_colors = ImageColorGenerator(background_Image)

#wc.recolor(color_func = image_colors)

plt.imshow(wc)

plt.axis('off')

plt.show()

wc.to_file('c2.jpg') #保存图片

生成的词云图:

wordcloud

词云中词语越大代表和大风厂这个词相关度越高,可以看出和大风厂关系最紧密的有:蔡成功,陈岩石,郑西坡,李达康,职工股权,侯亮平,高小琴,山水集团等

4.1.基于TF-IDF提取关键词

上面的词云图表示了大风厂同时出现频数多的词,但出现频数多的词并不能代表是文本中的关键词,故使用TF-IDF进行关键词提取。TF-IDF权重通过计算词频和逆向文件频率来计算,这里直接利用jieba分词工具进行提取,列出20个关键词如下表所示:

【蔡成功,李达康,郑西坡,陈岩石,侯亮平,赵东来,沙瑞金,陈清泉,职工,高小琴,山水集团,尤瑞星,郑乾,欧阳菁,股权,打电话,常成虎,京州,陈海,郑胜利】

可以发现与大风厂相关的主要人物都列出来了,此外,还有一些特别词,如山水集团,京州,职工,股权等,通过这些关键词我们可以比较清楚的知道有关大风厂的大部分信息。我们可以推测出:大风厂事件很可能和山水集团还有职工股权纠纷有关,其事件主要涉及到的人是山水集团、京州公务员、大风厂职工,各部分人在事件中扮演不同的角色。如果想看到每个词具体的TF-IDF权重也可以利用scikit-learn包进行计算,然后再根据权重的大小进行重要性排序。

#基于tf-idf提取关键词

from jieba import analyse

tfidf = analyse.extract_tags

#analyse.set_stop_words('stop_words.txt') #使用自定义停用词集合

text_dz = ''

for l in text:

text_dz += l

text_dz += ' '

keywords = tfidf(text_dz,topK=20)

print (keywords)

4.2.运用word2vec挖掘语义相似关系

由于文本分析中也经常会用到word2vec将文本转化为词向量的形式后来挖掘词语语义上的相似关系。这里我们使用word2vec来测试一下模型效果。导入gensim库后,将文本转化为模型输入的形式后进行训练,然后就可以得到每个词的向量形式。

4.2.1 先简单介绍一下word2vec的基本使用:

训练模型的定义:

from gensim.models import Word2Vec

model = Word2Vec(sentences, sg=1, size=100, window=5, min_count=5, negative=3, sample=0.001, hs=1, workers=4)

#参数解释:

#其中的sentences是句子列表,而每个句子又是词语的列表,即list[list]类型。

#1.sg=1是skip-gram算法,对低频词敏感;默认sg=0为CBOW算法。

#2.size是输出词向量的维数,值太小会导致词映射因为冲突而影响结果,值太大则会耗内存并使算法计算变慢,一般值取为100到200之间。

#3.window是句子中当前词与目标词之间的最大距离,3表示在目标词前看3-b个词,后面看b个词(b在0-3之间随机)。

#4.min_count是对词进行过滤,频率小于min-count的单词则会被忽视,默认值为5。

#5.negative和sample可根据训练结果进行微调,sample表示更高频率的词被随机下采样到所设置的阈值,默认值为1e-3。

#6.hs=1表示层级softmax将会被使用,默认hs=0且negative不为0,则负采样将会被选择使用。

#7.workers控制训练的并行,此参数只有在安装了Cython后才有效,否则只能使用单核,anaconda会自带Cython。

训练后的模型保存与加载:

model.save(fname)

model = Word2Vec.load(fname)

模型使用

model.most_similar(positive=['woman', 'king'], negative=['man'])

#woman+king-man的词向量结果:输出[('queen', 0.50882536), ...]

model.doesnt_match("breakfast cereal dinner lunch".split())

#输出'cereal' ,分出不是一类的词

model.similarity('woman', 'man')

#两个词的相似度,输出0.73723527

model['computer'] # raw numpy vector of a word

#某个词的特征,输出array([-0.00449447, -0.00310097, 0.02421786, ...], dtype=float32)

4.2.2 分析与‘大风厂’相似的词语关系

#word2vec

import gensim

sentences = []

for line in word:

if line.strip() != '':

sentences.append(line.strip().split(' '))

model = gensim.models.Word2Vec(sentences,size = 100,window = 5,min_count = 5,workers = 4)

例如打印出“大风厂”的向量如输出结果所示。

print (model['大风厂'])

向量图

我们也可以输出通过word2vec生成向量之后,和‘大风厂’最相似的向量

#这里我们输出相似度最高的18个(本来取了20个,最后两个词无特别含义,这里我们topn取18)

for k,s in model.most_similar(positive = ['大风厂'],topn=18):

print (k,s)

关联词向量

最后,我们测试一下word2vec的效果,由于篇幅有限这里就不介绍word2vec原理了,具体可以去看peghoty大神的word2vec系列博客。https://blog.csdn.net/itplus/article/details/37969635

【 需要注意的是:如果使用word2vec建议不要去除停用词,因为word2vec用于发现上下文的关系,如果去除了停用词,生成的词向量可能会受影响,最后生成的词向量差异过小没有区分度,这里我们为了方便还是使用去除停用词的语料,这导致了出现的关联词向量相似度两极分化的现象。另外在深度学习中也是不需要去除停用词的,但是去除了模型结果会有略微提升】

word2vec模型的优点在于不仅考虑了语境信息还压缩了数据规模。了解word2vec的人都知道,他的强大之处在于可以利用基本代数公式来发现单词之间的关系(比如,“国王”-“男人”+“女人”=“王后”)。所以word2vec生成的词向量很明显可以应用于:代替词袋用来预测未知数据的情感状况。

现在这些词向量已经捕捉到上下文的信息,我们来看一下效果:

计算一个词,该词的词向量和词向量(山水集团+大风厂-高小琴)最接近。

由于高小琴是山水集团的boss,而蔡成功是大风厂的boss,那么按照word2vec的原理我们可以自己推理出,这个词应该是'蔡成功'。

r = model.most_similar(positive = ['山水集团','大风厂'],negative=['高小琴'],topn=1)

print (r)

结果为:

word2vec运算结果.png

[('蔡成功', 0.9983329772949219)]

和预期结果一样,这就是word2vec的强大之处。

另:如果要直观的看词向量的效果,在词向量数量不是特别多的情况下,可以使用pca或LDA对词向量降维,降到两维度或者三维就可以进行可视化。

References:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值