«Это первый день моего участия в ноябрьском испытании обновлений, ознакомьтесь с подробностями события:Вызов последнего обновления 2021 г."
Автор документа: ArmorHTK
Информация из открытых источников:Woohoo.open kg.cai/DataSet/202…
Он содержит информацию о 100 лучших колледжах и университетах Китая; в этой статье поисковый робот получает структурированные данные обо всех колледжах и университетах моей страны и расширяет их для формирования карты знаний о колледжах.
Введение
Построение графа знаний представляет собой сложную системную инженерию. Его методы построения и реализации не уникальны, и не существует фиксированной парадигмы.
Алгоритмически извлечение сущностей, извлечение отношений, удаление знаний и алгоритмы встраивания графа знаний не рассматриваются.
С точки зрения данных, неструктурированные данные не учитываются, и только полуструктурированные данные сканируются через Baidu Baike для получения источника данных.
Таким образом, в этой демонстрации предполагается, что данные выпускаются из Neo4j или других графических баз данных при условии хорошего качества данных, выполняется визуальное отображение на стороне Интернета и соответственно разрабатываются некоторые основные функции или последующие задачи.
Обратитесь к проекту с открытым исходным кодом openKG и внесите некоторые изменения и адаптации.
1. Сбор и очистка данных
- Национальный список общеобразовательных высших учебных заведений, полученный из государственной информации.файл получить нажмите на меня
- По состоянию на 30 июня 2020 года в стране насчитывалось 3 005 высших учебных заведений, в том числе 2 740 общеобразовательных высших учебных заведений, в том числе 1 258 учреждений бакалавриата и 1 482 учреждения высшего профессионального (специального) образования;
1.1 Очистка данных списка университетов
- Перед очисткой откройте файл excel, чтобы удалить первые две строки, а затем используйте код 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()
? Хорошо! Получите чистый CSV-файл, а затем перейдите к сканеру.
1.2 Краулер получает данные json
Видно, что состав Baidu Encyclopedia колледжей и университетовhttps://baike.baidu.com/item/
+ "高校名称"
Прочитайте название школы в CSV-файле, чтобы соединить URL-адрес и получить список URL-адресов.
df = pd.read_csv("School_List_2020.csv")
urls = []
for i in df["学校名称"]:
url = "https://baike.baidu.com/item/" + str(i)
urls.append(url)
Возьмите следующие шесть тегов ['китайское имя', 'английское название', 'аббревиатура', 'время основания', 'тип', 'компетентный отдел']
Полный код сканера:
import requests
import json
import time
from tqdm import tqdm
import numpy as np
import pandas as pd
from bs4 import BeautifulSoup
# 计时
def run_time(start_time):
current_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
print(f"当前时间:{current_time}")
print("耗时:%.3f sec" %(time.time()-start_time))
# get url 并获取网页内容
def url_open(url):
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'}
r = requests.get(url, headers = headers)
return r
# get the school list
def school_list(filename):
schools = []
df = pd.read_csv(filename)
for i in df["学校名称"]:
schools.append(i)
return schools
if __name__ == "__main__":
school = school_list("School_List_2020.csv")
# print(school)
result_data = []
start_time = time.time()
for index in tqdm(school):
url = 'https://baike.baidu.com/item/' + index
print(url)
data = url_open(url)
soup = BeautifulSoup(data.content, 'html.parser', from_encoding='utf-8')
name_data = []
value_data = []
name_node = soup.find_all('dt', class_='basicInfo-item name')
# print(name_node)
for i in range(len(name_node)):
name_data.append(name_node[i].get_text().replace('\xa0', ''))
# name_data.append(name_node[i].get_text())
# print(name_data)
value_node = soup.find_all('dd', class_='basicInfo-item value')
for i in range(len(value_node)):
value_data.append(value_node[i].get_text().replace('\n', ''))
# print(type(value_node[i].get_text().replace('\n', '')))
# print(value_node[i].get_text().replace('\n', ''))
# print(value_data)
# print(type(value_data))
result = {'中文名': '无信息', '英文名': '无信息', '简称':'无信息','创办时间': '无信息', '类型': '综合', '主管部门': '无信息'}
for i in range(len(name_data)):
if name_data[i] == '中文名':
result['中文名'] = value_data[i]
if name_data[i] in ['英文名','外文名']:
result['英文名'] = value_data[i]
if name_data[i] == '简称':
result['简称'] = value_data[i]
if name_data[i] == '创办时间':
result['创办时间'] = value_data[i]
if name_data[i] == '类型':
result['类型'] = value_data[i]
if name_data[i] == '主管部门':
result['主管部门'] = value_data[i]
result_data.append({'中文名': result['中文名'], '英文名': result['英文名'], '简称': result['简称'], '创办时间': result['创办时间'], '类型': result['类型'], '主管部门': result['主管部门']})
# print('reading the website...')
# print(result_data)
fw = open('all.json', 'w', encoding='utf-8')
fw.write(json.dumps(result_data, ensure_ascii=False))
fw.close()
print('complete!')
run_time(start_time)
- Ожидайте около 15 минут
1.3 очистка данных json
- Очистка данных JSON делится на два этапа: извлечение признаков, построение узла связи.
- Извлечение признаков: в основном это извлечение атрибутов узла в тексты txt один за другим, что удобно для последующего построения тройной формы узел-ссылка-узел.
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
for i in range(len(full_data)):
# 傻瓜式遍历
for key, value in full_data[i].items():
if key == '中文名':
fw1.write("{'中文名': '" + value +"'}\n")
if key == '英文名':
fw2.write("{'英文名': '" + value +"'}\n")
if key == '简称':
fw3.write("{'简称': '" + value +"'}\n")
if key == '创办时间':
# fw4.write("{'创办时间': '" + value[0:4] +"年'}\n")
fw4.write("{'创办时间': '" + value +"'}\n")
if key == '类型':
fw5.write("{'类型': '" + value +"'}\n")
if key == '主管部门':
fw6.write("{'主管部门': '" + value +"'}\n")
fw1.close()
fw2.close()
fw3.close()
fw4.close()
fw5.close()
fw6.close()
- построение узла связи
import json
import csv
nodes = []
links = []
name_list = []
english_list = []
abbr_list = []
time_list = []
type_list = []
admin_list = []
# english2_list = []
# time2_list = []
# abbr2_list = []
# central node
nodes.append({'id': '大学', 'class': 'university', 'group': 0, 'size': 22})
# type node
fr = open('./dataprocess/Type.txt', 'r', encoding='utf-8')
for line in fr.readlines():
tmp = line.strip('\n')
for key, value in eval(tmp).items():
if value not in type_list:
type_list.append(value)
nodes.append({'id': value, 'class': 'type', 'group': 5, 'size': 18})
links.append({'source': '大学', 'target': value, 'value': 3})
links.append({'source': value, 'target': '大学', 'value': 3})
fr.close()
# english node
fr = open('./dataprocess/English.txt', 'r', encoding='utf-8')
for line in fr.readlines():
tmp = line.strip('\n')
for key, value in eval(tmp).items():
if value not in english_list:
english_list.append(value)
nodes.append({'id': value, 'class': 'english', 'group': 2, 'size': 15})
fr.close()
# abbr node
fr = open('./dataprocess/Abbr.txt', 'r', encoding='utf-8')
for line in fr.readlines():
tmp = line.strip('\n')
for key, value in eval(tmp).items():
if value not in abbr_list:
abbr_list.append(value)
nodes.append({'id': value, 'class': 'abbr', 'group': 3, 'size': 15})
fr.close()
# time node
fr = open('./dataprocess/Time.txt', 'r', encoding='utf-8')
for line in fr.readlines():
tmp = line.strip('\n')
for key, value in eval(tmp).items():
if value not in time_list:
time_list.append(value)
nodes.append({'id': value, 'class': 'time', 'group': 4, 'size': 11})
fr.close()
# admin node
fr = open('./dataprocess/Admin.txt', 'r', encoding='utf-8')
for line in fr.readlines():
tmp = line.strip('\n')
for key, value in eval(tmp).items():
if value not in admin_list:
admin_list.append(value)
nodes.append({'id': value, 'class': 'admin', 'group': 6, 'size': 11})
fr.close()
with open('./spider/all.json', 'r', encoding='utf-8') as fr:
str_data = fr.read()
full_data = json.loads(str_data)
for i in range(len(full_data)):
# for key, value in full_data[i].items():
# name node
nodes.append({'id': full_data[i]['中文名'], 'class': 'names', 'group': 1, 'size': 20})
links.append({'source': full_data[i]['类型'], 'target': full_data[i]['中文名'], 'value': 3})
links.append({'source': full_data[i]['中文名'], 'target': full_data[i]['类型'], 'value': 3})
# english node
links.append({'source': full_data[i]['中文名'], 'target': full_data[i]['英文名'], 'value': 3})
links.append({'source': full_data[i]['英文名'], 'target': full_data[i]['中文名'], 'value': 3})
# abbr node
links.append({'source': full_data[i]['中文名'], 'target': full_data[i]['简称'], 'value': 3})
links.append({'source': full_data[i]['简称'], 'target': full_data[i]['中文名'], 'value': 3})
# time node
links.append({'source': full_data[i]['简称'], 'target': full_data[i]['创办时间'], 'value': 3})
links.append({'source': full_data[i]['创办时间'], 'target': full_data[i]['简称'], 'value': 3})
# admin node
links.append({'source': full_data[i]['简称'], 'target': full_data[i]['主管部门'], 'value': 3})
links.append({'source': full_data[i]['主管部门'], 'target': full_data[i]['简称'], 'value': 3})
fw = open('./nodes.json', 'w', encoding='utf-8')
fw.write(json.dumps({'nodes': nodes, 'links': links}, ensure_ascii=False))
fw.close()
После серии обработок получают информацию об узле и звене.nodes.json
Далее мы генерируем визуализацию графа через D3.js.
2. Создайте карту
Для создания карт можно использовать Echarts или D3.js. Echarts прост в использовании, но недостаточно гибок для настройки. D3.js гибок и настраиваем, но сложен в использовании. Согласно справочным материалам, сначала используйте D3.js для отображения общей карты и внесения соответствующих изменений и украшений.
- Для получения карты сроков действия см.:blog.CSDN.net/Пробуждение от боли 007…
Показать результаты
<!DOCTYPE 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>2020年中国普通高等学校图谱可视化</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>
<script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
</head>
<style>
body {
background-color: #333333;
padding: 30px 40px;
text-align: center;
font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
}
.links line {
stroke: rgb(240, 240, 240);
stroke-opacity: 0.8;
}
.links line.inactive {
/*display: none !important;*/
stroke-opacity: 0;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.nodes circle:hover {
cursor: pointer;
}
.nodes circle.inactive {
display: none !important;
}
.texts text {
display: none;
}
.texts text:hover {
cursor: pointer;
}
.texts text.inactive {
display: none !important;
}
#indicator {
position: absolute;
left: 45px;
bottom: 50px;
text-align: left;
color: #f2f2f2;
font-size: 20px;
}
#indicator > div {
margin-bottom: 4px;
}
#indicator span {
display: inline-block;
width: 30px;
height: 14px;
position: relative;
top: 2px;
margin-right: 8px;
}
#mode {
position: absolute;
top: 60px;
left: 45px;
}
#mode span {
display: inline-block;
border: 1px solid #fff;
color: #fff;
padding: 6px 10px;
border-radius: 4px;
font-size: 14px;
transition: color, background-color .3s;
-o-transition: color, background-color .3s;
-ms-transition: color, background-color .3s;
-moz-transition: color, background-color .3s;
-webkit-transition: color, background-color .3s;
}
#mode span.active, #mode span:hover {
background-color: #fff;
color: #333;
cursor: pointer;
}
#info {
position: absolute;
bottom: 40px;
right: 30px;
text-align: right;
width: 270px;
}
#info p {
color: #fff;
font-size: 12px;
margin-bottom: 5px;
margin-top: 0px;
}
#info p span {
color: #888;
margin-right: 10px;
}
#search input {
position: absolute;
top: 100px;
left: 45px;
color: #000;
border: none;
outline: none;
box-shadow: none;
width: 160px;
background-color: #FFF;
}
#svg2 g.row:hover {
stroke-width: 1px;
stroke: #fff;
}
</style>
<body>
<h1 style="color: #fff;font-size: 32px;text-align: left;margin-left:40px;">2020年中国普通高等学校知识图谱</h1>
<div style="text-align: center;position: relative;">
<svg width="1600" height="1200" style="margin-left: 0px;margin-bottom: 0px;" id="svg1"></svg>
<div id="indicator"></div>
<div id="mode">
<span class="active" style="border-top-right-radius: 0;border-bottom-right-radius: 0; ">图形</span>
<span style="border-top-left-radius: 0;border-bottom-left-radius: 0; position: relative;left: -5px;">文字</span>
</div>
<div id="search">
<input type="text" class="form-control">
</div>
</div>
<div style="text-align: center;position: relative;"></div>
<div id="info">
<h4></h4>
</div>
<!-- <div id="main" style="width: 600px;height:400px;"></div> -->
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
$(document).ready(function () {
var svg = d3.select("#svg1"), width = svg.attr('width'), height = svg.attr('height');
var names = ['大学', '中文名','英文名','简称','创办时间','类型','主管部门'];
var colors = ['#bd0404','#b7d28d', '#b8f1ed', '#ca635f', '#5153ee','#836FFF', '#f0b631'];
// 图注
for (var i = 0; i < names.length; i++) {
$('#indicator').append("<div><span style='background-color: " + colors[i] + "'></span>" + names[i] + "</div>");
}
// 相互作用力,定义鼠标拖拽时的效果
var simulation = d3.forceSimulation()
//速度衰减因子,相当于摩擦力,0是无摩擦,1是冻结
.velocityDecay(0.6)
// α衰变,借用粒子的放射性的概念,指力的模拟经过一定次数后会逐渐停止;
// 数值范围也是0-1,如果设为1,经过300次迭代后,模拟就会停止;这里我们设为0,会一直进行模拟。
.alphaDecay(0)
//连线间的斥力
.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("nodes.json", function (error, data) {
if (error) throw error;
graph = data;
console.log(graph);
var link = svg.append("g").attr("class", "links").selectAll("line").data(graph.links).enter().append("line").attr("stroke-width", function (d) {
return 1;
});
var node = svg.append("g").attr("class", "nodes").selectAll("circle").data(graph.nodes).enter().append('circle').attr('r', function (d) {
return d.size;
}).attr("fill", function (d) {
return colors[d.group];
}).attr("stroke", "none").attr("name", function (d) {
return d.id;
}).call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
var text =
svg.append("g").attr("class", "texts").selectAll("text").data(graph.nodes).enter().append('text').attr("font-size", function (d) {
return d.size;
}).attr("fill", function (d) {
return colors[d.group];
}).attr("name", function (d) {
return d.id;
}).text(function (d) {
return d.id;
}).attr("text-anchor", 'middle').call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));
var data = svg.append("g").attr("class", "datas").selectAll("text").data(graph.nodes).enter();
node.append("title").text(function (d) {
return d.id;
});
print = node.append("title").text(function (d) {
return d.id;
});
print.enter().append("text").style("text-anchor", "middle").text(function (d) {
return d.name;
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
//tick函数的作用:由于力导向图是不断运动的,每一时刻都在发生更新,因此,必须不断更新节点和连线的位置。
//迭代力项导图位置
function ticked() {
link
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
node
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
});
text.attr('transform', function (d) {
return 'translate(' + d.x + ',' + (d.y + d.size / 2) + ')';
});
}
});
//激活导图函数
var dragging = false;
// 起始位置
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.6).restart();
d.fx = d.x;
d.fy = d.y;
dragging = true;
}
// 画图
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
// 结束位置 alphaTarget也代表衰减因子,如果设置为1迭代位置后定死位置
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
dragging = false;
}
// 图像/文字 按钮
$('#mode span').click(function (event) {
$('#mode span').removeClass('active');
$(this).addClass('active');
if ($(this).text() == '图形') {
$('.texts text').hide();
$('.nodes circle').show();
}
else {
$('.texts text').show();
$('.nodes circle').show();
}
});
$('#svg1').on('mouseenter', '.nodes circle', function (event) {
if (!dragging) {
var name = $(this).attr('name');
$('#info h4').css('color', $(this).attr('fill')).text(name);
$('#info p').remove();
console.log(info[name]);
for (var key in info[name]) {
if (typeof(info[name][key]) == 'object') {
continue;
}
if (key == 'url' || key == 'title' || key == 'name' || key == 'edited' || key == 'created' || key == 'homeworld') {
continue;
}
$('#info').append('<p><span>' + key + '</span>' + info[name][key] + '</p>');
}
d3.select("#svg1 .nodes").selectAll('circle').attr('class', function (d) {
if (d.id == name) {
return '';
}
for (var i = 0; i < graph.links.length; i++) {
if (graph.links[i]['source'].id == name && graph.links[i]['target'].id == d.id) {
return '';
}
if (graph.links[i]['target'].id == name && graph.links[i]['source'].id == d.id) {
return '';
}
}
return 'inactive';
});
d3.select("#svg1 .links").selectAll('line').attr('class', function (d) {
if (d.source.id == name || d.target.id == name) {
return '';
} else {
return 'inactive';
}
});
}
});
$('#svg1').on('mouseleave', '.nodes circle', function (event) {
if (!dragging) {
d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');
d3.select('#svg1 .links').selectAll('line').attr('class', '');
}
});
$('#svg1').on('mouseenter', '.texts text', function (event) {
if (!dragging) {
var name = $(this).attr('name');
$('#info h4').css('color', $(this).attr('fill')).text(name);
$('#info p').remove();
for (var key in info[name]) {
if (typeof(info[name][key]) == 'object') {
continue;
}
if (key == 'url' || key == 'title' || key == 'name' || key == 'edited' || key == 'created' || key == 'homeworld') {
continue;
}
$('#info').append('<p><span>' + key + '</span>' + info[name][key] + '</p>');
}
d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
if (d.id == name) {
return '';
}
for (var i = 0; i < graph.links.length; i++) {
if (graph.links[i]['source'].id == name && graph.links[i]['target'].id == d.id) {
return '';
}
if (graph.links[i]['target'].id == name && graph.links[i]['source'].id == d.id) {
return '';
}
}
return 'inactive';
});
d3.select("#svg1 .links").selectAll('line').attr('class', function (d) {
if (d.source.id == name || d.target.id == name) {
return '';
} else {
return 'inactive';
}
});
}
});
$('#svg1').on('mouseleave', '.texts text', function (event) {
if (!dragging) {
d3.select('#svg1 .texts').selectAll('text').attr('class', '');
d3.select('#svg1 .links').selectAll('line').attr('class', '');
}
});
$('#search input').keyup(function (event) {
if ($(this).val() == '') {
d3.select('#svg1 .texts').selectAll('text').attr('class', '');
d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');
d3.select('#svg1 .links').selectAll('line').attr('class', '');
}
else {
var name = $(this).val();
d3.select('#svg1 .nodes').selectAll('circle').attr('class', function (d) {
if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {
return '';
} else {
return 'inactive';
}
});
d3.select('#svg1 .texts').selectAll('text').attr('class', function (d) {
if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {
return '';
} else {
return 'inactive';
}
});
d3.select("#svg1 .links").selectAll('line').attr('class', function (d) {
return 'inactive';
});
}
});
var info;
d3.json("all.json", function (error, data) {
info = data;
});
});
</script>
</html>
3. Локальное развертывание
Структура текущего каталога следующая:
all.json
│ creatNodeLink.ipynb
│ getFeature.ipynb
│ index.html
│ nodes.json
│ School_list_2020.csv
│ Spider_school.ipynb
│ 全国高等学校名单.xls
│
└─dataprocess
Abbr.txt
Admin.txt
English.txt
Name.txt
Time.txt
Type.txt
- Перейдите в текущий каталог index.html, введите в каталоге команду cmd, введите следующий код и быстро выберите локальный сервер.
- python3 одна строка кода для получения сервера
python -m http.server 8000
Открыть браузерhttp://localhost:8000/
, проверьте свой график
Этот проект представляет собой простую визуализацию преобразования полуструктурированных данных в графы знаний.Технология немного грубая, а введение в интеллектуальный анализ данных и обработку естественного языка является новым.Пожалуйста, дайте мне больше советов~