《择天记》与自然语言处理

本文的灵感来源于李金同学的一篇关于金庸武侠小说的自然语言处理的文章,大家有兴趣可以在GitHub上关注他。地址

02 前言

在上一篇文章中,我们使用Python来收集网络数据。这个可以复用的Python小程序,可以抓取任意电影的海报以及剧照。当你想要找一张《加勒比海盗5》的电影海报作为电脑桌面的时候,这个小程序就可以轻松帮到你了!上一篇文章的链接

你可能还是没有下定决心学习Python,那么今天的教程将和大家一起用Python做一点有趣的事情。相信大家可以在这个小项目中体验到Python的强大!

03 Python与择天记

我们将在今天用Python来分析一本热门的网络小说《择天记》,在这个项目中,我们可以做到以下几件很有意思的事:

  1. 用Python分析出《择天记》小说中实力境界的划分体系
  2. 用Python找出《择天记》中的情侣
  3. 用Python分析出《择天记》中的大人物
  4. 用Python分析猫腻(择天记的作者)的写作风格
  5. .....

04 开始分析

04.01 读取小说文本

先在网络上下载一本择天记小说的txt版本,保存在项目目录中。我下载的小说保存下来就是64565.txt。使用Python读取整个文本,并构建列表。


In [17]:
# 读取择天记小说
with open('64565.txt',encoding='utf-8') as f:
    cont = [line.strip() for line in f.readlines() if line.strip()]

# 尝试在控制台打印一段文本
for line in cont[105:107]:
    print(line)


那名叫霜儿的丫环,看着他认真说道:“以后,再也不要说这句话。”
“为什么?”陈长生认真反问道。

04.02 统计《择天记》人物出场次数

我们需要收集在择天记中出现的人物姓名,以便统计人物的出场次数。主要人物的姓名抓了《择天记》百度百科中的人物列表,保存成了TXT文本,名为names.txt


In [3]:
#读取人物名字
with open('names.txt') as f:
    names = [name.strip() for name in f.readlines()]

print(names)


['陈长生', '徐有容', '唐三十六', '唐棠', '莫雨', '寅行道', '天海圣后', '梅里砂', '王之策', '计道人', '商行舟', '尘儿', '余人', '陈留王', '相王', '中山王', '娄阳王', '天海承武', '天海胜雪', '平国公主', '徐世绩', '薛醒川', '周通', '苏墨虞', '庄换羽', '霜儿', '王破', '肖张', '梁王孙', '荀梅', '唐家二爷', '关白', '别天心', '凌海之王', '司源道人', '桉琳', '白石道人', '茅秋雨', '庄之涣', '安华', '陈酬', '陈观松', '费典', '薛河', '梁红妆', '秋山君', '苟寒食', '梁笑晓', '关飞白', '梁半湖', '白菜', '苏离', '小松宫', '南方圣女', '叶小涟', '钟会', '落落', '折袖', '轩辕破', '金玉律', '白行夜', '小德', '魔君', '尼禄', '南客', '汗青', '黑袍', '魔帅', '陈玄霸', '周独夫', '先帝', '太宗', '太祖', '天机老人', '朱洛', '无穷碧']

In [4]:
# 统计人物出场次数

def find_main_charecters(num = 10):
    novel = ''.join(cont)
    count = []
    for name in names:
        count.append([name,novel.count(name)])
    count.sort(key = lambda v : v[1],reverse=True)
    return count[:num]

find_main_charecters()


Out[4]:
[['陈长生', 15673],
 ['唐三十六', 3609],
 ['徐有容', 3262],
 ['苏离', 1889],
 ['落落', 1877],
 ['折袖', 1656],
 ['苟寒食', 1183],
 ['轩辕破', 1158],
 ['魔君', 1146],
 ['王破', 1134]]

我们使用echarts做数据可视化的工作


In [4]:
from IPython.display import HTML

chart_header_html = """
<div id="main_charecters" style="width: 800px;height: 600px;" class="chart"></div>
<script>
    require.config({
         paths:{
            echarts: '//cdn.bootcss.com/echarts/3.2.3/echarts.min',
         }
    });
    require(['echarts'],function(ec){
    var charectersNames = ['陈长生', '唐三十六', '徐有容', '苏离', '落落', '折袖', '苟寒食', '轩辕破', '魔君', '王破', '商行舟', '周通', '南客', '莫雨', '秋山君', '黑袍', '天海圣后', '王之策', '朱洛', '肖张']
	var charectersCount = [15673, 3609, 3262, 1889, 1877, 1656, 1183, 1158, 1146, 1134, 1110, 945, 874, 746, 624, 589, 554, 532, 513, 476]

	var charectersCountChart = ec.init(document.getElementById('main_charecters'))

	charectersCountChart.setOption({
		title:{
			text: '择天记人物出场次数统计图'
		},
		tooltip:{},
		xAxis:{
			type: 'value',
			data: ['出场次数']
		},
		yAxis:{
			type: 'category',
			data: charectersNames.reverse()
		},
		series:{
			name: '主要人物',
			type: 'bar',
			data: charectersCount.reverse()
		}
	})
});
</script>
"""

# 我本地的浏览器可以渲染出来,放到git上就不行了...所以拿图片代替了

我们可以清楚的看到,《择天记》的主角为陈长生共出场接近16000次,紧随其后的是唐三十六(男性)和徐有容(有容奶大是女性)。仅从这个简单的数据,我们就可以推测唐三十六是主角陈长生的好基友徐有容很有可能和陈长生是恋人关系

另外,我们看到其他出场率比较相似的人物中,女性角色明显不多。我们可以大致推断《择天记》这本小说是一个单女主的小说。更进一步的说,徐有容和陈长生在整部小说中可能都很专情

出场次数前20的人物中,可以看出一个明显的规律——主要人物的人名都非常奇葩,一看就不是普通人能叫的名字!在现实生活中不可能有人叫唐三十六折袖苟寒食商行舟南客这样的名字。所以,《择天记》的作者多半是一个很中二的人

另外还有一个有趣的事情,就是这些人物的姓氏明显都不相同,只有王之策王破都姓王。这一点我们可以推断,《择天记》中的故事很有可能不是关于家族之间的纷争,或者说着墨不多。我们看到魔君的出场次数达到700多次,这种名字一看就是反派,无一例外。

第一部分的结论

  • 《择天记》男主陈长生,男配唐三十六,女主徐有容
  • 《择天记》是单女主的小说,男主女主在整部小说中可能都很专情。
  • 《择天记》的作者大概率是一个很中二的人
  • 《择天记》中的故事很有可能不是关于家族之间的纷争
  • 《择天记》中的反派BOSS可能叫做魔君

04.03 《择天记》与自然语言处理

统计人物出场次数显然是Python中最最基础的操作,那我们现在来使用更高级的算法来尝试分析《择天记》这本小说。

我们使用gensim库对《择天记》进行 Word2Vec 的操作,这种操作可以将小说中的词映射到向量空间中,从而分析出不同词汇之间的关系。另外值得一提的是,由于中文和英文不同,中文词语之间没有空格,所以我们需要使用Python第三方库结巴分词对文本进行分词。我们为了提高分词的准确性,我们需要将小说中一些专属名词添加到词库中。

中文分词


In [8]:
import jieba

# 读取门派、境界、招式等名词
with open('novelitems.txt') as f:
    items = [item.strip() for item in f.readlines()]

for i in items[10:20]:
    print(i)


人族
魔族
秀灵族
犍兽
土狲
黄金巨龙
玄霜巨龙
神圣巨龙
妖族
中土大陆

我们需要将这些名词添加到结巴分词的词库中。


In [10]:
for name in names:
    jieba.add_word(name)

for item in items:
    jieba.add_word(item)


Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.846 seconds.
Prefix dict has been built succesfully.

我们现在就可以开始使用机器学习来训练模型了。


In [33]:
novel_sentences = []

# 对小说进行分词,这里只是任选了一句
# for line in cont:
for line in cont[:6]:
    words = list(jieba.cut(line))   
    novel_sentences.append(words)

novel_sentences[4]


Out[33]:
['“', '那', '少年', '是', '个', '什么样', '的', '人', '?', '”']

训练模型


In [ ]:
# 按照默认参数进行训练

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

# 把训练好的模型存下来

model.save("zetianjied.model")

# 训练模型需要大概20分钟左右的时间,因性能而异。由于模型太大了就不放在github上面了。

In [35]:
import gensim
# 读取模型

model = gensim.models.Word2Vec.load("zetianjied.model")


c:\users\administrator\appdata\local\programs\python\python36-32\lib\site-packages\gensim\utils.py:860: UserWarning: detected Windows; aliasing chunkize to chunkize_serial
  warnings.warn("detected Windows; aliasing chunkize to chunkize_serial")
Slow version of gensim.models.doc2vec is being used

寻找境界体系

首先,让我们看看《择天记》中实力境界的划分。作者在一开始告诉我们有一种境界叫做坐照境。那么,我们就通过上文中用Word2Vec 训练出来的模型找到与坐照类似的词汇。


In [36]:
# 寻找相似境界

for s in model.most_similar(positive=["坐照"]):
    print(s)


('聚星', 0.8852657079696655)
('通幽', 0.884159505367279)
('洗髓', 0.882982075214386)
('破境', 0.8720254302024841)
('上境', 0.85828697681427)
('巅峰', 0.8580290079116821)
('自观', 0.8551108241081238)
('境', 0.8437061905860901)
('突破', 0.8296098709106445)
('星域', 0.8197416067123413)

择天记中的大人物

找到择天记中和反派魔君实力水平相似的人物


In [37]:
for s in model.most_similar(positive=["魔君"])[:7]:
    print(s)


('商行舟', 0.846238374710083)
('天海圣后', 0.8413384556770325)
('黑袍', 0.8392456769943237)
('别样红', 0.8350812792778015)
('苏离', 0.798599123954773)
('朱洛', 0.7769572138786316)
('白帝', 0.774543285369873)

我们可以看到结果中出现了与魔君实力水平相似的前七个人物,这些人物可以与反派BOSS相提并论,肯定是站在《择天记》实力巅峰的大人物。事实上这些人物在原著中都是从圣境。

择天记中的情侣

训练出来的模型还可以找到具有相似联系的词汇,比如给定情侣关系的两个人物,模型会找到小说中的情侣关系。

我们先来测试一下模型,因为我们知道别样红和无穷碧是在小说中直接描写的情侣,所以我们给定陈长生和徐有容之间的关系,看看程序能否找出和无穷碧有情侣关系的人物。


In [41]:
d = model.most_similar(positive=['无穷碧', '陈长生'], negative=['徐有容'])[0]
d


Out[41]:
('别样红', 0.6611273884773254)

我们随便找一个人物,比如:折袖。运行程序,看一看机器眼中折袖与谁是情侣?


In [46]:
d = model.most_similar(positive=['折袖', '无穷碧'], negative=['别样红'])[0]
d


Out[46]:
('七间', 0.7867282032966614)

因为之前看过一点《择天记》,并且看到了折袖和七间登场。可是明明折袖和七间都是男性角色,所以可能程序出错了?但我百度了一下,才知道原来七间是女伴男装,最后真的和折袖在一起了!!!妹的,竟然被机器给剧透了

第二部分结论

  1. 择天记的境界:聚星、通幽、洗髓、破境....
  2. 择天记中的大人物:商行舟、天海圣后、黑袍、别样红...
  3. 择天记中的情侣:陈长生&徐有容,折袖&七间,商行舟&寅行道

05 写在最后

上面的代码虽然使用了自然语言处理和机器学习,但实际上只要你掌握了Python基础就可以轻松玩转了。如果你也有喜欢的小说,你也可以训练一个模型,来探索隐藏在小说中的秘密。