微信公眾號:CuteHand

關注可了解更多的金融與Python乾貨。若CuteHand對你有幫助,請點贊Thanks?(?ω?)?

背景2018年10月30日下午,金庸在香港養和醫院逝世,享年94歲。「一部百年武俠小說史,自還珠樓主以下,名家輩出,惟金庸名頭最盛、享譽最長,橫掃華人世界。他以汪洋恣肆的想像力,十餘年間寫下15部作品」。可用"飛雪連天射白鹿,笑書神俠倚碧鴛"來形容,分別是《飛狐外傳》(1960年)、《雪山飛狐》(1959年)、《連城訣》(1963年)、《天龍八部》(1963年)、《射鵰英雄傳》(1957年)、《白馬嘯西風》(1961年)、《鹿鼎記》(1969年)、《笑傲江湖》(1967年)、《書劍恩仇錄》(1955年)、《神鵰俠侶》(1959年)、《俠客行》(1965年)、《倚天屠龍記》(1961年)、《碧血劍》(1956年)《鴛鴦刀》(1961年)、《越女劍》(短篇小說)(1970年)。

為了緬懷金大俠,我們使用Python對其15部小說展開分析,通過文本挖掘,為大家展示別樣的江湖恩怨情仇。

數據獲取

編寫簡單的爬蟲程序獲取金庸15本小說,並寫入本地txt文件中。爬蟲函數不在此展示,需要源碼的請在公眾號CuteHand後台回復「金庸小說爬蟲源碼」;如果懶得動手運行爬蟲程序的,回復「金庸小說下載」,免費提供txt下載。

文本處理

分別將小說的人物(names)、功夫(kungfu)、派別(bangs)寫入txt文件中,並與小說放在同一個文件夾中。

file=D:/CuteHand/jr_novels/names.txt
#本地文件夾,根據需要修改
#可以使用os模塊的添加路徑
with open(file) as f:
# 去掉結尾的換行符
data = [line.strip() for line in
f.readlines()]
novels = data[::2]
names = data[1::2]

novel_names = {k: v.split() for k, v
in zip(novels, names)}

金庸小說充滿恩怨情仇,其中,《倚天屠龍記》中張無忌一生遇到很多女人,如趙敏、周芷若、小昭、蛛兒,朱九真,楊不悔等,到底誰是女主角呢?我們來看下這幾位美女在小說中分別出現的次數。

file=D:/CuteHand/jr_novels/倚天屠龍記.txt
with open(file) as f:
data = f.read()

Actress=[趙敏,周芷若,小昭,蛛兒,
朱九真,楊不悔]
for name in Actress:
print("%s"% name,data.count(name))

趙敏 1240
周芷若 819
小昭 352
蛛兒 231
朱九真 141
楊不悔 190

將這幾位美女在小說中出現的次數進行可視化,可以更直觀地看出哪位才是張無忌的歸屬:

#可視化,重點在於學習使用matplotlib庫畫圖
#導入需要的包
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
%matplotlib inline
#畫圖正常顯示中文
from pylab import mpl
mpl.rcParams[font.sans-serif] = [SimHei]
# 用來正常顯示中文標籤
mpl.rcParams[axes.unicode_minus]=False
# 用來正常顯示負號

actress_data = {趙敏:1240,周芷若: 819,
小昭: 352,蛛兒: 231,
朱九真: 141,楊不悔: 190}
for a, b in actress_data.items():
plt.text(a, b + 0.05, %.0f % b,
ha=center, va=bottom, fontsize=12)
#ha 文字指定在柱體中間,
#va指定文字位置
#fontsize指定文字體大小
# 設置X軸Y軸數據,兩者都可以是list或者tuple
x_axis = tuple(actress_data.keys())
y_axis = tuple(actress_data.values())
plt.bar(x_axis, y_axis, color=rgbyck)
# 如果不指定color,所有的柱體都會是一個顏色
#b: blue g: green r: red c: cyan
#m: magenta y: yellow k: black w: white
plt.xlabel("女角名") # 指定x軸描述信息
plt.ylabel("小說中出現次數") # 指定y軸描述信息
plt.title("誰是女主角?") # 指定圖表描述信息
plt.ylim(0, 1400) # 指定Y軸的高度
plt.show()

眾所周知,張無忌最終和趙敏在一起了,而與周芷若之間很是坎坷…;小昭挺喜歡的角色,可惜被不可抗拒的外力給分開了;蛛兒,暫且說是女方單戀吧;朱九真 只是過客,不過也算是張無忌情竇初開喜歡的一個;楊不悔只能說是玩伴。

文本挖掘

接下來,通過分析小說人物的出場次數來判斷小說的主要人物。

#繼續挖掘下倚天屠龍記裡面人物出現次數排名
namelist=[name.strip() for name in
novel_names[倚天屠龍記]]
namelist=.join(namelist)
namelist=namelist.split(、)
count = []
num=10 #統計前10名

for name in namelist:
count.append([name, data.count(name)])
count.sort(key=lambda x: x[1])
_, ax = plt.subplots()
numbers = [x[1] for x in count[-num:]]
names = [x[0] for x in count[-num:]]
ax.barh(range(num), numbers, align=center)
ax.set_title(倚天屠龍記, fontsize=14)
ax.set_yticks(range(num))
ax.set_yticklabels(names, fontsize=10)
plt.show()

網上收集了下金庸小說的功夫和門派種類,分別寫入kungfu.txt和bangs.txt中,其中武功246種,門派120個。

#加入功夫和門派數據
file=D:/CuteHand/jr_novels/
with open(file+"kungfu.txt") as f:
kungfu_names = [line.strip()
for line in f.readlines()]
with open(file+"bangs.txt") as f:
bang_names = [line.strip()
for line in f.readlines()]

#編寫文本挖掘可視化函數
#尋找小說出現最多的十大人物
def find_main_characters(novel):
file=D:/CuteHand/jr_novels/
with open(file+names.txt) as f:
df = [line.strip() for
line in f.readlines()]
novels = df[::2]
names = df[1::2]
novel_names = {k: v.split() for
k, v in zip(novels, names)}
with open(file+{}.txt.format(novel)) as f:
data = f.read()
count = []
namelist=[name.strip() for name
in novel_names[novel]]
namelist=.join(namelist)
namelist=namelist.split(、)
for name in namelist:
count.append([name, data.count(name)])
count.sort(key=lambda x: x[1])
_, ax = plt.subplots()
num=10
numbers = [x[1] for x in count[-num:]]
names = [x[0] for x in count[-num:]]
ax.barh(range(num), numbers, align=center)
ax.set_title(novel+"出現最多的十大人物",
fontsize=16)
ax.set_yticks(range(num))
ax.set_yticklabels(names, fontsize=14)

#尋找小說出現最多的十大武功
def kungfu(novel):
file=D:/CuteHand/jr_novels/
with open(file+{}.txt.format(novel)) as f:
df = f.read()
namelist=kungfu_names
count = []
num=10 #統計前10名

for name in namelist:
count.append([name, df.count(name)])
count.sort(key=lambda x: x[1])
_, ax = plt.subplots()
numbers = [x[1] for x in count[-num:]]
names = [x[0] for x in count[-num:]]
ax.barh(range(num), numbers, align=center)
ax.set_title(novel+"出現最多的十大武功",
fontsize=16)
ax.set_yticks(range(num))
ax.set_yticklabels(names, fontsize=14)

#尋找小說出現最多的十大門派
def bang(novel):
file=D:/CuteHand/jr_novels/
with open(file+{}.txt.format(novel)) as f:
df = f.read()
namelist=bang_names
count = []
num=10 #統計前10名

for name in namelist:
count.append([name, df.count(name)])
count.sort(key=lambda x: x[1])
_, ax = plt.subplots()
numbers = [x[1] for x in count[-num:]]
names = [x[0] for x in count[-num:]]
ax.barh(range(num), numbers, align=center)
ax.set_title(novel+"出現最多的十大門派",
fontsize=16)
ax.set_yticks(range(num))
ax.set_yticklabels(names, fontsize=14)

#將三個函數合成一個主函數
def main(novel):
find_main_characters(novel)
bang(novel)
kungfu(novel)
main(倚天屠龍記)

main(天龍八部)

main(神鵰俠侶)

main(笑傲江湖)

尋找人物關係

使用gensim和jieba包對文本做進一步挖掘,尋找人物之間的關係。一般要先安裝相應的包,只要在Anaconda Prompt上輸入pip install gensim和pip install jieba進行安裝即可。

import gensim
import warnings
warnings.filterwarnings(action=ignore,
category=UserWarning,module=gensim)
warnings.filterwarnings(action=ignore,
category=FutureWarning,module=gensim)
import jieba
for _, names in novel_names.items():
for name in names:
jieba.add_word(name)
file=D:/CuteHand/jr_novels/
with open(file+"kungfu.txt") as f:
kungfu_names = [line.strip()
for line in f.readlines()]
with open(file+"bangs.txt") as f:
bang_names = [line.strip()
for line in f.readlines()]

for name in kungfu_names:
jieba.add_word(name)

for name in bang_names:
jieba.add_word(name)

books = [天龍八部,鹿鼎記,神鵰俠侶,笑傲江湖,
碧血劍,倚天屠龍記,飛狐外傳,書劍恩仇錄,
俠客行,鴛鴦刀,白馬嘯西風,雪山飛狐]
sentences = []
for novel in books:
print ("處理:{}".format(novel))
with open(file+{}.txt.format(novel)) as f:
data = [line.strip()
for line in f.readlines()
if line.strip()]
for line in data:
words = list(jieba.cut(line))
sentences.append(words)

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

首先,來看下《倚天屠龍記》里張無忌與哪位女角的關係最緊密。

Actress=[趙敏,周芷若,小昭,蛛兒,
朱九真,楊不悔]
for a in Actress:
print("張無忌與%s的相關度" % a,model.
wv.similarity(張無忌,a))

結果如下:

張無忌與趙敏的相關度 0.7922112
張無忌與周芷若的相關度 0.7983359
張無忌與小昭的相關度 0.60103273
張無忌與蛛兒的相關度 0.7526051
張無忌與朱九真的相關度 0.5569755
張無忌與楊不悔的相關度 0.5574214

從文本挖掘上看,張無忌似乎與周芷若「關係」更加緊密。不過,周芷若與趙敏的相關度非常接近。

其次,運用12部小說(其中,射鵰英雄傳、越女劍和連城訣可能存在非法字元,讀不出來)交叉判斷人物之間的關係。

def find_relationship(a, b, c):
"""
返回 d
a與b的關係,跟c與d的關係一樣
"""
d, _ = model.wv.most_similar([c, b], [a])[0]
print ("給定「{}」與「{}」,「{}」和「{}」有類似的關係".
format(a, b, c, d))
find_relationship(小龍女,楊過 ,黃蓉)

輸出結果(Interesting!):

給定「小龍女」與「楊過」,「黃蓉」和「郭襄」有類似的關係

詞雲

通過對小說文本中出現頻率較高的「關鍵詞」予以視覺上的突出,形成「關鍵詞雲層」或「關鍵詞渲染」,過濾掉大量的文本信息,大家可以試著通過關鍵詞來自行串起故事的梗概和判斷人物的關係。

#引入需要的包
import jieba
import jieba.analyse
import numpy as np
import codecs
import pandas as pd
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator

#讀入《倚天屠龍記》文本內容
text=codecs.open(D:/CuteHand/jr_novels/倚天屠龍記.txt,
rb,gbk).read()

tags=jieba.analyse.extract_tags(text,topK=100,
withWeight=True)
tf=dict((a[0],a[1]) for a in tags)
#識別中文文本
wc=WordCloud(font_path=C:WindowsFontsSTZHONGS.TTF)
wc=wc.generate_from_frequencies(tf)
plt.figure(num=None,figsize=(12,10),facecolor=w,edgecolor=k)
plt.imshow(wc)
plt.axis(off)
plt.show()

生成特定形狀的詞雲

backgroud_Image = plt.imread(D:/CuteHand/jr_novels/地圖.jpg)
#可以自己找適合的圖片做背景,最後是背景白色
wc = WordCloud(
background_color=white,
# 設置背景顏色
mask=backgroud_Image,
# 設置背景圖片
font_path=C:WindowsFontsSTZHONGS.TTF,
# 若是有中文的話,這句代碼必須添加
max_words=2000, # 設置最大現實的字數
stopwords=STOPWORDS,# 設置停用詞
max_font_size=150,# 設置字體最大值
random_state=30
# 設置有多少種隨機生成狀態,即有多少種配色方案
)
wc.generate_from_frequencies(tf)

#img_colors = ImageColorGenerator(backgroud_Image)
#字體顏色為背景圖片的顏色
#wc.recolor(color_func=img_colors)
plt.figure(num=None,figsize(12,10),
facecolor=w,edgecolor=k)
plt.imshow(wc)
# 是否顯示x軸、y軸下標
plt.axis(off)
plt.show()

將上述過程包裝成函數,方便批量處理

def jr_cloud(novel,file):
import jieba
import jieba.analyse
import numpy as np
import codecs
import pandas as pd
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
text=codecs.open(file+{}.txt.format(novel),
rb,gbk).read()
tags=jieba.analyse.extract_tags(text,topK=50,withWeight=True)
tf=dict((a[0],a[1]) for a in tags)
wc=WordCloud(font_path=c:windowsfontssimsun.ttc,
background_color=white)
wc=wc.generate_from_frequencies(tf)
plt.figure(num=None,figsize=(12,10),
facecolor=w,edgecolor=k)
plt.title(novel,fontsize=18)
plt.imshow(wc)
plt.axis(off)
plt.show()

file=D:/CuteHand/jr_novels/
novels = [天龍八部,鹿鼎記,神鵰俠侶,笑傲江湖,
碧血劍,倚天屠龍記,飛狐外傳,書劍恩仇錄,
俠客行,鴛鴦刀,白馬嘯西風,雪山飛狐]
jr_cloud(novels[0],file)

#鹿鼎記詞雲
jr_cloud(novels[1],file)

#笑傲江湖詞雲
jr_cloud(novels[3],file)

人物關係網路分析

最後運用網路分析法,將小說中的人物關係用圖形展示出來。

import networkx as nx
import matplotlib.pyplot as plt
import jieba
import codecs
import jieba.posseg as pseg

names = {}
# 姓名字典
relationships = {}
# 關係字典
lineNames = []
# 每段內人物關係

# count names
jieba.load_userdict(novel_names[倚天屠龍記])
with codecs.open("D:/CuteHand/jr_novels/
倚天屠龍記.txt", "r") 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

# explore relationships
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

with codecs.open("D:/CuteHand/jr_novels/person_edge.txt",
"a+", "utf-8") as f:

for name, edges in relationships.items():
for v, w in edges.items():
if w >500:
f.write(name + " " + v + "
" + str(w) + "
")

a = []
f = open(D:/CuteHand/jr_novels/person_edge.txt,
r,encoding=utf-8)
line = f.readline()
while line:
a.append(line.split())
#保存文件是以空格分離的
line = f.readline()
f.close()

#畫圖
G = nx.Graph()
G.add_weighted_edges_from(a)
nx.draw(G,with_labels=True,font_size=9,
node_size=800,node_color=r)
plt.show()

◆◆關於CuteHand◆◆

能告訴你每天星座運勢,查天氣、附近酒店、股票行情,講笑話、小故事,聊天互動聊天,不定期分享原創經濟金融乾貨,手把手教你使用Python做金融數據分析。分享知識,點亮智慧 ,歡迎關注CuteHand,一起學習,一起進步!


往期精彩回顧

  • Python金融數據分析系列:【手把手教你】Python金融財務分析 【手把手教你】Python獲取財經數據和可視化分析

    【手把手教你】玩轉Python量化金融工具之NumPy

    【手把手教你】玩轉Python金融量化利器之Pandas

  • 經濟金融分析框架與思維:大勢觀瀾與研判邏輯經濟危機--明斯基時刻

    共克時艱,你做好準備了嗎?

推薦閱讀:

相关文章