【python】python+gephi对《西部世界》进行人物关系提取

由于最近一部英剧《西部世界》挺火,在实验楼中看到有关人物关系提取的教程,所以尝试对其进行了人物关系的简单抽取,用到了第三方的jieba分词器,和数据分析软件gephi

环境准备:

  • 操作系统:linux ubuntu
  • python: 2.7
  • 第三方模块:分词器jieba
  • 分析软件:gephi-0.9.1

首先先通过各大搜索引擎搜索获得主要的人物

wdict.txt

德洛丽丝 100 nr
艾伯纳西 100 nr
伯纳德 100 nr
泰迪 100 nr
黑衣人 100 nr
福特博士 100 nr
接待员 100 nr
运营人员 100 nr
游客 100 nr
卡伦 100 nr
土匪 100 nr
实验人员 100 nr
老鸨 100 nr
开发人员 100 nr
梅芙 100 nr
劳伦斯 100 nr
年轻男子 100 nr

获取西部世界的剧本(第一季1-2集)

westworld.txt

第1集 - 科技乐园造人吸金 叛逆意识暗中萌生
德洛丽丝正如往日一样,清晨时分,从梦中醒来,迎接一天美好的开始。她向父亲艾伯纳西道别,然后出门写生,再到小镇转悠。德洛丽丝的久别多时的好朋友泰迪从外地回来,两人相互对视的眼神中,透露着浓浓的爱意。尽管很久不见,但泰迪和德洛丽丝的感情丝毫不减。两人骑着马在草原上驰骋,谈天说地,直到晚上,泰迪把德洛丽丝送回她的家。
然而回到家时,德洛丽丝却发现家里的牛还在乱跑,要在平时这个时间,父亲早已把家里的牲口安置好了。眼前惨绝人寰的景象让德洛丽丝崩溃了,她看见父母被土匪残忍杀害。泰迪立刻出手把两个土匪击毙,可惜父母亲已回天乏术。一位黑衣人突然出现,他用着迷的目光看着德洛丽丝。他认识德洛丽丝三十年了,这次看见的德洛丽丝,比以往多了一些勇气。 泰迪察觉到黑衣人对德洛丽丝的不善,打算连他也一起击倒,然而开枪过后,黑衣人屹立不倒。黑衣人傲慢地告诉泰迪,他是“新住民”,泰迪伤害不了自己,但泰迪他们却可以任由“新住民”随意处置。
泰迪茫然地跪下,手无缚鸡之力的德洛丽丝被黑衣人拖走。第二天,德洛丽丝从睡梦中醒来,又开始新的一天,一如既往地重复着之前发生过的事情。
西部世界是福特博士一手策划出来的乐园,里面的“接待员”皆是他属下公司制造的人造人。西部世界通过逼真的场景,还有和活人没什么两样的人造人,为游客带来最真实的体验。运营人员给乐园里的人造人编排剧本,对人造人进行编程。乐园的入场费价值不菲,但游客们能在这里满足了杀戮和性的欲望,这让不少有钱人都感到乐而忘返。黑衣人是众多游客的其中一位,他来了这里三十年了,但这次前来,他不仅仅是为了满足一己私欲,还带着更深层次的目的。
最近,乐园里出现了不少不按剧本编排行动的人造人,这让很多运营人员担心,尤其是女高管卡伦。她认为应该把这些不可控制的人造人直接回收,不再循环使用,但福特却不甚赞同。福特认为这些人造人出现反叛,都是有原因的,只是多次循环使用,才令他们的意识和记忆出现了错乱。福特为了让自己的作品更加接近人类,给他们植入了“冥想”的意识系统,让他们得以自发性地思考。
艾伯纳西无意中发现了一张来自现实世界的照片,这让他对这个世界产生了怀疑。一连串的记忆在他脑海里涌现,原来,他在西部世界里曾经扮演过大学教授和食人族,而现在这个剧本里,他扮演的是德洛丽丝的父亲。他用尽自己毕生所知道的东西,在一天清晨时,借用莎士比亚的名句告诉了德洛丽丝。
在剧情中扮演土匪的其中一名人造人失控了,在西部世界造成了一场混乱。尽管大部分人造人都能修好,但卡伦等人察觉到艾伯纳西和德洛丽丝在早晨时的那场异常的对话,可能会有后患,他们单独把两人放在实验室,重新进行程序设置。艾伯纳西在接受盘问时,透露了很多反叛的念头,但用的是莎士比亚的句子,福特对自己的作品并无怀疑,他觉得艾伯纳西只是意识错乱了。
德洛丽丝也接受了实验人员的盘问和调试,在回答中,德洛丽丝保持着一如既往的人造人姿态,面无表情,情绪毫无波动。
艾伯纳西的自发性思维让福特和其他运营人员感受到威胁,只能把他暂时回收,德洛丽丝则重新投入西部世界继续当“接待员”。运营人员对德洛丽丝都很放心,毕竟,她是西部世界里最老的一位接待员了。
德洛丽丝开始了新的一天,向换了人扮演的父亲道别,然后准备出门写生。她一脸笑意地享受着晨曦的阳光,似乎在想着什么。
第2集 -老鸨忆起残忍往事 黑衣人肆杀接待员
年轻男子怀着既期待又紧张不安的心情,随朋友来到西部世界。美丽的接待员带领他进行换装,还有向他简单地讲解了西部世界可以怎么玩。在现实生活里,一直中规中矩的年轻男子对这样无法无天的地方似乎有点抵触,但毕竟好奇心作崇,他还是跟朋友一起,进入了这个不可思议的地方。
伯纳德正和一位开发人员讨论着艾伯纳西。开发人员反复查看艾伯纳西的录像,发现他看见那张来自现实世界的照片后,懂得去思索,开发人员认为是他的认知出现了问题。伯纳德和开发人员都认为,艾伯纳西很特别,希望能让他重新投入到西部世界中。
在乐园里扮演着老鸨角色的梅芙,最近似乎也出现了记忆混乱。一些来自过去的片段不时地出现,导致她在接待客人时突然沉默。运营人员发现到梅芙的异常行为后,打算把她回收。
黑衣人继续来到西部世界大杀四方。他之前杀掉一个接待员,然后割下他的头盖骨,头盖骨背后,刻画了一个迷宫地图一样的图案,黑衣人认为,这个地图就是找到西部世界深层秘密的关键。身手不凡的他捉了一个似乎知道真相的接待员劳伦斯,然后在他面前不断杀人,并捉了他的家人,打算威胁其说出进入西部世界内部的入口。劳伦斯本身带有感情,面对家人遭受黑衣人的威胁,难免感到伤心和愤怒,但是他依旧选择闭口不提,哪怕是妻子就在自己眼前,命丧黑衣人之手。劳伦斯的女儿突然开口,把所在地的位置告诉了黑衣人。但她面无表情地警告黑衣人,那里不是他该去的地方。黑衣人不屑地笑了笑,继续他的挖掘真相之旅。
开发人员对梅芙进行了调试,认为她调整一下参数,便可以继续投入运作,毕竟她是那么出色的一名接待员。重新回到老鸨角色的梅芙比起以前,显得更加轻浮,带有攻击性。尽管经过了调试,但她的记忆越来越清晰,甚至做起了噩梦。她记起自己曾经有个女儿,两人相依为命过着简单开心的生活,但这一切都被突然闯进的印第安人打破了。残暴的印第安部落杀害了周围的人,还打算伤害自己和女儿,幸好此时有牛仔出手相助,才得以暂时逃命。梅芙带着女儿一脸惊恐地逃回家里,这时,门外出现了一个印第安人,她执起枪杆,打算与这个野蛮人决一死战。门开了,进来的并不是印第安人,而是黑衣人,情绪激动的梅芙向黑衣人开了几枪,只见黑衣人毫发无损。
梅芙知道自己在做噩梦,她用自创的稳定心神方式,心里默念“三、二、一”,试图唤醒自己。醒来后,她发现自己躺着一个手术室,周围的场景是她从未见过的,眼前两个对着自己开膛破肚的医护人员,让她惊恐万分。医护人员看见她醒来了也显得很惊讶,毕竟他们明明对梅芙进行了睡眠处理,可现在却不知为何突然醒来了。
梅芙抢过刀子威胁医护人员,然后趁机逃出了手术室。她拖着尚未完成手术的身子,不断在这个陌生的环境狂奔,当她来到一间清洗处理室时,眼前的场景吓了她一跳。众多赤身裸体的人造人,正被各种医护人员处理着。梅芙手脚发软,随后摊坐在地。从手术室追过来的医护人员及时给她打了麻醉,带走了她。
德洛丽丝继续在西部世界扮演她的农家女孩角色,但她没有忘记之前发生的事。夜里,万籁俱寂,德洛丽丝来到后院的,刨开地上的泥土,找到一把不知何时埋在地下的手枪。

接下来就是分析人物关系的py编写

首先我们引入所需模块包括os、sys和第三方分词器 jieba

1
2
3
4
# -*- coding: utf-8 -*-
import os, sys
import jieba, codecs, math
import jieba.posseg as pseg

定义相关数据结构接收数据:

1
2
3
names = {}          # 姓名字典
relationships = {} # 关系字典
lineNames = [] # 每段内人物关系

利用分词器jieba统计分析出场人物的总数,从wdict.txt中提取:

1
jieba.load_userdict("wdict.txt")        # 加载人物字典

读取剧本,分析名字出现的频率,向字典中加入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
with codecs.open("westworld.txt", "r", "utf8") as f: # 读取剧本分析人物出现次数
for line in f.readlines():
poss = pseg.cut(line) # 分词并返回该词词性
lineNames.append([]) # 为新读入的一段添加人物名称列表
for w in poss:
if w.flag != "nr" or len(w.word) < 2:
continue # 当分词长度小于2或该词词性不为nr时认为该词不为人名
lineNames[-1].append(w.word) # 为当前段的环境增加一个人物
if names.get(w.word) is None:
names[w.word] = 0
relationships[w.word] = {}
names[w.word] += 1 # 该人物出现次数加 1

分析各个人物之间的关系

根据人物在lineNames中每段是否同时出现来判断,出现次数越多人物关系越密切

1
2
3
4
5
6
7
8
9
for line in lineNames:                  # 对于每一段
for name1 in line:
for name2 in line: # 每段中的任意两个人
if name1 == name2:
continue
if relationships[name1].get(name2) is None: # 若两人尚未同时出现则新建项
relationships[name1][name2]= 1
else:
relationships[name1][name2] = relationships[name1][name2]+ 1 # 两人共同出现次数加 1

将分析结果持久化到本地

将分析得到的结果保存为.txt格式存储到本地

注意:为在gephi中生成相应的节点
节点表格中的第一行格式为Id Label Weight
边表格中的第一行格式为Source Target Weight

1
2
3
4
5
6
7
8
9
10
11
12
# 节点表
with codecs.open("westworld.txt", "w", "gbk") as f:
f.write("Id Label Weight\r\n")
for name, times in names.items():
f.write(name + " " + name + " " + str(times) + "\r\n")
# 关系表
with codecs.open("westworld.txt", "w", "gbk") as f:
f.write("Source Target Weight\r\n")
for name, edges in relationships.items():
for v, w in edges.items():
if w > 3:
f.write(name + " " + v + " " + str(w) + "\r\n")

接下来就是讲数据导入gephi中进行分析

对导入后的数据进行平均化和模块化处理
img1

最后的结果

img1