上周末闲来无事,一时兴起,想着金庸老爷子的武侠世界那么宏大,加上最近无聊看了《Python自然语言处理》这本书,作为金庸迷,为何不做个人物关系知识图谱看看效果如何?

一、数学模型构建

利用Gephi构建知识图谱,无外乎两点:节点信息和边界信息。节点数据还是很好处理的,将金庸武侠世界的所有有名有姓的人物取为节点数据即可,关键在于边界数据的提取。参考众多大神的经验,大部分是以小说章节或者段落为基本分析单元,将出现在同一分析单元的人物视为发生一次关联。个人感觉这种处理方法有其合理性,但也存在一定不足。其一是考虑到金庸小说中每一章节基本讲述的是同一个武侠场景,众多人物对话也是一段一段接替发生,即上下段落之间人物大部分情况是存在直接关系的。其二是如若将出现在同一章节的人物视为发生一次关联,无疑将放大小人物的权重,反之若将出现在同一段落的人物视为发生一次关联,同样会人为缩小主角人物边界联系的权重。基于以上两点考虑,我将同一章节中任意连续两个段落作为基本的分析单元,出现在两个连续段落中的人物即视为发生一次关联。以此为数据样本进行人物关系知识图谱的构建。

二、文本数据提取

1、数据来源

考虑到是以连续两个段落为基本分析单元来分析数据,并且金庸小说经过多次修订,因此为方便数据分析(<p>与</p>完美的将文章段落进行了分割)和保证小说数据的权威性,决定利用Python爬虫爬取金庸先生15本小说的最终版。15本小说链接如下:

2、爬取文本数据

爬虫代码:

# -*- coding: UTF-8 -*-
import urllib2
from bs4 import BeautifulSoup
import lxml
import  re
#import requests
import osclass PaChong:def  __init__(self,URL,URL_ID,URL_Base):#构造函数参数分别为小说网址链接、小说编号(1~15)、网站链接;self.URL = URLself.URL_ID = URL_IDself.URL_Base = URL_Baseself.listpath = None#打开小说章节def   OpenSeeion(self):#获取小说所有章节链接;try:respond = urllib2.urlopen(self.URL)html = respond.read()soup = BeautifulSoup(html,'lxml',from_encoding='utf-8')OringialPath = soup.find_all(class_="mlist");pattern = re.compile('(?<=href=").*?(?=")')#print str(OringialPath)self.listpath =  re.findall(pattern,str(OringialPath) )          except:print    "Error"def    gettext(self,sessionURL):#获取该章节所有段落,返回数据类型为Lisweb = self.URL_Base + sessionURLrespond = urllib2.urlopen(web)html = respond.read()pattern = re.compile('(?<=<p>).*?(?=</p>)')#print str(html)#temp = re.search(pattern,str(html))#soup = BeautifulSoup(html,'lxml',from_encoding='utf-8')  #print str(soup.find_all('p'))#text = temp.group()text = re.findall(pattern,str(html) )     return text

3、数据提取

目前自然语言处理主要针对的是诸如英语这种字母语言,针对中文的语言处理并不是很多。目前比较好的Python库可能算是jieba分词了,因此,本人利用jieba分词对小说的文本数据进行处理。

jieba分词算法使用了基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能生成词情况所构成的有向无环图(DAG), 再采用了动态规划查找最大概率路径,找出基于词频的最大切分组合,对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法。jieba分词在安装路径中有一个名为dic.txt的文件,该文件为jieba分词的字典库,主要依据该库来扫描数据。

jieba分词支持三种分词模式:

(1)  精确模式, 试图将句子最精确地切开,适合文本分析:

(2) 全模式,把句子中所有的可以成词的词语都扫描出来,速度非常快,但是不能解决歧义;

(3)  搜索引擎模式,在精确模式的基础上,对长词再词切分,提高召回率,适合用于搜索引擎分词;

jieba主要函数有如下几种:

(1)  jieba.cut:该方法三个输入参数:需要分词的字符串; cut_all 参数用来控制是否采用全模式;HMM参数用来控制是否适用HMM模型;

(2)  jieba.cut_for_search:该方法两个参数:需要分词的字符串;是否使用HMM模型,该方法适用于搜索引擎构建倒排索引的分词,粒度比较细;

(3)  jieba.cut 以及jieba.cut_for_search返回的结构都是可以得到的generator(生成器), 可以使用for循环来获取分词后得到的每一个词语或者使用;

(4)  jieb.lcut 以及 jieba.lcut_for_search 直接返回list;

(5)  jieba.Tokenizer(dictionary=DEFUALT_DICT) 新建自定义分词器,可用于同时使用不同字典,jieba.dt为默认分词器,所有全局分词相关函数都是该分词器的映射;

jieba分词可以导入指定的自定义词典:

(1)  jieba.load_userdict(filename)  # filename为自定义词典的路径

(2) jieba.add_word(newword)  # newword为自定义词

在本文中无需对所有词语进行分词统计,只需统计金庸所有武侠人物进行词频分析统计,为加快程序运行速度,因此直接通过修改jieba的字典库文件。根据网上资料,金庸武侠世界中共计有1316名人物,算上主要人物别名,共计1387个人物名称。以这些人物名称构建jieba字典库,并替换原有字典。

为方便后期数据处理,利用MySQL构建人物数据库,其中ID:主键、Names:人物名称,alias_1、alias_2、alias_3为对应别名。

构建小说名库,其中 idNovel:小说ID、NovelName:小说名。

构建人物出现频次数据库,主要统计人物在文章中出现次数,其中idJieDian:主键、UserID:人物ID、Num:频次、Novel:出现文章ID。

构建元数据数据库,该数据用于存储文本数据分析结果,作为一级数据库。其中Novel:数据来源小说,Dtat存储人物关联关系,以Json格式存储数据,针对每一分析单元,产生一个Json数组,ID表示该分析单元的人物ID、Nm表示该人物在该分析单元出现的频次。

创建完数据库,下面就开始针对文本数据进行分析处理入库。首先利用MySQLdb创建数据库操作类,主要包括数据库的连接、释放、查询、以及SQL语句的执行。

 # -*- coding: utf-8 -*-
import MySQLdb
import os
import sysclass OperateMySQL:def __init__(self,IP,username,pwd,dbname):self.cursor = Noneself.db = Noneself.IP = IP              #数据库IPself.username = username  #用户名self.pwd = pwd            #密码self.dbname = dbname      #数据库名称#连接数据库def connection(self):self.db = MySQLdb.connect(self.IP, self.username, self.pwd, self.dbname, charset='utf8')self.cursor = self.db.cursor()#释放连接def __del__(self):if(self.db!=None):self.db.close()# 执行sql语句def excute(self,sql):try:self.cursor.execute(sql)           # 提交到数据库执行self.db.commit()return 1except:# Rollback in case there is any errorself.db.rollback()return 0#查询def select(self,sql):try:# 执行SQL语句self.cursor.execute(sql)# 获取所有记录列表results = self.cursor.fetchall()return resultsexcept:print "Error: unable to fecth data"

下面就是利用jieba库对文本数据进行分析了,分析代码如下(不得不吐槽Python对中文字符的支持真是不友好)。输入分别为:人物名称、人物的三个别名、文本数据,输出为对应人物出现的频次。

# -*- coding: utf-8 -*-
import sys
stdi,stdo,stde=sys.stdin,sys.stdout,sys.stderr
reload(sys) # Python2.5 初始化后删除了 sys.setdefaultencoding 方法,我们需要重新载入
sys.stdin,sys.stdout,sys.stderr=stdi,stdo,stde
sys.setdefaultencoding('utf-8')
import os, codecs
import  jieba
from    collections import Counterclass cipin:@staticmethoddef tongji(namelist,namelist_1,namelist_2,namelist_3,text):#print len(namelist)#print len(namelist_1)#print len(namelist_2)#print len(namelist_3)#m = raw_input()#for newword in namelist:#    #print newword# jieba.add_word(newword)#for newword1 in namelist_1:#print newword1# jieba.add_word(newword1)#for newword2 in namelist_2:#   #print newword# jieba.add_word(newword2)#for newword3 in namelist_3:#print newword# jieba.add_word(newword3)result = [0] * (len(namelist))for t in range(len(result)):result[t] = 0seg_list = jieba.cut(text)c = Counter()for x in seg_list:if len(x)>1 and x != '\r\n':c[x] += 1#print xfor (key,value) in c.items():#print('%s%s %s  %d' % ('  '*(5-len(key)), key, '*'*int(value/3), value))k = "%s" % (key)v = int(value)#for str in namelist:#print vif k.decode('utf-8') in namelist:num = namelist.index(k.decode('utf-8'))result[num] = result[num] + v#print namelist[num - 1] elif k.decode('utf-8') in namelist_1:num = namelist_1.index(k.decode('utf-8'))result[num] = result[num] + velif k.decode('utf-8') in namelist_2:num = namelist_2.index(k.decode('utf-8'))result[num] = result[num] + velif k.decode('utf-8') in namelist_3:num = namelist_3.index(k.decode('utf-8'))result[num] = result[num] + velse:continuereturn result

下面出场的就是主函数了:

# encoding:utf-8
import OperateMySQL
import sys
import PaChong
import cipin
import jsondef getname(namelist,nameliet_1,nameliet_2,nameliet_3):url = "localhost"username = "root"pwd = "1qaz@wsx3edc"db = "jingyong"cu = OperateMySQL.OperateMySQL(url,username,pwd,db)cu.connection()sql = "SELECT * FROM jingyong.`character` "re = cu.select(sql)for row in re:nameID = row[0]namelist.append(row[1])if len(str(row[2]))!=0:nameliet_1.append(row[2])if len(str(row[3]))!=0:nameliet_2.append(row[3])if len(str(row[4]))!=0:nameliet_3.append(row[4])del cu# 打印结果#i = 0#for strs in namelist:#print str(i) + " " +  strs + " " + nameliet_1[i] + " " + nameliet_2[i] + " " +nameliet_3[i]#i = i+1def main():url = "localhost"username = "root"pwd = "1qaz@wsx3edc"db = "jingyong"cu2 = OperateMySQL.OperateMySQL(url,username,pwd,db)cu2.connection()namelist = []nameliet_1 = []nameliet_2 = []nameliet_3 = []getname(namelist,nameliet_1,nameliet_2,nameliet_3)URL = []URL.append("http://www.jinyongwang.com/fei/")#飞狐外传URL.append("http://www.jinyongwang.com/xue/")#雪山飞狐URL.append("http://www.jinyongwang.com/lian/")#连城诀URL.append("http://www.jinyongwang.com/tian/")#天龙八部URL.append("http://www.jinyongwang.com/she/")#射雕英雄传URL.append("http://www.jinyongwang.com/bai/")#白马啸西风URL.append("http://www.jinyongwang.com/lu/")#鹿鼎记URL.append("http://www.jinyongwang.com/xiao/")#笑傲江湖   URL.append("http://www.jinyongwang.com/shu/")#书剑恩仇录URL.append("http://www.jinyongwang.com/shen/")#神雕侠侣URL.append("http://www.jinyongwang.com/xia/")#侠客行URL.append("http://www.jinyongwang.com/yi/")#倚天屠龙记URL.append("http://www.jinyongwang.com/bi/")#碧血剑URL.append("http://www.jinyongwang.com/yuan/")#鸳鸯刀URL.append("http://www.jinyongwang.com/yue/")#越女剑URL_Base = "http://www.jinyongwang.com"for i in range(len(URL)):cu = PaChong.PaChong(URL[i],i+1,URL_Base)cu.OpenSeeion()for j in range(len(cu.listpath)):temp = cu.gettext(cu.listpath[j])print (cu.listpath[j])for text in temp:#print textresultJieDian = cipin.cipin.tongji(namelist,nameliet_1,nameliet_2,nameliet_3,text)li=list(set(resultJieDian))if len(li) == 1:continuedata = []for t in range(len(resultJieDian)):if resultJieDian[t]!= 0:dic = {}dic["ID"] = tdic["Nm"] = resultJieDian[t]data.append(dic)in_json = json.dumps(data)sql = "INSERT INTO jingyong.yuanshidata SET Novel = " + str(i + 1) + ", Dtat = '" + str(in_json) + "'"#print sqlre = cu2.excute(sql)if re == 0:print "Error " + cu.listpath[j]m = raw_input()del sqldel resultJieDiandel lidel cuif __name__ == "__main__":  main()

至此,人物通联关系的数据提取以完成,顺便还想分析下金庸小说词频,按照如上套路,对金庸小说的词频顺带进行了统计。

通过上述操作,共获取元数据65418条。

三、利用Gephi构建人物关系

Gephi是一款开源免费跨平台基于JVM的复杂网络分析软件,,其主要用于各种网络和复杂系统,动态和分层图的交互可视化与探测开源工具。

首先自然是基于元数据创建二级库。创建边界数据表,其中UserID_1与UserID_2表示通联人物ID,Num表示通联次数,novel表示数据来源文章。

根据元数据库,出现在同一分析单元的人物两两存在一次关联,二级库提取代码如下:

# encoding:utf-8
import OperateMySQL
import sys
import PaChong
import cipin
import jsondef getname(namelist,nameliet_1,nameliet_2,nameliet_3):url = "localhost"username = "root"pwd = "1qaz@wsx3edc"db = "jingyong"cu = OperateMySQL.OperateMySQL(url,username,pwd,db)cu.connection()sql = "SELECT * FROM jingyong.`character` "re = cu.select(sql)for row in re:nameID = row[0]namelist.append(row[1])nameliet_1.append(row[2])nameliet_2.append(row[3])nameliet_3.append(row[4])del cudef main():url = "localhost"username = "root"pwd = "1qaz@wsx3edc"db = "jingyong"cu2 = OperateMySQL.OperateMySQL(url,username,pwd,db)cu2.connection()sql = "SELECT count(idYuanShiData) FROM jingyong.yuanshidata " re = cu2.select(sql)for row in re:num = int(row[0])loop = int(num/1000) + 1for temp in range(loop):sql2 = "SELECT * FROM jingyong.yuanshidata "  + " limit " + str(temp*1000) + ",1000"print sql2re2 = cu2.select(sql2)for row2 in re2:IDs = int(row2[0])novel = int(row2[1])data = str(row2[2])sValue = json.loads(data)for med in sValue:sql3 = "SELECT count(idJieDian) FROM jingyong.jiedian where UserID = " + str(med["ID"]) + " and Novel = " + str(novel)#print sql3re3 = cu2.select(sql3)for row3 in re3:num3 = int(row3[0])if num3 == 0:sql4 = "INSERT INTO jingyong.jiedian SET UserID = " + str(med["ID"]) + ", Novel = " + str(novel) + ",Num = " + str(med["Nm"])re4 = cu2.excute(sql4)if re4 == 0:print  " insert error " + sql4m = raw_input()elif num3 == 1:sql4 = "SELECT Num FROM jingyong.jiedian where UserID = " + str(med["ID"]) + " and Novel = " + str(novel)re4 = cu2.select(sql4)for row4 in re4:num4 = int(row4[0])count = num4 + med["Nm"]sql5 = "UPDATE jingyong.jiedian SET Num = " + str(count) + " where UserID = " + str(med["ID"]) + " and Novel = " + str(novel)re5 = cu2.excute(sql5)if re5 == 0:print " insert error  " + sql5m = raw_input()else:print " count error " + sql4m = raw_input()    if __name__ == "__main__":  main()

以鹿鼎记为例,首先 根据人物关联二级库,挑选出鹿鼎记中人物关联数据,并导出为CSV文件,编码格式为UTF-8,注意要在文件第一行加上列名。其中前两列为两关联节点名,第三列为权重。

在Gephi数据区,选择边-导入数据表格

选择无向图

导入边数据后,会自动生成节点数据,但是节点数据lable为空,需手动复制过去。

选择外观中节点、边的着色方式,选择一类布局方式,分别点击应用和运行。右侧过滤和统计可以进行数据过滤和统计。

调整标签名称字体

选择预览区,待概览去渲染数据完成后,点击刷新。

右下角可选择数据导出。

四、结果

按照以上思路,基本完成了金庸武侠人物关系知识库。

利用Python+Gephi构建金庸人物知识图谱相关推荐

  1. 用Hadoop分析金庸人物关系网-实验报告

    用Hadoop分析金庸人物关系网 --- 用大数据粗略的分析金庸人物关系网 整体结果报告 达到预期目标并完成了选做内容 实验目标描述: 金庸的江湖 课程设计目标 通过一个综合数据分析案例:" ...

  2. 不错的金庸人物考考你android游戏源码

    这是刚刚在源码天堂上看到的一款不错的金庸人物考考你android游戏源码,分享给大家学习一下吧. 1.有关金庸原著小说的问答题.2.题目多为金庸qq群的比赛题.较难,目测能通关者不会很多.3.&quo ...

  3. python 数据分析学什么-利用Python做数据分析 需要学习哪些知识

    根据调查结果,十大最常用的数据工具中有八个来自或利用Python.Python广泛应用于所有数据科学领域,包括数据分析.机器学习.深度学习和数据可视化.不过你知道如何利用Python做数据分析吗?需要 ...

  4. 知识图谱实战1:构建红楼梦人物知识图谱

    一.安装Neo4j 在Windows环境中安装Neo4j 并创建两个节点和他们之间的关系 这个是先安装neo4j desktop 然后访问 brower 注意 : neo4j和jdk的版本要对应 ne ...

  5. 利用Python爬取金十数据新闻事件

    刚入门爬虫,由于女朋友的工作需要,便写了这个爬虫程序.本人也是第一次发帖, 相信爬虫也没什么好介绍的.直接上代码. from selenium import webdriver from bs4 im ...

  6. 苍蝇的下场——金庸人物之招式应对

    最残忍的打法--梅超风:抓住苍蝇以后,把脑袋揪下来,在上面按五个洞. 最损失的打法--一灯:几记一阳指过后,苍蝇倒是被烧焦了,可是屋里的彩电.床单.墙皮.地板无一幸免...... 最仿生的打法--欧阳 ...

  7. 利用Python爬取百度指数中需求图谱的关键词

    文章目录 需求背景 0.获取cookies 一.使用datetime计算查询的日期 二.爬取需求图谱关键词 三.扔进csv里 总结 已更新!!! 之前有小伙伴在评论里反应代码有点问题,今天看了下,报错 ...

  8. 《知识图谱》赵军 学习笔记

    知识图谱读书笔记 文章目录 知识图谱读书笔记 一. 概述 1.1 什么是知识图谱 1.2 知识图谱发展历程 1.3 知识图谱类型 1.4 知识图谱生命周期 知识体系构建 知识获取 知识融合 知识存储 ...

  9. 为知识的海洋绘制航图 —— 利用CirroData-Graph图数据库构建知识图谱

    人类对世界的认知日益广博.精深,如何将浩如烟海的知识有效组织和整理,方便高效存储和查询是一个重要问题.知识图谱(Knowledge Graph)的概念应运而生,简单的说知识图谱就是一个具有有向图结构的 ...

  10. 知识图谱基础知识(一): 概念和构建

    推荐: 知识图谱构建技术一览 知识图谱基础知识之三--知识图谱的构建过程 目录 一.什么是知识图谱 二.知识图谱的分层架构 三.知识图谱构架技术 (一)数据获取(Data Acquisition) ( ...

最新文章

  1. 529. Minesweeper
  2. HTML5 高级系列:web Storage 学前端开发要先看这个
  3. java接口废弃注释_Java注释Override、Deprecated、SuppressWarnings详解
  4. C/C++开发进阶的学习路线
  5. stm32 USART rs485 rs232
  6. 菜比如我的漫漫react学习路(二)
  7. 【SEW-伺服电机-Profinet-io通讯控制——伺服配置】
  8. logback 自定义PatternLayout
  9. 【django】django面试题总结
  10. html表格行的悬停事件,jQuery实现HTML表格隔行变色及鼠标悬停变色效果
  11. 那些值得吟唱的诗词歌赋
  12. C语言文件重定向---“系统找不到指定的文件”
  13. 计算机开机按f1,电脑开机按F1的原因及解决方法
  14. 【二】分布式训练---参数服务器训练(飞桨paddle1.8)
  15. LATEX公式下标短横线过长
  16. 二维码登录原理+Web端即时通讯技术
  17. 有关PHP文档生成工具---PHPDocumentor
  18. Flowable 6.6.0 BPMN用户指南 - (5)Spring Boot - 5.8 Flowable应用属性
  19. 最新 ICCV | 35个GAN应用主题梳理,最全GAN生成对抗论文汇总
  20. 主数据建设的挑战与发展

热门文章

  1. Ubuntu下locale命令路径无法找到问题解决方法:Cannot set LC_CTYPE to default locale: No such file or directory
  2. C++计算四则表达式的模板
  3. Lack of free swap space on
  4. 实现12306全自动下单功能(Python+PyCharm附:主要代码)
  5. sklearn中精确率、召回率及F1值得micro,macro及weighted算法
  6. android studio创建桌面插件widget
  7. 百钱买百鸡,公鸡五元一只,母鸡三元一只,小鸡一元三只
  8. app提现至微信(微信企业付款到个人微信号)
  9. postgresql的下载与安装
  10. 精密光学测量1-概论