用word2vec解读延禧攻略人物关系

article/2025/9/1 12:51:59

用word2vec解读延禧攻略人物关系

原文来自公众号 无界社区mixlab 链接如下:
https://mp.weixin.qq.com/s/zRqt9OL6G1s3UZY1AJR9ag
关系图谱地址 https://shadowcz007.github.io/text2kg/
本文是对原文进行的复现,现将具体的实现过程记录如下。

#####一. 语料准备

  • 延禧攻略剧本
  • 延禧攻略小说
  • 剧中人物名称

######1.爬取延禧攻略剧本
要获取原始语料的通用办法就是利用爬虫技术对相应的内容进行爬取,由于本次爬取的内容比较简单,不需要考虑网址去重、增量爬取等比较难的问题,因此没有必要选取 scrapy 这种重量级的工具,直接用 request + BeautifulSoup 就足以应付了,代码如下:

# -*- coding: utf-8 -*-
# Author:gaozhengjie
# Blog:https://www.jianshu.com/u/02877dbc2662
# E-mail:gaozhengj@foxmail.com
# Python Version:3.6.1
# Time:2018/8/29
# Description:爬取延禧攻略剧本,并保存在本地import urllib.request
from bs4 import BeautifulSoup# 延禧攻略小说的URL
url = 'http://www.pingyaoji.com/yanqing/yxgljb/'
res = urllib.request.urlopen(url)
soup = BeautifulSoup(res, "html.parser")
character_list = soup.select('ul')[3]# 开始捕捉具体页面的信息
content = ''
for each_li in character_list.select('.title'):res = urllib.request.urlopen('http://www.pingyaoji.com' + each_li.get('href'))soup = BeautifulSoup(res, "html.parser")content = content + soup.select('p')[0].getText()with open(u'延禧攻略剧本.txt', 'w', encoding='utf-8') as fp:fp.write(content)

######2. 爬取延禧攻略小说
小说和剧本均来源于同一个网站,其网页结构均一样,因此代码中只需要修改对应的网址和保存的文件名字即可。

# -*- coding: utf-8 -*-
# Author:gaozhengjie
# Blog:https://www.jianshu.com/u/02877dbc2662
# E-mail:gaozhengj@foxmail.com
# Python Version:3.6.1
# Time:2018/8/29
# Description:爬取延禧攻略小说,并保存在本地import urllib.request
from bs4 import BeautifulSoup# 延禧攻略小说的URL
url = 'http://www.pingyaoji.com/yanxigonglue/?tdsourcetag=s_pctim_aiomsg'
res = urllib.request.urlopen(url)
soup = BeautifulSoup(res, "html.parser")
character_list = soup.select('ul')[3]# 开始捕捉具体页面的信息
content = ''
for each_li in character_list.select('.title'):res = urllib.request.urlopen('http://www.pingyaoji.com' + each_li.get('href'))soup = BeautifulSoup(res, "html.parser")content = content + soup.select('p')[0].getText()with open(u'延禧攻略小说', 'w', encoding='utf-8') as fp:fp.write(content)

######3. 剧中人物名称
延禧攻略演员表

上面这个截图就取自于前面我提供的网址里面显示的延禧攻略演员表,这个相比前两个来说,我采用的方法就很简单粗暴了。因为这本身就是一个表格,我直接将其复制到了 Excel 表格中进行处理,复制过去的内容会变成一列,剧中名称和真实名称会交替出现,如下图所示,在这种情况下,如何获取剧中角色的名称呢,可以参考此文 使用Excel隔行选取的3种小技巧。

将网页中的内容复制粘贴到 Excel 中

最后别忘记两件事:

  • 将里面的 “饰演角色” 和 “演员名字” 删掉;
  • 将 “/” 中的内容进行分割,这是同一个人物的不同称呼,当然我们还需要对这些称呼进行完善,部分结果如下图所示。在此感谢 小昕同学 不辞劳苦帮忙完善的角色名称表!

延禧攻略名称表

#####二. 数据处理
######1. 分词

  • jieba 分词
  • 停用词表
  • 由角色名称构成的用户自定义词典,以此确保分词的时候能将人物名称提取出来
# -*- coding: utf-8 -*-
# Author:gaozhengjie
# Blog:https://www.jianshu.com/u/02877dbc2662
# E-mail:gaozhengj@foxmail.com
# Python Version:3.6.1
# Time:2018/8/29
# Description:对延禧攻略的文本进行去停用词和分词处理import jieba
import jieba.analyse
import openpyxl# jieba.analyse.set_stop_words('.\\stopwords.txt')
stopwords_file = 'stopwords.txt'  # 停用词表
stopwords_set = open(stopwords_file, 'r', encoding='utf-8').read().split('\n')  # 导入停用词表
stopwords = [word.strip() for word in stopwords_set]
name_list =  open('name_dict.txt', 'r', encoding='GBK').read().split('\n')
jieba.load_userdict(name_list)text = open(u'延禧攻略小说.txt', 'r', encoding='utf-8').read()
word_cut = jieba.cut(text, cut_all=False)  # 精确模式,返回的结构是一个可迭代的genertor
word_list = list(word_cut)  # genertor转化为list,每个词unicode格式
word_set = []
for word in word_list:word = word.strip()if word not in stopwords and word != '' and not word.isdigit():  # 去停用词# if word >= u'\u4e00' and word <= u'\u9fa5':  # 判断是否是汉字word_set.append(word)
word_str = ' '.join(word_set)
# 对别称进行替换并保存分词后的结果
wb = openpyxl.load_workbook('延禧攻略人物名称.xlsx')
sheet = wb[wb.sheetnames[0]]
for i in range(1, sheet.max_row+1):for j in range(2, sheet.max_column+1):if sheet.cell(row=i,column=j).value != None:word_str = word_str.replace(sheet.cell(row=i,column=j).value, sheet.cell(row=i,column=1).value)
open(u'延禧攻略小说分词结果.txt', 'w', encoding='utf-8').write(' '.join(word_set)) 

######2. 训练 Word2Vec 模型
关于 Word2Vec 的介绍,可以查看公众号中的原文,关于 gensim 中的 word2vec 模型的简单使用,可以参考博文 word2vec的应用----使用gensim来训练模型

from gensim.models import word2vec#加载分此后的文本,使用的是Ttext2Corpus类
sentences = word2vec.Text8Corpus(u'延禧攻略剧本和小说分词结果.txt')#训练模型,部分参数如下
model  = word2vec.Word2Vec(sentences, size = 100, hs = 1, min_count = 1, window = 3)# 模型的保存与载入
model.save(u'延禧攻略小说.model')

######3. 处理数据
本模块主要涵盖以下任务:

  • 关系图谱中人物的图标有大小之分,越核心的人物的图标越大,关于如何界定人物的重要性呢,本文采用 TF-IDF 方法来计算人物的重要性,将其 TF-IDF 值作为图标的 size 的基准值。对于频率实在太低的边缘性人物,本文直接为其设定了 size 的下界为 1,以此避免图标太小根本看不见的情况。
    result.append({"name": i[0], "value": i[1], "symbolSize": max(i[1]*30, 1)})
  • 通过设定阈值,计算出每个人物的近邻集合,近邻即是与谁的关系比较接近
  • 先将所有人物按照频率降序排列,然后将所有人物依次遍历计算一遍相似度,找到相似人集合,以此建立 source → target 有向关系图
  • 将数据构造成 json 格式,然后存储于本地文件夹中,方便前端进行调用

需要注意的是
json 格式中的引号是双引号,而非单引号,同时,一定要用 json.dumps() 方法进行将字符串转换成 json 格式,否则前端的关系图谱无法正常显示。关于python如何解析和转换json数据的可以参看我之前的博文 用Python解析json数据。

from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
import gensim
import jsonfp = open(u'延禧攻略剧本和小说分词结果.txt', 'r', encoding='utf-8').read()
corpus = [fp]vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))#第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵
word=vectorizer.get_feature_names()#获取词袋模型中的所有词语
weight=tfidf.toarray()#将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重name_dict = {}
with open('name.txt', 'r', encoding='GBK') as fp:for each_name in fp.readlines():name = each_name.strip()try:index = word.index(name)except ValueError as error:pass
#             print("无对应的名称", each_name.strip())else:frequency = weight[0,word.index(name)]
#             print(name, frequency)name_dict[name] = frequencysort_list = sorted(name_dict.items(),key = lambda x:x[1],reverse = True)# 计算每个节点的信息
result = []
for i in name_dict.items():result.append({"name": i[0], "value": i[1], "symbolSize": max(i[1]*30, 1)})# 接下来主要是处理 source → target 的关系
# 主要需要计算人物之间的相似度
model = gensim.models.Word2Vec.load(u'延禧攻略小说.model')
link = []
for i in range(0,len(sort_list)-1):for j in range(i+1, len(sort_list)):try:if model.similarity(sort_list[i][0], sort_list[j][0]) > 0.9:link.append({"source": sort_list[i][0], "target": sort_list[j][0]})except KeyError as error:passwith open('node_info.txt', 'w', encoding='utf-8') as fp:fp.write(json.dumps(result))
with open('edge_info.txt', 'w', encoding='utf-8') as fp:fp.write(json.dumps(link))

#####前端显示
采用的前端数据可视化框架是 echarts
要显示人物关系图,需要注意以下几个问题:

  • json 数据通过 ajax 访问本地文件进行加载,此时需要配置服务器才能进行访问,所幸的是 python 自带的有 http.server ,使用起来非常方便,只需要一行命令即可启动服务器,然后在浏览器中访问即可。

python -m http.server

<!DOCTYPE html>
<html style="height: 100%">
<head><meta charset="utf-8"><!-- 引入 ECharts 文件 --><script src="echarts.min.js"></script><script src="jquery-3.2.1.min.js"></script><script src="http://echarts.baidu.com/gallery/vendors/echarts/extension/dataTool.js"></script>
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%; width: 100%"></div><script type="text/javascript">$.ajax({type: "GET",url: "/data/node_info.txt",dataType: "text",success: function (data) {data = eval(data)console.log(data);node_info = data;},error: function (err) {alert("节点信息读取错误");}});$.ajax({type: "GET",url: "/data/edge_info.txt",dataType: "text",success: function (data) {data = eval(data)console.log(data);edge_info = data;},error: function (err) {alert("边信息读取错误");}});var dom = document.getElementById("container");var myChart = echarts.init(dom);var app = {};option = null;app.title = '力引导布局';myChart.showLoading();$.get('/data/les-miserables.gexf', function (xml) {myChart.hideLoading();var graph = echarts.dataTool.gexf.parse(xml);option = {title: {},tooltip: {},legend: [{data: categories.map(function (a) {return a.name;})}],animation: false,  // 是否开启动画series: [{type: 'graph',layout: 'force',data: node_info,links: edge_info,roam: true,label: {normal: {position: 'right',  // 标签位置show: true,  // 显示标签// showContent: true}},force: {initLayout: 'circular',// layoutAnimation: false,repulsion: 100  //排斥力},draggable: true,  // 允许拖拽focusNodeAdjacency: true,  // 在鼠标移到节点上的时候突出显示节点以及节点的边和邻接节点emphasis: {  // 设置高亮图形样式lineStyle: {width: 3},itemStyle: {color: 'black',}},roam: true,  // 开启鼠标缩放和平移漫游edgeLength: 10,}]};myChart.setOption(option);}, 'xml');;if (option && typeof option === "object") {myChart.setOption(option, true);}
</script>
</body>
</html>

#####参考文章

  • word2vec的应用----使用gensim来训练模型
  • 使用Excel隔行选取的3种小技巧
  • Python3实现简单的http server
  • ECharts Graph Force Demo

http://chatgpt.dhexx.cn/article/rLwruXou.shtml

相关文章

基于共现关系的人物关系图

共现&#xff0c;顾名思义 &#xff0c;共同出现。共现分析有多种&#xff0c;比如分析参考文献、作者、词汇对&#xff08;关键词&#xff09;、作者机构等同时出现的情况&#xff0c;分析两者之间的密切联系。 在科研中貌似出现最多的是共词分析&#xff0c;即根据词汇在同一…

关系模式(关系模式必须遵循)

关系模式和关系是什么意思&#xff1f; 关系模式和关系&#xff1a;描述模式描述关系的静态结构&#xff0c;由模式名、关系模式所包含的属性及属性值所满足的条件组成模式定义。 委托代理关系有哪些内涵及模式&#xff1f; 委托代理关系有5种模式 (1)委托方与代理方均为单一的…

关系型数据库表之间的联系[关系]详解

关系型数据库表之间的联系[关系]详解 在表中&#xff0c;行和列的逻辑顺序无关紧要。每个表至少包含一列&#xff0c;该列被称为主键&#xff0c;它唯一地标识了表中的每一行。 键是表中扮演特殊角色的列&#xff0c;有两种键&#xff1a;主键、外键。 主键&#xff08;Prim…

解析SQL的表间血缘关系工具

一、sqllineage SQL Lineage Analysis Tool powered by Python 源码地址&#xff1a;https://github.com/reata/sqllineage 安装&#xff1a;pip install sqllineage 使用&#xff1a;sqllineage -e "select * from tabA" sqllineage -f demo.sql 二、在线工具 http…

数据库进阶3--表之间的关联关系

学习这一部分之前先去回顾一下之前的基础知识哦&#xff01;&#xff01;&#xff01; 这里写目录标题 关联关系关联关系分类关联关系的创建关联查询等值连接内连接外连接左连接右连接 关联关系 指一个业务中创建的表与表之间的关系。其关联关系有一下几种&#xff1a; 关联…

小感悟:多对多关系,一定要创建关系表吗?

- 感悟&#xff1a;多对多关系&#xff0c;一定要创建关系表吗&#xff1f; 初学数据库时&#xff0c;如果出现多对多关系&#xff0c;那么就需要额外创建一个关系表&#xff0c;将两边的外键存入&#xff0c;以此建立连接&#xff1b; 但是遇到多对多关系&#xff0c;一定需…

关系推理

知识图谱 知识图谱本质上是语义网络&#xff0c;是一种基于图的数据结构&#xff0c;由节点&#xff08;“实体”&#xff09;和边&#xff08;“关系”&#xff09;组成。在知识图谱里&#xff0c;每个节点表示现实世界中存在的“实体”&#xff0c;每条边为实体与实体之间的“…

【数据库专题】智多星带你五分钟攻略“关系代数”——《狗叫江湖》第二幕续集

、 “百分百无规则教学时间&#xff01;&#x1f525;&#x1f525;&#x1f525;” 兄弟萌晚上好&#xff01;我是无规则教学创始人【东星耀杨】&#xff0c;感谢兄弟萌对我的不离不弃&#xff0c;爱你们熬爱你们&#xff01;还望兄弟萌给为师一个三连支持&#xff0c;要不然我…

互联网人的职场关系攻略

本篇文章原本是给公司校招新人的做的一个内部培训ppt(需要的可以找我)&#xff0c;作为一名技术人员&#xff0c;真是不太擅长处理职场关系&#xff0c;但身在职场&#xff0c;估计都能感到其重要性&#xff0c;要想愉快恰饭&#xff0c;万万不可忽视&#xff0c;为了防止误导新…

vue 生命周期详解 (附代码)

一、 vue的生命周期是什么 vue每个组件都是独立的&#xff0c;都有自己的生命周期&#xff0c;从一个组件创建、数据初始化、挂载、更新、销毁&#xff0c;就是一个组件的生命周期。 一个组件首次加载时&#xff0c;也就只执行 创建、数据初始化到挂载。 生命周期给了用户在…

详解vue生命周期(常问面试题)

面试常问&#xff1a;1、vue生命周期是什么&#xff1f; Vue 实例从创建到销毁的过程为生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程&#xff0c;称之为 Vue 的生命周期。 2、vue生命周期的作用是什么&#xff1f; 在控制整个…

Vue学习 — 详解Vue生命周期

目录 一、前言二、生命周期1、beforeCreat / created(1)、beforeCreat&#xff1a;&#xff08;2&#xff09;、created&#xff1a; 2、eforeMount / mounted&#xff08;1&#xff09;、beforeMount&#xff1a;&#xff08;2&#xff09;、mounted&#xff1a; 3、beforeUpd…

Vue生命周期详解学习笔记

生命周期 生命周期又名生命周期回调函数&#xff0c;生命周期函数&#xff0c;生命周期钩子。生命周期是Vue在关键时刻帮我们调用的一些特殊名称的函数。生命周期函数的名字不可更改&#xff0c;但函数的具体内容是程序员根据需求编写的。生命周期中的this指向vm或组件实例对象…

Vue的生命周期详解,一文带你弄懂Vue的生命周期

大家好&#xff0c;我是Ned&#x1f440;&#xff0c;一个刚刚入门前端未满两年的大三小学生&#x1f339; 未来路还长&#x1f389;, 一起努力加油吧❤~ 前言 我记得尤大曾经说过&#xff0c;你看Vue源码干嘛&#xff1f;你使用Vue又不需要它的源码&#xff0c;你只需要会用就…

Vue系列之vue生命周期详解

文章の目录 1、什么是 vue 生命周期2、生命周期钩子函数2.1、beforeCreate&#xff08;创建前&#xff09;2.2、created&#xff08;创建后&#xff09;2.3、beforeMount&#xff08;挂载前&#xff09;2.4、mounted&#xff08;挂载后&#xff09;2.5、beforeUpdate&#xff0…

Vue的生命周期详解(简单易懂)

一、定义 Vue的生命周期就是实例从创建到销毁的一个过程&#xff0c;即从创建、初始化数据、编译模板、挂载Dom->渲染、更新->渲染&#xff0c;卸载等一系列的过程。 写代码时注意&#xff1a; Vue的生命周期钩子函数会自动绑定this上下文到实例中&#xff0c;这意味着…

【Vue】 Vue生命周期详解

每一个vue实例从创建到销毁的过程&#xff0c;就是这个vue实例的生命周期。在这个过程中&#xff0c;它经历了从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。 通过这张流程图已经可以清晰的看到Vue整个生命周期的过程 beforeCreate( 创建前…

Vue极简教程,生命周期详解

Vue | 渐进式 | JS框架 | 极简教程 生命周期详解

vue实例的生命周期详解

Vue实例的生命周期 简介 此篇文章说的是最简单的单个VUE组件的生命周期。 官网中的长图诠释了Vue实例从创建&#xff0c;运行到销毁的整个过程。从vue实例的创建&#xff0c;运行&#xff0c;销毁期间&#xff0c;总是伴随着各种各样的事件&#xff0c;这些事件统称为生命周期…

【Vue生命周期详解】

细谈Vue声明周期 什么是生命周期&#xff1f;Vue的生命周期beforeCreate( 创建前 )&#xff1a;created ( 创建后 &#xff09;&#xff1a;beforeMount&#xff1a;mounted&#xff1a;beforeUpdate&#xff1a;updated&#xff1a;beforeDestroy&#xff1a;destroyed&#x…