前面作者讲解了很多知识图谱原理知识,包括知识图谱相关技术、Neo4j绘制关系图谱等,但仍缺少一个系统全面的实例。为了加深自己对知识图谱构建的认识,为后续创建贵州旅游知识图谱打下基础,作者深入学习了张宏伦老师的网易云课程(星球系列电影),并结合自己的理解和技术分享了该系列专栏,从数据采集、数据展示、数据分析到知识图谱构建,文章后续还会讲解中文数据的实体识别、关系抽取、知识计算等。
前文介绍了Python3抓取电影实体知识,Seaborn可视化展示电影信息,本文着重构建知识图谱的布局,包括三部分内容:一是Python提取节点和关联两类数据,二是HTML和CSS进行网页布局,三是调用D3显示提取的JSON数据。
这是一系列基础性文章,希望对您有所帮助 ,尤其是对知识图谱感兴趣和编程刚入门的同学。同时也因为最近准备博士考试,做题做吐了,写点新东西调节下心情,与君共勉,一起加油。
前文:
[知识图谱实战篇] 一.数据抓取之Python3抓取JSON格式的电影实体
[知识图谱实战篇] 二.Json+Seaborn可视化展示电影实体
推荐作者的知识图谱前文:
知识图谱相关会议之观后感分享与学习总结
中文知识图谱研讨会的学习总结 (上) 图谱引入、百度知心、搜狗知立方
搜索引擎和知识图谱那些事 (上).基础篇
基于VSM的命名实体识别、歧义消解和指代消解
CSDN下载-第一届全国中文知识图谱研讨会演讲PPT 清华大学
CSDN下载-知识图谱PDF资料 清华大学知识图谱研讨会汇报PPT
[知识图谱构建] 一.Neo4j图数据库安装初识及药材供应图谱实例
[知识图谱构建] 二.《Neo4j基础入门》基础学习之创建图数据库节点及关系
[关系图谱] 一.Gephi通过共线矩阵构建知网作者关系图谱
[关系图谱] 二.Gephi导入共线矩阵构建作者关系图谱
再次强烈推荐大家阅读张宏伦老师的网易云课程及Github源码,受益匪浅。
https://github.com/Honlan/starwar-visualization/tree/master/star_war
https://study.163.com/course/courseLearn.htm?courseId=1003528010
前文讲述了Python3代码爬取星球大战的信息,实体包括filmes电影、characters人物、planets星球、starships飞船、vehicles装备、species种族。如下所示:
某一部电影中JSON数据如下图所示,它详细讲述了该部电影中出现了哪些人物、星球、飞船、装备、种族。同样,characters文件中也详细讲解每个人物与其他实体的关系。
可视化展示通报包括两种方法:
1.写入数据库,后台获取数据库中数据并展示,推荐Neo4j图数据库。
2.将数据写入Json格式文件,然后前端用Ajax请求数据展示。
这里讲采用第二种方法,接下来讲解通过 Python 提取节点和关联数据的过程。主要包括两个步骤:
https://swapi.co/api/films/4/
{
'planets': ['https://swapi.co/api/planets/8/',
'https://swapi.co/api/planets/9/',
'https://swapi.co/api/planets/1/'],
'url': 'https://swapi.co/api/films/4/',
'director': 'George Lucas',
'opening_crawl': 'Turmoil has engulfed the\r\nGalactic ....',
'species': ['https://swapi.co/api/species/1/', 'https://swapi.co/api/species/2/', ...],
'characters': ['https://swapi.co/api/people/2/', 'https://swapi.co/api/people/3/', ...],
'vehicles': ['https://swapi.co/api/vehicles/33/', 'https://swapi.co/api/vehicles/34/', ...],
'release_date': '1999-05-19',
'title': 'The Phantom Menace'
}
get_alldata_json
# coding:utf8
import time
import json
import pprint
##################################################
# 第一部分 读取数据
##################################################
#字典定义六类实体
films = {}
characters = {}
planets = {}
starships = {}
vehicles = {}
species = {}
#读取电影链接
fr = open('films.csv', 'r')
for line in fr:
tmp = json.loads(line.strip('\n')) #转换为字典
films[tmp['url']] = tmp
print(tmp['url'], tmp)
fr.close()
#读取人物数据
fr = open('film_characters.csv', 'r')
for line in fr:
tmp = json.loads(line.strip('\n'))
characters[tmp['url']] = tmp
fr.close()
#读取星球数据
fr = open('film_planets.csv', 'r')
for line in fr:
tmp = json.loads(line.strip('\n'))
planets[tmp['url']] = tmp
fr.close()
#读取飞船数据
fr = open('film_starships.csv', 'r')
for line in fr:
tmp = json.loads(line.strip('\n'))
starships[tmp['url']] = tmp
fr.close()
#读取装备数据
fr = open('film_vehicles.csv', 'r')
for line in fr:
tmp = json.loads(line.strip('\n'))
vehicles[tmp['url']] = tmp
fr.close()
#读取物种数据
fr = open('film_species.csv', 'r')
for line in fr:
tmp = json.loads(line.strip('\n'))
species[tmp['url']] = tmp
fr.close()
##################################################
# 第二部分 获取节点和边
##################################################
#定义节点和边
nodes = []
links = []
#nodes实体包括id(电影名称)、class、group、size
#links为电影与人物、星球、种族、装备、飞船的边
for key, value in films.items():
nodes.append({'id': value['title'], 'class': 'film', 'group': 0, 'size': 20})
#遍历value['characters'] 如果存在则建立一条电影到人物的边, 边的权重设置为3
#characters 人物
for item in value['characters']:
#python2的has_key在python3中删除
#if dict.has_key(word) => if word in dict
if item in characters:
links.append({'source': value['title'], 'target': characters[item]['name'], 'value': 3})
#planets 星球
for item in value['planets']:
if item in planets:
links.append({'source': value['title'], 'target': planets[item]['name'], 'value': 3})
#species 种族
for item in value['species']:
if item in species:
links.append({'source': value['title'], 'target': species[item]['name'], 'value': 3})
# starships
for item in value['starships']:
if item in starships:
links.append({'source': value['title'], 'target': starships[item]['name'], 'value': 3})
# vehicles
for item in value['vehicles']:
if item in vehicles:
links.append({'source': value['title'], 'target': vehicles[item]['name'], 'value': 3})
#建立人物的节点和关联
for key, value in characters.items():
nodes.append({'id': value['name'], 'class': 'character', 'group': 1, 'size': 5})
# films
for item in value['films']:
if item in films:
links.append({'source': value['name'], 'target': films[item]['title'], 'value': 3})
# planets
if value['homeworld'] in planets:
links.append({'source': value['name'], 'target': planets[value['homeworld']]['name'], 'value': 3})
# species
for item in value['species']:
if item in species:
links.append({'source': value['name'], 'target': species[item]['name'], 'value': 3})
# starships
for item in value['starships']:
if item in starships:
links.append({'source': value['name'], 'target': starships[item]['name'], 'value': 3})
# vehicles
for item in value['vehicles']:
if item in vehicles:
links.append({'source': value['name'], 'target': vehicles[item]['name'], 'value': 3})
for key, value in planets.items():
nodes.append({'id': value['name'], 'class': 'planet', 'group': 2, 'size': 16})
# films
for item in value['films']:
if item in films:
links.append({'source': value['name'], 'target': films[item]['title'], 'value': 3})
# characters
for item in value['residents']:
if item in characters:
links.append({'source': value['name'], 'target': characters[item]['name'], 'value': 3})
for key, value in starships.items():
nodes.append({'id': value['name'], 'class': 'starship', 'group': 3, 'size': 8})
# films
for item in value['films']:
if item in films:
links.append({'source': value['name'], 'target': films[item]['title'], 'value': 3})
# characters
for item in value['pilots']:
if item in characters:
links.append({'source': value['name'], 'target': characters[item]['name'], 'value': 3})
for key, value in vehicles.items():
nodes.append({'id': value['name'], 'class': 'vehicle', 'group': 4, 'size': 8})
# films
for item in value['films']:
if item in films:
links.append({'source': value['name'], 'target': films[item]['title'], 'value': 3})
# characters
for item in value['pilots']:
if item in characters:
links.append({'source': value['name'], 'target': characters[item]['name'], 'value': 3})
for key, value in species.items():
nodes.append({'id': value['name'], 'class': 'species', 'group': 5, 'size': 14})
# planets
if value['homeworld'] in planets:
links.append({'source': value['name'], 'target': planets[value['homeworld']]['name'], 'value': 3})
# films
for item in value['films']:
if item in films:
links.append({'source': value['name'], 'target': films[item]['title'], 'value': 3})
# characters
for item in value['people']:
if item in characters:
links.append({'source': value['name'], 'target': characters[item]['name'], 'value': 3})
fw = open('starwar_alldata.json', 'w')
fw.write(json.dumps({'nodes': nodes, 'links': links}))
fw.close()
输出结果为nodes和links的键值对,如下图所示,
接着讲解HTML和CSS网页布局,其最终结果如下图所示,包括标题和左下角的图例,后续的知识图谱会展示在网页中间,下部为时间线。同样,作者强烈推荐大家掌握D3获取JSON数据展示的过程,然后将其加载至更好的或开源的前端中。
1.加载JQuery样式
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js">script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js">script>
2.定义body样式:背景颜色,padding 周围留空白,上下30,左右40,设置文字居中,字体样式。
<style type="text/css">
body {
background-color: #272b30;
padding: 30px 40px;
text-align: center;
font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
}
style>
3.设置标题样式及div布局,并设置设置相对定位 position:relative。
<h1 style="color:#fff;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">Star Warsh1>
<div style="text-align: center; position:relative;">
<svg width="800" height="500" style="margin-right:80px;margin-bottom:-40px;" id="svg1">
svg>
div>
4.增加D3库,它是非常流行的可视化库。
D3是基于数据的文档操作javascript库,D3能够把数据和HTML、SVG、CSS结合起来,创造出可交互的数据图表。D3 (Data-Driven Documents)是基于数据的文档操作javascript库,D3能够把数据和HTML、SVG、CSS结合起来,创造出可交互的数据图表。
此时的完整代码如下所示:
main.html
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>知识图谱title>
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="" />
<link rel="shortcut icon" href="">
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js">script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js">script>
head>
<style type="text/css">
body {
background-color: #272b30;
padding: 30px 40px;
text-align: center;
font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
}
style>
<body>
<h1 style="color:#fff;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">Star Warsh1>
<div style="text-align: center; position:relative;">
<svg width="800" height="500" style="margin-right:80px;margin-bottom:-40px;" id="svg1">
svg>
<div id="indicator">
div>
<div id="mode">
div>
<div id="search">
div>
<div id="info">
div>
div>
<div style="text-align: center; position:relative;">
<svg width="960" height="240" style="margin-right:60px;margin-bottom:-40px;" id="svg1">
<g>g>
svg>
div>
body>
<script src="https://d3js.org/d3.v4.min.js">script>
html>
显示结果如下图所示:
接下来这部分代码是绘制图例,其中核心代码是通过JS获取SVG1布局,并加载六种颜色,对应六个图例。再设置 indicator 图例布局,动态加载颜色。
<script type="text/javascript">
$(document).ready(function() {
//定义svg变量将布局svg1选出来
var svg = d3.select("#svg1"),
width = svg.attr("width"),
height = svg.attr("height");
//定义name变量制作图标
var names = ['Films', 'Characters', 'Planets', 'Starships', 'Vehicles', 'Species'];
var colors = ['#6ca46c', '#4e88af', '#ca635f', '#d2907c', '#d6744d', '#ded295'];
//背景颜色设置 补充CSS样式设置字体布局
for (var i=0; i < names.length; i++) {
$('#indicator').append("" + names[i] + "");
}
});
script>
同时增加的indicator为绝对布局,其每个小div对应小的色块,通过span设置内联元素,才能显示其色块元素。核心代码如下:
#indicator {
position: absolute;
left: 60px;
bottom: 120px;
text-align: left;
color: #f2f2f2;
font-size: 12px;
}
#indicator>div {
margin-bottom: 4px;
}
#indicator span {
display: inline-block;
width: 30px;
height: 14px;
position: relative;
top: 2px;
margin-right: 8px;
}
此时的main.html完整代码修改如下:
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>知识图谱title>
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="" />
<link rel="shortcut icon" href="">
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js">script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js">script>
head>
<style type="text/css">
body {
background-color: #272b30;
padding: 30px 40px;
text-align: center;
font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
}
#indicator {
position: absolute;
left: 60px;
bottom: 120px;
text-align: left;
color: #f2f2f2;
font-size: 12px;
}
#indicator>div {
margin-bottom: 4px;
}
#indicator span {
display: inline-block;
width: 30px;
height: 14px;
position: relative;
top: 2px;
margin-right: 8px;
}
style>
<body>
<h1 style="color:#fff;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">Star Warsh1>
<div style="text-align: center; position:relative;">
<svg width="800" height="500" style="margin-right:80px;margin-bottom:-40px;" id="svg1">
svg>
<div id="indicator">
div>
<div id="mode">
div>
<div id="search">
div>
<div id="info">
div>
div>
<div style="text-align: center; position:relative;">
<svg width="960" height="240" style="margin-right:60px;margin-bottom:-40px;" id="svg1">
<g>g>
svg>
div>
body>
<script src="https://d3js.org/d3.v4.min.js">script>
<script type="text/javascript">
$(document).ready(function() {
//定义svg变量将布局svg1选出来
var svg = d3.select("#svg1"),
width = svg.attr("width"),
height = svg.attr("height");
//定义name变量制作图标
var names = ['Films', 'Characters', 'Planets', 'Starships', 'Vehicles', 'Species'];
var colors = ['#6ca46c', '#4e88af', '#ca635f', '#d2907c', '#d6744d', '#ded295'];
//背景颜色设置 补充CSS样式设置字体布局
for (var i=0; i < names.length; i++) {
$('#indicator').append("" + names[i] + "");
}
});
script>
html>
显示结果如下图所示:
接下来调用D3获取JSON文件数据,利用d3.forceSimulation()定义关系图 包括设置边link、排斥电荷charge、关系图中心点。
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
最后定义d3.json请求python处理好的节点及边 请求成功返回数据,否则报错,利用console.log(graph)输出获取的JSON数据结果。
var graph;
d3.json("starwar_alldata.json", function(error, data) {
if(error) throw error;
graph = data;
console.log(graph);
});
完整代码如下:
main.html
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>知识图谱title>
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="" />
<link rel="shortcut icon" href="">
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js">script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js">script>
head>
<style type="text/css">
body {
background-color: #272b30;
padding: 30px 40px;
text-align: center;
font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
}
#indicator {
position: absolute;
left: 60px;
bottom: 120px;
text-align: left;
color: #f2f2f2;
font-size: 12px;
}
#indicator>div {
margin-bottom: 4px;
}
#indicator span {
display: inline-block;
width: 30px;
height: 14px;
position: relative;
top: 2px;
margin-right: 8px;
}
style>
<body>
<h1 style="color:#fff;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">Star Warsh1>
<div style="text-align: center; position:relative;">
<svg width="800" height="500" style="margin-right:80px;margin-bottom:-40px;" id="svg1">
svg>
<div id="indicator">
div>
<div id="mode">
div>
<div id="search">
div>
<div id="info">
div>
div>
<div style="text-align: center; position:relative;">
<svg width="960" height="240" style="margin-right:60px;margin-bottom:-40px;" id="svg1">
<g>g>
svg>
div>
body>
<script src="https://d3js.org/d3.v4.min.js">script>
<script type="text/javascript">
$(document).ready(function() {
//定义svg变量将布局svg1选出来
var svg = d3.select("#svg1"),
width = svg.attr("width"),
height = svg.attr("height");
//定义name变量制作图标
var names = ['Films', 'Characters', 'Planets', 'Starships', 'Vehicles', 'Species'];
var colors = ['#6ca46c', '#4e88af', '#ca635f', '#d2907c', '#d6744d', '#ded295'];
//背景颜色设置 补充CSS样式设置字体布局
for (var i=0; i < names.length; i++) {
$('#indicator').append("" + names[i] + "");
}
//利用d3.forceSimulation()定义关系图 包括设置边link、排斥电荷charge、关系图中心点
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
//存储关系图的数据
var graph;
//定义d3.json请求python处理好的节点及边 请求成功返回数据,否则报错
d3.json("starwar_alldata.json", function(error, data) {
if(error) throw error;
graph = data;
console.log(graph);
});
// Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension
// 本地json数据需要放置服务器中请求
});
script>
html>
此时通过浏览器后台Console能够查看获取的starwar_alldata.json数据结构,但是如果直接双击main.html文件会报错,因为需要将HTML和JSON文件放置外网服务器或本地服务器中。
这里推荐大家看看我以前的文章。
PHP服务器配置:PHP XAMPP配置PHP环境和Apache80端口被占用解决方案
阿里云服务器:[网站搭建] 阿里云虚拟主机搭建及FTP文件上传
CentOS服务器:[CentOS Python系列] 五.阿里云部署web环境及通过IP地址访问服务器网页
接着我们安装XAMPP配置HTML。
运行Apache,如果80端口占用请通过上面那篇文章修改端口解决。
并将main.html和starwar_alldata.json文件放置在KG文件中,置于htdocs文件夹中,通过本地进行访问,则能够模拟浏览器获取数据。
最终显示结果如下图所示,可以看到 localhost/KG/main.html 本地访问获取了1112条边和228个实体。
最终的效果图如下所示,下一篇文章将讲解如何加载关系图谱。
PS:如果你有服务器,将HTML相关文件放置服务器中远程访问效果更好。
PS:最近参加CSDN2018年博客评选,希望您能投出宝贵的一票。我是59号,Eastmount,杨秀璋。投票地址:https://bss.csdn.net/m/topic/blog_star2018/index
五年来写了320篇博客,12个专栏,是真的热爱分享,热爱CSDN这个平台,也想帮助更多的人,专栏包括Python、数据挖掘、网络爬虫、图像处理、C#、Android等。现在也当了两年老师,更是觉得有义务教好每一个学生,让贵州学子好好写点代码,学点技术,“师者,传到授业解惑也”,提前祝大家新年快乐。2019我们携手共进,为爱而生。
(By:Eastmount 2019-02-03 深夜2点 http://blog.csdn.net/eastmount/)