文字挖掘,帶你看金庸筆下不一樣的恩怨情仇

達觀資料發表於2018-11-21

提起中國武俠小說,金庸先生是繞不開的名字,十餘年間以汪洋恣肆的想象力,寫下15部作品。可用"飛雪連天射白鹿,笑書神俠倚碧鴛"來形容。

這些作品分別是《飛狐外傳》(1960年)、《雪山飛狐》(1959年)、《連城訣》(1963年)、《天龍八部》(1963年)、《射鵰英雄傳》(1957年)、《白馬嘯西風》(1961年)、《鹿鼎記》(1969年)、《笑傲江湖》(1967年)、《書劍恩仇錄》(1955年)、《神鵰俠侶》(1959年)、《俠客行》(1965年)、《倚天屠龍記》(1961年)、《碧血劍》(1956年)《鴛鴦刀》(1961年)、《越女劍》(短篇小說)(1970年)。

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

資料獲取

編寫簡單的爬蟲程式獲取金庸15本小說,並寫入本地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: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 = ['天龍八部','鹿鼎記','神鵰俠侶','笑傲江湖',          '碧血劍','倚天屠龍記','飛狐外傳','書劍恩仇錄',         '俠客行','鴛鴦刀','白馬嘯西風','雪山飛狐'] 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金融量化分析原始碼、經濟分析框架和金融思維,手把手教你使用Python做金融資料分析。公眾號:Python 金融量化


相關文章