提起中國武俠小說,金庸先生是繞不開的名字,十余年間以汪洋恣肆的想象力,寫下15部作品??捎?#8221;飛雪連天射白鹿,笑書神俠倚碧鴛”來形容。
本文使用Python對其15部小說展開分析,通過文本挖掘,為大家展示別樣的江湖恩怨情仇。
?
編寫簡單的爬蟲程序獲取金庸15本小說,并寫入本地txt文件中。爬蟲函數(shù)不在此展示。
分別將小說的人物(names)、功夫(kungfu)、派別(bangs)寫入txt文件中,并與小說放在同一個文件夾中。
file='D:/CuteHand/jr_novels/names.txt'???
#本地文件夾,根據(jù)需要修改
#可以使用os模塊的添加路徑
with?open(file)?as?f:
????#?去掉結(jié)尾的換行符
????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)}
金庸小說充滿恩怨情仇,其中,《倚天屠龍記》中張無忌一生遇到很多女人,如趙敏、周芷若、小昭、蛛兒、朱九真、楊不悔等,到底誰是女主角呢?我們來看下這幾位美女在小說中分別出現(xiàn)的次數(shù)。
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
將這幾位美女在小說中出現(xiàn)的次數(shù)進行可視化,可以更直觀地看出哪位才是張無忌的歸屬:
#可視化,重點在于學(xué)習(xí)使用matplotlib庫畫圖
#導(dǎo)入需要的包??
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指定文字體大小
#?設(shè)置X軸Y軸數(shù)據(jù),兩者都可以是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("小說中出現(xiàn)次數(shù)")??#?指定y軸描述信息
plt.title("誰是女主角?")??#?指定圖表描述信息
plt.ylim(0,?1400)??#?指定Y軸的高度
plt.show()
眾所周知,張無忌最終和趙敏在一起了,而與周芷若之間很是坎坷…;小昭挺喜歡的角色,可惜被不可抗拒的外力給分開了;蛛兒,暫且說是女方單戀吧;朱九真只是過客,不過也算是張無忌情竇初開喜歡的一個;楊不悔只能說是玩伴。
接下來,通過分析小說人物的出場次數(shù)來判斷小說的主要人物。
#繼續(xù)挖掘下倚天屠龍記里面人物出現(xiàn)次數(shù)排名
namelist=[name.strip()?for?name?in?
? ? ? ? ?novel_names['倚天屠龍記']]
namelist=''.join(namelist)
namelist=namelist.split('、')
count?=?[]
num=10?#統(tǒng)計前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()
網(wǎng)上收集了下金庸小說的功夫和門派種類,分別寫入kungfu.txt和bangs.txt中,其中武功246種,門派120個。
#加入功夫和門派數(shù)據(jù)
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()]
#編寫文本挖掘可視化函數(shù)
#尋找小說出現(xiàn)最多的十大人物
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+"出現(xiàn)最多的十大人物",
???? ? ? ? ?fontsize=16)
????ax.set_yticks(range(num))
????ax.set_yticklabels(names,?fontsize=14)
#尋找小說出現(xiàn)最多的十大武功
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?#統(tǒng)計前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+"出現(xiàn)最多的十大武功",?
???? ? ? ? fontsize=16)
????ax.set_yticks(range(num))
????ax.set_yticklabels(names,?fontsize=14)
#尋找小說出現(xiàn)最多的十大門派
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?#統(tǒng)計前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+"出現(xiàn)最多的十大門派",?
???? ? ? ? ?fontsize=16)
????ax.set_yticks(range(num))
????ax.set_yticklabels(names,?fontsize=14)
#將三個函數(shù)合成一個主函數(shù)
def?main(novel):
????find_main_characters(novel)
????bang(novel)
????kungfu(novel)
main('倚天屠龍記')
main('天龍八部')
main('神雕俠侶')
main('笑傲江湖')
使用gensim和jieba包對文本做進一步挖掘,尋找人物之間的關(guān)系。一般要先安裝相應(yīng)的包,只要在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?=?['天龍八部','鹿鼎記','神雕俠侶','笑傲江湖',
?????'碧血劍','倚天屠龍記','飛狐外傳','書劍恩仇錄',
?????'俠客行','鴛鴦刀','白馬嘯西風(fēng)','雪山飛狐']
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)
首先,來看下《倚天屠龍記》里張無忌與哪位女角的關(guān)系最緊密。
Actress=['趙敏','周芷若','小昭','蛛兒',
? ? ? ? '朱九真','楊不悔']
for?a?in?Actress:
????print("張無忌與%s的相關(guān)度"?%?a,model.
???? ? ? ?wv.similarity('張無忌',a))?
結(jié)果如下:
張無忌與趙敏的相關(guān)度?0.7922112
張無忌與周芷若的相關(guān)度?0.7983359
張無忌與小昭的相關(guān)度?0.60103273
張無忌與蛛兒的相關(guān)度?0.7526051
張無忌與朱九真的相關(guān)度?0.5569755
張無忌與楊不悔的相關(guān)度?0.5574214
從文本挖掘上看,張無忌似乎與周芷若“關(guān)系”更加緊密。不過,周芷若與趙敏的相關(guān)度非常接近。
其次,運用12部小說(其中,射雕英雄傳、越女劍和連城訣可能存在非法字符,讀不出來)交叉判斷人物之間的關(guān)系。
def?find_relationship(a,?b,?c):
“””
返回?d
a與b的關(guān)系,跟c與d的關(guān)系一樣
“””
d,?_?=?model.wv.most_similar([c,?b],?[a])[0]
print?(“給定“{}”與“{}”,“{}”和“{}”有類似的關(guān)系”.
format(a,?b,?c,?d))
find_relationship(‘小龍女’,‘楊過’?,‘黃蓉’)
輸出結(jié)果(Interesting!):
給定“小龍女”與“楊過”,“黃蓉”和“郭襄”有類似的關(guān)系
通過對小說文本中出現(xiàn)頻率較高的“關(guān)鍵詞”予以視覺上的突出,形成“關(guān)鍵詞云層”或“關(guān)鍵詞渲染”,過濾掉大量的文本信息,可以試著通過關(guān)鍵詞來自行串起故事的梗概和判斷人物的關(guān)系。
#引入需要的包
import?jieba
import?jieba.analyse
import?numpy?as?np
import?codecs
import?pandas?as?pd
from?wordcloud?import?WordCloud,?STOPWORDS,?ImageColorGenerator
#讀入《倚天屠龍記》文本內(nèi)容
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’,
#?設(shè)置背景顏色
mask=backgroud_Image,
#?設(shè)置背景圖片
font_path=‘C:WindowsFontsSTZHONGS.TTF’,
#?若是有中文的話,這句代碼必須添加
max_words=2000,?#?設(shè)置最大現(xiàn)實的字數(shù)
stopwords=STOPWORDS,#?設(shè)置停用詞
max_font_size=150,#?設(shè)置字體最大值
random_state=30
#?設(shè)置有多少種隨機生成狀態(tài),即有多少種配色方案
)
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()
將上述過程包裝成函數(shù),方便批量處理
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:windowsontssimsun.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?=?[‘天龍八部’,‘鹿鼎記’,‘神雕俠侶’,‘笑傲江湖’,
‘碧血劍’,‘倚天屠龍記’,‘飛狐外傳’,‘書劍恩仇錄’,
‘俠客行’,‘鴛鴦刀’,‘白馬嘯西風(fēng)’,‘雪山飛狐’]
jr_cloud(novels[0],file)
#鹿鼎記詞云
jr_cloud(novels[1],file)
#笑傲江湖詞云
jr_cloud(novels[3],file)
最后運用網(wǎng)絡(luò)分析法,將小說中的人物關(guān)系用圖形展示出來。
import?networkx?as?nx
import?matplotlib.pyplot?as?plt
import?jieba
import?codecs
import?jieba.posseg?as?pseg
names?=?{}
#?姓名字典
relationships?=?{}
#?關(guān)系字典
lineNames?=?[]
#?每段內(nèi)人物關(guān)系
#?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)
#?為當前段的環(huán)境增加一個人物
if?names.get(w.word)?is?None:
names[w.word]?=?0
relationships[w.word]?=?{}
names[w.word]?+=?1
#?該人物出現(xiàn)次數(shù)加?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:
#?若兩人尚未同時出現(xiàn)則新建項
relationships[name1][name2]=?1
else:
relationships[name1][name2]?=
relationships[name1][name2]+?1
#?兩人共同出現(xiàn)次數(shù)加?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()
?
最終呈現(xiàn):
?