1. K8凯发(中国)

      K8凯发(中国)动态

      K8凯发(中国)愿与业内同行分享 助力各企业在大数据浪潮来临之际一起破浪前行

      文本挖掘,带你看金庸笔下不一样的恩怨情仇

      提起中国武侠小说,金庸先生是绕不开的名字,十余年间以汪洋恣肆的想象力,写下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(01400)  # 指定Y轴的高度
      plt.show()
      

      1

      众所周知,张无忌最终和赵敏在一起了,而与周芷若之间很是坎坷…;小昭挺喜欢的角色,可惜被不可抗拒的外力给分开了;蛛儿,暂且说是女方单恋吧;朱九真只是过客,不过也算是张无忌情窦初开喜欢的一个;杨不悔只能说是玩伴。

      文本挖掘

      接下来,顺利获得分析小说人物的出场次数来判断小说的主要人物。

      #继续挖掘下倚天屠龙记里面人物出现次数排名
      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[1for x in count[-num:]]
      names = [x[0for 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()
      

      2

      网上收集了下金庸小说的功夫和门派种类,分别写入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[1for x in count[-num:]]
          names = [x[0for 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[1for x in count[-num:]]
          names = [x[0for 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[1for x in count[-num:]]
          names = [x[0for 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('倚天屠龙记')
      

      3

      4

      5

      main('天龙八部')
      

      6

      7

      8

      main('神雕侠侣')
      

      9

      10

      11

      main('笑傲江湖')
      

      12

      13

      14

      寻找人物关系

      使用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()

      15

      生成特定形状的词云

      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()

      16

      将上述过程包装成函数,方便批量处理

      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)

      17

      #鹿鼎记词云
      jr_cloud(novels[1],file)

      18

      #笑傲江湖词云
      jr_cloud(novels[3],file)

      19

       

      人物关系网络分析

      最后运用网络分析法,将小说中的人物关系用图形展示出来。

      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()

       

      最终呈现:

       

      20

       

      关于作者:CuteHand,专注于分享Python金融量化分析源码、经济分析框架和金融思维,手把手教你使用Python做金融数据分析。公众号:Python 金融量化