Hi,大家好,我是半亩花海。本项目旨在构建一个基于电商网站商品分类目录的知识图谱,通过Python程序爬取分类信息,形成商品的目录树,并提取底层商品的概念信息。项目将重点展示如何利用知识图谱呈现概念层级知识,以及如何将数据存储至Neo4j数据库以进行可视化和查询。通过该项目,我们将深入了解知识图谱的构建、数据入库及其在电商领域中的实际应用,提升对知识组织和信息管理的理解。
目录
一、实验目的
二、实验要求
三、实验原理
四、环境配置
1. 下载代码
2. 安装依赖环境
五、实验步骤
1. 启动Neo4j服务
2. 爬取数据
3. 知识入库
六、实验结果
七、完整代码
1. collect_info.py
2. build_kg.py
一、实验目的
(1)了解如何利用知识图谱呈现概念层级知识。
(2)了解如何利用电商网站中的商品分类目录建立知识图谱。
(3)了解知识图谱的入库和查看方式。
二、实验要求
(1)掌握如何用Python程序爬取电商网站中的商品分类目录并构建商品的目录树。
(2)掌握如何获取目录树对应的底层商品的概念信息并组织形成商品的知识图谱。
(3)掌握数据入库方式及如何用Neo4jDesktop查看数据。
三、实验原理
下面以京东电商为实验数据来源,采集京东商品目录树,获取其对应的底层商品概念信息,并组织形成商品的知识图谱。
目前,该图谱包括两类关系,即概念之间的上下位关系(用isa表示)及商品品牌与商品之间的销售关系(用sale表示),涉及商品概念数目达1300多个,商品品牌数目达10万多个,属性数目达几千种,关系数目规模达65万。
四、环境配置
1. 下载代码
下载代码(https://github.com/Airobin329/ProductKnowledgeGraph.git),并解压。
--|ProductKnowledgeGraph #项目文件夹 --|--|data #存放爬取数据的文件夹 --|--|--brands.txt #品牌数据文件 --|--|--goods.txt #商品数据文件 --|--|--goods_info.json #商品信息文件 --|--|image #图片 --|--|build_kg.py #数据入库程序 --|--|collect_info.py #爬虫程序 --|--|README.md #仓库说明 |
2. 安装依赖环境
(1)安装Python3(Download Python | Python.org),下载对应操作系统的安装包。
(2)安装依赖库。
win+R→cmd→输入以下命令行:
pipinstalllxmlpymongopy2neochardet-ihttps://pypi.tuna.tsinghua.edu.cn/simple |
- lxml:用于解析html文件
- pymongo:用于连接mongodb
- py2neo:用于连接neo4j
- chardet:用于识别html文件编码
(3)安装MongoDB并启动服务(Windows 平台安装 MongoDB | 菜鸟教程,也可不安装)。
(4)安装Neo4j并启动服务(../课程学习/知识表示与处理/实验/21142604陶锐-第五章-语义网络写入图形数据库-实验报告.docx,内含Neo4j的安装过程)。
五、实验步骤
1. 启动Neo4j服务
首先以管理员身份运行cmd,再在命令行处输入neo4j.batconsole,然后在浏览器中输入网址http://localhost:7474/,即会出现相关界面。
2. 爬取数据
运行如下的爬虫程序:pythoncollect_info.py
这段代码用于构建一个商品知识图谱,主要目的是将商品数据以图谱的形式存储到数据库中,以便后续进行商品相关的查询和分析。
(1)主要功能
- 读取数据:从指定的数据文件中读取商品信息,包括商品的分类、属性和品牌等信息。
- 构建图谱:根据读取的数据,构建商品知识图谱。图谱中的节点表示商品、品牌等实体,边表示实体之间的关系,例如商品之间的属于关系,品牌与商品之间的销售关系等。
- 连接到Neo4j数据库:使用py2neo库连接到Neo4j图数据库,将构建的知识图谱存储到数据库中。
(2)代码的执行流程
- 初始化GoodsKg类和设置参数:在初始化GoodsKg类时,首先需要设置数据文件的路径和连接到Neo4j数据库所需的参数,如主机地址、用户名和密码等。这些参数会在构建图谱时用到。
- 读取数据文件并解析商品信息:使用read_data方法读取数据文件,并解析其中的商品信息。此方法会返回商品的类别、属性和品牌等信息,以便后续构建图谱。
- 构建图谱中的节点和边:根据解析得到的商品信息,构建图谱中的节点和边。节点的类型包括商品(Product)和品牌(Brand),节点的属性是商品或品牌的名称。边的类型包括商品之间的属于关系和品牌与商品之间的销售关系,边的属性包括关系名称。
- 执行create_graph方法存储图谱到数据库中:最后,执行create_graph方法将构建的图谱存储到Neo4j数据库中。
(3)代码的运行结果
3. 知识入库
运行如下的入库程序:pythonbuild_kg.py
- 初始化连接和关闭:
- 在__init__方法中,初始化了与Neo4j数据库的连接,包括数据库地址和认证信息。
- close方法用于关闭与数据库的连接,确保资源被正确释放。
- 获取网页内容:
- get_html方法用于发送HTTP请求获取京东网站首页的HTML内容。
- 使用了requests库发送GET请求,并返回响应内容。
- 解析网页结构:
- 在home_list方法中,使用XPath解析HTML内容,提取商品分类信息。
- 使用lxml.etree库解析HTML,定位到指定的HTML元素。
- 遍历分类信息:
- 遍历提取的分类信息,包括一级、二级和三级分类以及对应的链接。
- 通过循环遍历,依次访问各个分类的链接,获取其下的三级分类信息和链接。
- 创建节点和关系:
- create_node方法用于在Neo4j数据库中创建节点和关系,表示商品的分类信息。
- 使用Cypher查询语言,执行MERGE操作创建节点,并通过关系连接不同级别的分类节点。
- 执行主程序:
- 在__main__部分,创建GoodSchema对象,调用其home_list方法获取商品分类信息。
- 最后关闭与数据库的连接,释放资源。
六、实验结果
使用Neo4jDesktop查看数据。
由于数据量巨大,在此只截取部分结果,如下图所示。
(1)当爬取数据之后,打开Neo4j界面的结果为:
(2)当知识入库之后,打开Neo4j界面的结果为:
七、完整代码
1. collect_info.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
@Project : ProductKnowledgeGraph
@File : collect_info.py
@IDE : PyCharm
@Author : 半亩花海
@Date : 2024/06/03 11:07
"""
import json
import os
from py2neo import Graph, Node, Relationship
class GoodsKg:
def __init__(self):
cur = '/'.join(os.path.abspath(__file__).split('/')[:-1])
self.data_path = os.path.join(cur, 'data/goods_info.json')
self.g = Graph(
host="127.0.0.1", # neo4j 搭载服务器的 ip 地址,ifconfig 可获取到
user="neo4j", # 数据库 user name,如果没有更改过,应该是 neo4j
password="0123456789")
self.g.delete_all() # 删除数据库中的所有节点和关系
return
'''读取数据'''
def read_data(self):
rels_goods = []
rels_brand = []
goods_attrdict = {}
concept_goods = set()
concept_brand = set()
count = 0
for line in open(self.data_path, encoding='utf-8'):
count += 1
print(count)
line = line.strip()
data = json.loads(line)
first_class = data['fisrt_class'].replace("'", '')
second_class = data['second_class'].replace("'", '')
third_class = data['third_class'].replace("'", '')
attr = data['attrs']
concept_goods.add(first_class)
concept_goods.add(second_class)
concept_goods.add(third_class)
rels_goods.append('@'.join([second_class, 'is_a', '属于', first_class]))
rels_goods.append('@'.join([third_class, 'is_a', '属于', second_class]))
if attr and '品牌' in attr:
brands = attr['品牌'].split(';')
for brand in brands:
brand = brand.replace("'", '')
concept_brand.add(brand)
rels_brand.append('@'.join([brand, 'sales', '销售', third_class]))
goods_attrdict[third_class] = {name: value for name, value in attr.items() if name != '品牌'}
return concept_brand, concept_goods, rels_goods, rels_brand
'''构建图谱'''
def create_graph(self):
concept_brand, concept_goods, rels_goods, rels_brand = self.read_data()
print('creating nodes....')
self.create_node('Product', concept_goods)
self.create_node('Brand', concept_brand)
print('creating edges....')
self.create_edges(rels_goods, 'Product', 'Product')
self.create_edges(rels_brand, 'Brand', 'Product')
return
'''批量建立节点'''
def create_node(self, label, nodes):
pairs = []
bulk_size = 1000
batch = 0
bulk = 0
batch_all = len(nodes) // bulk_size
print(batch_all)
for node_name in nodes:
sql = """CREATE(:%s {name:'%s'})""" % (label, node_name)
pairs.append(sql)
bulk += 1
if bulk % bulk_size == 0 or bulk == batch_all + 1:
sqls = '\n'.join(pairs)
self.g.run(sqls)
batch += 1
print(batch * bulk_size, '/', len(nodes), 'finished')
pairs = []
return
'''构造图谱关系边'''
def create_edges(self, rels, start_type, end_type):
batch = 0
count = 0
for rel in set(rels):
count += 1
rel = rel.split('@')
start_name = rel[0]
end_name = rel[3]
rel_type = rel[1]
rel_name = rel[2]
sql = 'match (m:%s), (n:%s) where m.name = "%s" and n.name = "%s" create (m)-[:%s{name:"%s"}]->(n)' % (
start_type, end_type, start_name, end_name, rel_type, rel_name)
try:
self.g.run(sql)
except Exception as e:
print(e)
if count % 10 == 0:
print(count)
return
if __name__ == '__main__':
handler = GoodsKg()
handler.create_graph()
2. build_kg.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
@Project : ProductKnowledgeGraph
@File : build_kg.py
@IDE : PyCharm
@Author : 半亩花海
@Date : 2024/06/04 20:13
"""
import requests
from lxml import etree
import json
from neo4j import GraphDatabase
class GoodSchema:
def __init__(self):
self.driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "0123456789"))
def close(self):
self.driver.close()
def get_html(self, url):
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.5.17 (KHTML, like Gecko) Version/8.0.5 Safari/600.5.17"
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.text
except Exception as e:
print(f"Failed to fetch HTML from {url}: {e}")
return None
def home_list(self):
url = 'https://www.jd.com/allSort.aspx'
html = self.get_html(url)
if html:
selector = etree.HTML(html)
divs = selector.xpath('//div[@class= "category-item m"]')
for indx, div in enumerate(divs):
first_name = div.xpath('./div[@class="mt"]/h2/span/text()')[0]
second_classes = div.xpath('./div[@class="mc"]/div[@class="items"]/dl')
for dl in second_classes:
second_name = dl.xpath('./dt/a/text()')[0]
third_classes = ['https:' + i for i in dl.xpath('./dd/a/@href')]
third_names = dl.xpath('./dd/a/text()')
for third_name, url in zip(third_names, third_classes):
try:
self.create_node(first_name, second_name, third_name, url)
print(indx, len(divs), first_name, second_name, third_name)
except Exception as e:
print(e)
else:
print("Failed to fetch HTML from JD homepage")
def create_node(self, first_name, second_name, third_name, url):
with self.driver.session() as session:
session.run(
"MERGE (f:FirstClass {name: $first_name})"
"MERGE (s:SecondClass {name: $second_name})"
"MERGE (t:ThirdClass {name: $third_name, url: $url})"
"MERGE (f)-[:HAS_SECOND]->(s)"
"MERGE (s)-[:HAS_THIRD]->(t)",
first_name=first_name, second_name=second_name, third_name=third_name, url=url
)
if __name__ == '__main__':
handler = GoodSchema()
handler.home_list()
handler.close()