文档作者:Armor
资料参考:http://www.openkg.cn/dataset/2020

简介

构造知识图谱是一个复杂的系统工程。其构造和实现方法并不唯一,尚未存在固定的范式。
在算法上不考虑知识图谱的实体抽取、关系抽取、知识消融和嵌入算法。
在数据上不考虑非结构化数据,仅通过百度百科爬取半结构化的数据进行数据源获取。
所以本Demo是假设在已具备数据质量优良的前提下,把数据从Neo4j或其他的图数据库中释放出来,进行web端的可视化展示,并据此开发一些基本功能或下游任务。
参考openKG的开源项目,进行一定程度的修改和适配。
目的有二:

  • 一是作为CQUSTKG的小型的知识图谱展示Demo
  • 二是提供一个简单的知识图谱可视化开发流程

1.数据获取和清洗


  • 从政府公开信息获得 全国普通高等学校名单 文件获取点我
  • 截至2020年6月30日,全国高等学校共计3005所,其中:普通高等学校2740所,含本科院校1258所、高职(专科)院校1482所;


1.1 清洗高校名单数据

  • 在清洗之前,先打开excel文件删除头两行,再用python代码进行清洗 ```python import numpy as np import pandas as pd

用pandas 打开

df = pd.read_excel(“全国高等学校名单.xls”)

先把备注中所有的Nan全部替换为公办

df[“备注”].fillna(“公办”,inplace=True)

删除含Nan的空行

df.dropna(axis=0,inplace=True)

学校表示码转换数据类型 成int64

df[“学校标识码”] = df[“学校标识码”].astype(np.int64)

保存文件

df.to_csv(“School_List_2020.csv”,index=False)

看看长啥样

print(df.shape) df.head()

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2655886/1617184909988-0531a5e6-fccb-47a7-8896-0924ae11a03b.png#align=left&display=inline&height=194&margin=%5Bobject%20Object%5D&name=image.png&originHeight=194&originWidth=529&size=15471&status=done&style=shadow&width=529)<br />🚀OK!得到一个干净的csv文件,接下来进行爬虫。
  2. <a name="E6mWb"></a>
  3. ## 1.2 爬虫获取json数据
  4. 可以看到高校的百度百科的构成是 `https://baike.baidu.com/item/` + `"高校名称"` <br />所以爬虫遍历的urls链接可以用上面的csv文件来构造:
  5. ```python
  6. df = pd.read_csv("School_List_2020.csv")
  7. urls = []
  8. for i in df["学校名称"]:
  9. url = "https://baike.baidu.com/item/" + str(i)
  10. urls.append(url)

image.png

报错缺什么自己pip ,爬虫完整代码:

  1. import requests
  2. import json
  3. import time
  4. from tqdm import tqdm
  5. import numpy as np
  6. import pandas as pd
  7. from bs4 import BeautifulSoup
  8. # 计时
  9. def run_time(start_time):
  10. current_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
  11. print(f"当前时间:{current_time}")
  12. print("耗时:%.3f sec" %(time.time()-start_time))
  13. # get url 并获取网页内容
  14. def url_open(url):
  15. headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
  16. r = requests.get(url, headers = headers)
  17. return r
  18. # get the school list
  19. def school_list(filename):
  20. schools = []
  21. df = pd.read_csv(filename)
  22. for i in df["学校名称"]:
  23. schools.append(i)
  24. return schools
  25. if __name__ == "__main__":
  26. school = school_list("School_List_2020.csv")
  27. # print(school)
  28. result_data = []
  29. start_time = time.time()
  30. for index in tqdm(school):
  31. url = 'https://baike.baidu.com/item/' + index
  32. print(url)
  33. data = url_open(url)
  34. soup = BeautifulSoup(data.content, 'html.parser', from_encoding='utf-8')
  35. name_data = []
  36. value_data = []
  37. name_node = soup.find_all('dt', class_='basicInfo-item name')
  38. # print(name_node)
  39. for i in range(len(name_node)):
  40. name_data.append(name_node[i].get_text().replace('\xa0', ''))
  41. # name_data.append(name_node[i].get_text())
  42. # print(name_data)
  43. value_node = soup.find_all('dd', class_='basicInfo-item value')
  44. for i in range(len(value_node)):
  45. value_data.append(value_node[i].get_text().replace('\n', ''))
  46. # print(type(value_node[i].get_text().replace('\n', '')))
  47. # print(value_node[i].get_text().replace('\n', ''))
  48. # print(value_data)
  49. # print(type(value_data))
  50. result = {'中文名': '无信息', '英文名': '无信息', '简称':'无信息','创办时间': '无信息', '类型': '综合', '主管部门': '无信息'}
  51. for i in range(len(name_data)):
  52. if name_data[i] == '中文名':
  53. result['中文名'] = value_data[i]
  54. if name_data[i] in ['英文名','外文名']:
  55. result['英文名'] = value_data[i]
  56. if name_data[i] == '简称':
  57. result['简称'] = value_data[i]
  58. if name_data[i] == '创办时间':
  59. result['创办时间'] = value_data[i]
  60. if name_data[i] == '类型':
  61. result['类型'] = value_data[i]
  62. if name_data[i] == '主管部门':
  63. result['主管部门'] = value_data[i]
  64. result_data.append({'中文名': result['中文名'], '英文名': result['英文名'], '简称': result['简称'], '创办时间': result['创办时间'], '类型': result['类型'], '主管部门': result['主管部门']})
  65. # print('reading the website...')
  66. # print(result_data)
  67. fw = open('all.json', 'w', encoding='utf-8')
  68. fw.write(json.dumps(result_data, ensure_ascii=False))
  69. fw.close()
  70. print('complete!')
  71. run_time(start_time)

image.png

  • 预计等候15分钟左右

image.png

1.3 json数据清洗

  • json数据清洗分为两步骤:特征提取、linknode构造。
  • 特征提取:主要是把节点属性提取为一个一个的txt文本,方便后续构造node-link-node的三元组形式。 ```python import json

with open(‘./spider/all.json’, ‘r’, encoding=’utf-8’) as fr: str_data = fr.read() full_data = json.loads(str_data) # json 解码 fw1 = open(‘./dataprocess/Name.txt’, ‘w’, encoding=’utf-8’) # 名称list fw2 = open(‘./dataprocess/English.txt’, ‘w’, encoding=’utf-8’) # 英文名list fw3 = open(‘./dataprocess/Abbr.txt’, ‘w’, encoding=’utf-8’) # 简称list fw4 = open(‘./dataprocess/Time.txt’, ‘w’, encoding=’utf-8’) # 创办时间list fw5 = open(‘./dataprocess/Type.txt’, ‘w’, encoding=’utf-8’) # 类型list fw6 = open(‘./dataprocess/Admin.txt’, ‘w’, encoding=’utf-8’) # 主管部门list

  1. for i in range(len(full_data)):
  2. # 傻瓜式遍历
  3. for key, value in full_data[i].items():
  4. if key == '中文名':
  5. fw1.write("{'中文名': '" + value +"'}\n")
  6. if key == '英文名':
  7. fw2.write("{'英文名': '" + value +"'}\n")
  8. if key == '简称':
  9. fw3.write("{'简称': '" + value +"'}\n")
  10. if key == '创办时间':
  11. # fw4.write("{'创办时间': '" + value[0:4] +"年'}\n")
  12. fw4.write("{'创办时间': '" + value +"'}\n")
  13. if key == '类型':
  14. fw5.write("{'类型': '" + value +"'}\n")
  15. if key == '主管部门':
  16. fw6.write("{'主管部门': '" + value +"'}\n")

fw1.close() fw2.close() fw3.close() fw4.close() fw5.close() fw6.close()

  1. - linknode构造
  2. ```python
  3. import json
  4. import csv
  5. nodes = []
  6. links = []
  7. name_list = []
  8. english_list = []
  9. abbr_list = []
  10. time_list = []
  11. type_list = []
  12. admin_list = []
  13. # english2_list = []
  14. # time2_list = []
  15. # abbr2_list = []
  16. # central node
  17. nodes.append({'id': '大学', 'class': 'university', 'group': 0, 'size': 22})
  18. # type node
  19. fr = open('./dataprocess/Type.txt', 'r', encoding='utf-8')
  20. for line in fr.readlines():
  21. tmp = line.strip('\n')
  22. for key, value in eval(tmp).items():
  23. if value not in type_list:
  24. type_list.append(value)
  25. nodes.append({'id': value, 'class': 'type', 'group': 5, 'size': 18})
  26. links.append({'source': '大学', 'target': value, 'value': 3})
  27. links.append({'source': value, 'target': '大学', 'value': 3})
  28. fr.close()
  29. # english node
  30. fr = open('./dataprocess/English.txt', 'r', encoding='utf-8')
  31. for line in fr.readlines():
  32. tmp = line.strip('\n')
  33. for key, value in eval(tmp).items():
  34. if value not in english_list:
  35. english_list.append(value)
  36. nodes.append({'id': value, 'class': 'english', 'group': 2, 'size': 15})
  37. fr.close()
  38. # abbr node
  39. fr = open('./dataprocess/Abbr.txt', 'r', encoding='utf-8')
  40. for line in fr.readlines():
  41. tmp = line.strip('\n')
  42. for key, value in eval(tmp).items():
  43. if value not in abbr_list:
  44. abbr_list.append(value)
  45. nodes.append({'id': value, 'class': 'abbr', 'group': 3, 'size': 15})
  46. fr.close()
  47. # time node
  48. fr = open('./dataprocess/Time.txt', 'r', encoding='utf-8')
  49. for line in fr.readlines():
  50. tmp = line.strip('\n')
  51. for key, value in eval(tmp).items():
  52. if value not in time_list:
  53. time_list.append(value)
  54. nodes.append({'id': value, 'class': 'time', 'group': 4, 'size': 11})
  55. fr.close()
  56. # admin node
  57. fr = open('./dataprocess/Admin.txt', 'r', encoding='utf-8')
  58. for line in fr.readlines():
  59. tmp = line.strip('\n')
  60. for key, value in eval(tmp).items():
  61. if value not in admin_list:
  62. admin_list.append(value)
  63. nodes.append({'id': value, 'class': 'admin', 'group': 6, 'size': 11})
  64. fr.close()
  65. # # english2 node
  66. # fr = open('./dataprocess/English.txt', 'r', encoding='utf-8')
  67. # for line in fr.readlines():
  68. # tmp = line.strip('\n')
  69. # for key, value in eval(tmp).items():
  70. # if value not in english2_list:
  71. # english2_list.append(value)
  72. # nodes.append({'id': value, 'class': 'english2', 'group': 7, 'size': 13})
  73. # fr.close()
  74. # # abbr2 node
  75. # fr = open('./dataprocess/Abbr.txt', 'r', encoding='utf-8')
  76. # for line in fr.readlines():
  77. # tmp = line.strip('\n')
  78. # for key, value in eval(tmp).items():
  79. # if value not in abbr2_list:
  80. # abbr2_list.append(value)
  81. # nodes.append({'id': value, 'class': 'abbr2', 'group': 8, 'size': 13})
  82. # fr.close()
  83. # # time2 node
  84. # fr = open('./dataprocess/Time.txt', 'r', encoding='utf-8')
  85. # for line in fr.readlines():
  86. # tmp = line.strip('\n')
  87. # for key, value in eval(tmp).items():
  88. # if value not in time2_list:
  89. # time2_list.append(value)
  90. # nodes.append({'id': value, 'class': 'time2', 'group': 9, 'size': 13})
  91. # fr.close()
  92. with open('./spider/all.json', 'r', encoding='utf-8') as fr:
  93. str_data = fr.read()
  94. full_data = json.loads(str_data)
  95. for i in range(len(full_data)):
  96. # for key, value in full_data[i].items():
  97. # name node
  98. nodes.append({'id': full_data[i]['中文名'], 'class': 'names', 'group': 1, 'size': 20})
  99. links.append({'source': full_data[i]['类型'], 'target': full_data[i]['中文名'], 'value': 3})
  100. links.append({'source': full_data[i]['中文名'], 'target': full_data[i]['类型'], 'value': 3})
  101. # english node
  102. links.append({'source': full_data[i]['中文名'], 'target': full_data[i]['英文名'], 'value': 3})
  103. links.append({'source': full_data[i]['英文名'], 'target': full_data[i]['中文名'], 'value': 3})
  104. # abbr node
  105. links.append({'source': full_data[i]['中文名'], 'target': full_data[i]['简称'], 'value': 3})
  106. links.append({'source': full_data[i]['简称'], 'target': full_data[i]['中文名'], 'value': 3})
  107. # time node
  108. links.append({'source': full_data[i]['简称'], 'target': full_data[i]['创办时间'], 'value': 3})
  109. links.append({'source': full_data[i]['创办时间'], 'target': full_data[i]['简称'], 'value': 3})
  110. # admin node
  111. links.append({'source': full_data[i]['简称'], 'target': full_data[i]['主管部门'], 'value': 3})
  112. links.append({'source': full_data[i]['主管部门'], 'target': full_data[i]['简称'], 'value': 3})
  113. fw = open('./nodes.json', 'w', encoding='utf-8')
  114. fw.write(json.dumps({'nodes': nodes, 'links': links}, ensure_ascii=False))
  115. fw.close()

经过一系列处理得到了含node和link信息的 nodes.json 接下来我们通过D3.js进行图谱可视化生成。

2.生成图谱

图谱生成可以用Echarts或者D3.js,Echarts简单易上手但定制不够灵活,D3.js灵活可定制但难上手。根据参考资料,先用D3.js实现整体图谱的展示,并做适当修改美化。

image.png

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8"/>
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>2020年中国普通高等学校图谱可视化</title>
  8. <meta name="description" content=""/>
  9. <meta name="keywords" content=""/>
  10. <meta name="author" content=""/>
  11. <link rel="shortcut icon" href="">
  12. <script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
  13. <link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
  14. <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
  15. <script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
  16. </head>
  17. <style>
  18. body {
  19. background-color: #333333;
  20. padding: 30px 40px;
  21. text-align: center;
  22. font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
  23. }
  24. .links line {
  25. stroke: rgb(240, 240, 240);
  26. stroke-opacity: 0.8;
  27. }
  28. .links line.inactive {
  29. /*display: none !important;*/
  30. stroke-opacity: 0;
  31. }
  32. .nodes circle {
  33. stroke: #fff;
  34. stroke-width: 1.5px;
  35. }
  36. .nodes circle:hover {
  37. cursor: pointer;
  38. }
  39. .nodes circle.inactive {
  40. display: none !important;
  41. }
  42. .texts text {
  43. display: none;
  44. }
  45. .texts text:hover {
  46. cursor: pointer;
  47. }
  48. .texts text.inactive {
  49. display: none !important;
  50. }
  51. #indicator {
  52. position: absolute;
  53. left: 45px;
  54. bottom: 50px;
  55. text-align: left;
  56. color: #f2f2f2;
  57. font-size: 20px;
  58. }
  59. #indicator > div {
  60. margin-bottom: 4px;
  61. }
  62. #indicator span {
  63. display: inline-block;
  64. width: 30px;
  65. height: 14px;
  66. position: relative;
  67. top: 2px;
  68. margin-right: 8px;
  69. }
  70. #mode {
  71. position: absolute;
  72. top: 60px;
  73. left: 45px;
  74. }
  75. #mode span {
  76. display: inline-block;
  77. border: 1px solid #fff;
  78. color: #fff;
  79. padding: 6px 10px;
  80. border-radius: 4px;
  81. font-size: 14px;
  82. transition: color, background-color .3s;
  83. -o-transition: color, background-color .3s;
  84. -ms-transition: color, background-color .3s;
  85. -moz-transition: color, background-color .3s;
  86. -webkit-transition: color, background-color .3s;
  87. }
  88. #mode span.active, #mode span:hover {
  89. background-color: #fff;
  90. color: #333;
  91. cursor: pointer;
  92. }
  93. #info {
  94. position: absolute;
  95. bottom: 40px;
  96. right: 30px;
  97. text-align: right;
  98. width: 270px;
  99. }
  100. #info p {
  101. color: #fff;
  102. font-size: 12px;
  103. margin-bottom: 5px;
  104. margin-top: 0px;
  105. }
  106. #info p span {
  107. color: #888;
  108. margin-right: 10px;
  109. }
  110. #search input {
  111. position: absolute;
  112. top: 100px;
  113. left: 45px;
  114. color: #000;
  115. border: none;
  116. outline: none;
  117. box-shadow: none;
  118. width: 160px;
  119. background-color: #FFF;
  120. }
  121. #svg2 g.row:hover {
  122. stroke-width: 1px;
  123. stroke: #fff;
  124. }
  125. </style>
  126. <body>
  127. <h1 style="color: #fff;font-size: 32px;text-align: left;margin-left:40px;">2020年中国普通高等学校知识图谱</h1>
  128. <div style="text-align: center;position: relative;">
  129. <svg width="1600" height="1200" style="margin-left: 0px;margin-bottom: 0px;" id="svg1"></svg>
  130. <div id="indicator"></div>
  131. <div id="mode">
  132. <span class="active" style="border-top-right-radius: 0;border-bottom-right-radius: 0; ">图形</span>
  133. <span style="border-top-left-radius: 0;border-bottom-left-radius: 0; position: relative;left: -5px;">文字</span>
  134. </div>
  135. <div id="search">
  136. <input type="text" class="form-control">
  137. </div>
  138. </div>
  139. <div style="text-align: center;position: relative;"></div>
  140. <div id="info">
  141. <h4></h4>
  142. </div>
  143. <!-- <div id="main" style="width: 600px;height:400px;"></div> -->
  144. </body>
  145. <script src="https://d3js.org/d3.v4.min.js"></script>
  146. <script>
  147. $(document).ready(function () {
  148. var svg = d3.select("#svg1"), width = svg.attr('width'), height = svg.attr('height');
  149. var names = ['大学', '中文名','英文名','简称','创办时间','类型','主管部门'];
  150. var colors = ['#bd0404','#b7d28d', '#b8f1ed', '#ca635f', '#5153ee','#836FFF', '#f0b631'];
  151. // 图注
  152. for (var i = 0; i < names.length; i++) {
  153. $('#indicator').append("<div><span style='background-color: " + colors[i] + "'></span>" + names[i] + "</div>");
  154. }
  155. // 相互作用力,定义鼠标拖拽时的效果
  156. var simulation = d3.forceSimulation()
  157. //速度衰减因子,相当于摩擦力,0是无摩擦,1是冻结
  158. .velocityDecay(0.6)
  159. // α衰变,借用粒子的放射性的概念,指力的模拟经过一定次数后会逐渐停止;
  160. // 数值范围也是0-1,如果设为1,经过300次迭代后,模拟就会停止;这里我们设为0,会一直进行模拟。
  161. .alphaDecay(0)
  162. //连线间的斥力
  163. .force("link", d3.forceLink().id(function (d) {
  164. return d.id;
  165. }))
  166. //斥力
  167. .force("charge", d3.forceManyBody())
  168. //中心力
  169. .force("center", d3.forceCenter(width / 2, height / 2));
  170. //导图设置
  171. var graph;
  172. d3.json("nodes.json", function (error, data) {
  173. if (error) throw error;
  174. graph = data;
  175. console.log(graph);
  176. var link = svg.append("g").attr("class", "links").selectAll("line").data(graph.links).enter().append("line").attr("stroke-width", function (d) {
  177. return 1;
  178. });
  179. var node = svg.append("g").attr("class", "nodes").selectAll("circle").data(graph.nodes).enter().append('circle').attr('r', function (d) {
  180. return d.size;
  181. }).attr("fill", function (d) {
  182. return colors[d.group];
  183. }).attr("stroke", "none").attr("name", function (d) {
  184. return d.id;
  185. }).call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
  186. var text =
  187. svg.append("g").attr("class", "texts").selectAll("text").data(graph.nodes).enter().append('text').attr("font-size", function (d) {
  188. return d.size;
  189. }).attr("fill", function (d) {
  190. return colors[d.group];
  191. }).attr("name", function (d) {
  192. return d.id;
  193. }).text(function (d) {
  194. return d.id;
  195. }).attr("text-anchor", 'middle').call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
  196. var data = svg.append("g").attr("class", "datas").selectAll("text").data(graph.nodes).enter();
  197. node.append("title").text(function (d) {
  198. return d.id;
  199. });
  200. print = node.append("title").text(function (d) {
  201. return d.id;
  202. });
  203. print.enter().append("text").style("text-anchor", "middle").text(function (d) {
  204. return d.name;
  205. });
  206. simulation
  207. .nodes(graph.nodes)
  208. .on("tick", ticked);
  209. simulation.force("link")
  210. .links(graph.links);
  211. //tick函数的作用:由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。
  212. //迭代力项导图位置
  213. function ticked() {
  214. link
  215. .attr("x1", function (d) {
  216. return d.source.x;
  217. })
  218. .attr("y1", function (d) {
  219. return d.source.y;
  220. })
  221. .attr("x2", function (d) {
  222. return d.target.x;
  223. })
  224. .attr("y2", function (d) {
  225. return d.target.y;
  226. });
  227. node
  228. .attr("cx", function (d) {
  229. return d.x;
  230. })
  231. .attr("cy", function (d) {
  232. return d.y;
  233. });
  234. text.attr('transform', function (d) {
  235. return 'translate(' + d.x + ',' + (d.y + d.size / 2) + ')';
  236. });
  237. }
  238. });
  239. //激活导图函数
  240. var dragging = false;
  241. // 起始位置
  242. function dragstarted(d) {
  243. if (!d3.event.active) simulation.alphaTarget(0.6).restart();
  244. d.fx = d.x;
  245. d.fy = d.y;
  246. dragging = true;
  247. }
  248. // 画图
  249. function dragged(d) {
  250. d.fx = d3.event.x;
  251. d.fy = d3.event.y;
  252. }
  253. // 结束位置 alphaTarget也代表衰减因子,如果设置为1迭代位置后定死位置
  254. function dragended(d) {
  255. if (!d3.event.active) simulation.alphaTarget(0);
  256. d.fx = null;
  257. d.fy = null;
  258. dragging = false;
  259. }
  260. // 图像/文字 按钮
  261. $('#mode span').click(function (event) {
  262. $('#mode span').removeClass('active');
  263. $(this).addClass('active');
  264. if ($(this).text() == '图形') {
  265. $('.texts text').hide();
  266. $('.nodes circle').show();
  267. }
  268. else {
  269. $('.texts text').show();
  270. $('.nodes circle').show();
  271. }
  272. });
  273. $('#svg1').on('mouseenter', '.nodes circle', function (event) {
  274. if (!dragging) {
  275. var name = $(this).attr('name');
  276. $('#info h4').css('color', $(this).attr('fill')).text(name);
  277. $('#info p').remove();
  278. console.log(info[name]);
  279. for (var key in info[name]) {
  280. if (typeof(info[name][key]) == 'object') {
  281. continue;
  282. }
  283. if (key == 'url' || key == 'title' || key == 'name' || key == 'edited' || key == 'created' || key == 'homeworld') {
  284. continue;
  285. }
  286. $('#info').append('<p><span>' + key + '</span>' + info[name][key] + '</p>');
  287. }
  288. d3.select("#svg1 .nodes").selectAll('circle').attr('class', function (d) {
  289. if (d.id == name) {
  290. return '';
  291. }
  292. for (var i = 0; i < graph.links.length; i++) {
  293. if (graph.links[i]['source'].id == name && graph.links[i]['target'].id == d.id) {
  294. return '';
  295. }
  296. if (graph.links[i]['target'].id == name && graph.links[i]['source'].id == d.id) {
  297. return '';
  298. }
  299. }
  300. return 'inactive';
  301. });
  302. d3.select("#svg1 .links").selectAll('line').attr('class', function (d) {
  303. if (d.source.id == name || d.target.id == name) {
  304. return '';
  305. } else {
  306. return 'inactive';
  307. }
  308. });
  309. }
  310. });
  311. $('#svg1').on('mouseleave', '.nodes circle', function (event) {
  312. if (!dragging) {
  313. d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');
  314. d3.select('#svg1 .links').selectAll('line').attr('class', '');
  315. }
  316. });
  317. $('#svg1').on('mouseenter', '.texts text', function (event) {
  318. if (!dragging) {
  319. var name = $(this).attr('name');
  320. $('#info h4').css('color', $(this).attr('fill')).text(name);
  321. $('#info p').remove();
  322. for (var key in info[name]) {
  323. if (typeof(info[name][key]) == 'object') {
  324. continue;
  325. }
  326. if (key == 'url' || key == 'title' || key == 'name' || key == 'edited' || key == 'created' || key == 'homeworld') {
  327. continue;
  328. }
  329. $('#info').append('<p><span>' + key + '</span>' + info[name][key] + '</p>');
  330. }
  331. d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
  332. if (d.id == name) {
  333. return '';
  334. }
  335. for (var i = 0; i < graph.links.length; i++) {
  336. if (graph.links[i]['source'].id == name && graph.links[i]['target'].id == d.id) {
  337. return '';
  338. }
  339. if (graph.links[i]['target'].id == name && graph.links[i]['source'].id == d.id) {
  340. return '';
  341. }
  342. }
  343. return 'inactive';
  344. });
  345. d3.select("#svg1 .links").selectAll('line').attr('class', function (d) {
  346. if (d.source.id == name || d.target.id == name) {
  347. return '';
  348. } else {
  349. return 'inactive';
  350. }
  351. });
  352. }
  353. });
  354. $('#svg1').on('mouseleave', '.texts text', function (event) {
  355. if (!dragging) {
  356. d3.select('#svg1 .texts').selectAll('text').attr('class', '');
  357. d3.select('#svg1 .links').selectAll('line').attr('class', '');
  358. }
  359. });
  360. $('#search input').keyup(function (event) {
  361. if ($(this).val() == '') {
  362. d3.select('#svg1 .texts').selectAll('text').attr('class', '');
  363. d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');
  364. d3.select('#svg1 .links').selectAll('line').attr('class', '');
  365. }
  366. else {
  367. var name = $(this).val();
  368. d3.select('#svg1 .nodes').selectAll('circle').attr('class', function (d) {
  369. if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {
  370. return '';
  371. } else {
  372. return 'inactive';
  373. }
  374. });
  375. d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
  376. if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {
  377. return '';
  378. } else {
  379. return 'inactive';
  380. }
  381. });
  382. d3.select("#svg1 .links").selectAll('line').attr('class', function (d) {
  383. return 'inactive';
  384. });
  385. }
  386. });
  387. var info;
  388. d3.json("all.json", function (error, data) {
  389. info = data;
  390. });
  391. });
  392. </script>
  393. </html>

3.本地部署

当前目录下的结构如下图:

  1. all.json
  2. │ creatNodeLink.ipynb
  3. │ getFeature.ipynb
  4. │ index.html
  5. │ nodes.json
  6. │ School_list_2020.csv
  7. │ Spider_school.ipynb
  8. │ 全国高等学校名单.xls
  9. └─dataprocess
  10. Abbr.txt
  11. Admin.txt
  12. English.txt
  13. Name.txt
  14. Time.txt
  15. Type.txt
  • 移动到当前index.html目录下,在目录下进入cmd命令,输入以下代码,快速假设本地服务器。
  • python3一行代码搞定服务器
    1. python -m http.server 8000
    打开浏览器 http://localhost:8000/ ,查看自己的图谱吧
    image.png
    Demo预览:http://yzy616.xyz/
    接下来,不管是增加下游任务,或是添加事务功能。
    不管怎么说,先做增删改查,对于图数据库的内容还不是很熟悉,后续会由实验室的老武同学完善Neo4j相关协助开发。

    4.下游任务

    待开发