[知识图谱实战篇] 三.Python提取JSON数据、HTML+D3构建基本可视化布局

版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/86755610

前面作者讲解了很多知识图谱原理知识,包括知识图谱相关技术、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


一.Python提取节点和关联数据

前文讲述了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'
}
  • 第二步获取节点和边并存储至本地JSON文件中。其中节点为六大类,关系为它们之间互为关联,并定义节点属性(名称、类别、大小等)和关系属性(起始点、结束点、大小)。

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网页布局

接着讲解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 Wars</h1>

<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

<!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>知识图谱</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 Wars</h1>

	<!-- 第一个布局 绘制知识图谱主图 -->
	<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>

<!-- 增加D3元素库 -->
<script src="https://d3js.org/d3.v4.min.js"></script>
</html>

显示结果如下图所示:


三.绘制图例

接下来这部分代码是绘制图例,其中核心代码是通过JS获取SVG1布局,并加载六种颜色,对应六个图例。再设置 indicator 图例布局,动态加载颜色。

<!-- 补充JS代码 -->
<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("<div><span style='background-color:" + colors[i] + "'></span>" + names[i] + "</div>");
		}

	});
</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完整代码修改如下:

<!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>知识图谱</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 Wars</h1>

	<!-- 第一个布局 绘制知识图谱主图 -->
	<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>

<!-- 增加D3元素库 -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- 补充JS代码 -->
<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("<div><span style='background-color:" + colors[i] + "'></span>" + names[i] + "</div>");
		}

	});
</script>
</html>

显示结果如下图所示:


四.D3获取JSON数据

接下来调用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

<!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>知识图谱</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 Wars</h1>

	<!-- 第一个布局 绘制知识图谱主图 -->
	<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>

<!-- 增加D3元素库 -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- 补充JS代码 -->
<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("<div><span style='background-color:" + colors[i] + "'></span>" + names[i] + "</div>");
		}

		//利用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/)

展开阅读全文

没有更多推荐了,返回首页