多读书多实践,勤思考善领悟

从零开始学习知识图谱 之 十.百科知识图谱构建 4.结构化数据到RDF

本文于1581天之前发表,文中内容可能已经过时。

一. 简介

Direct mapping 本质上是通过编写启发式规则将数据库中的表转换为RDF三元组, 但该方式灵活性不强。这里我们用 D2RQ 工具,它的主要功能是提供以虚拟的、只读的RDF图形式进入到关系型数据库中。也就是说比如你通过SPARQL端口查询,输入是SPARQL查询语言,D2RQ通过mapping文件将其转换为SQL语句在关系数据库上查询,因此实际上访问的是关系型数据库。同时你也可以使用它从数据库创建 RDF 格式的文件。

D2RQ 由自己的mapping语言,R2RML-kit。它和W3C推荐的R2RML类似。你可以通过D2RQ提供的工具来根据数据库自动生成mapping文件。你可以根据自己的需求去修改定制自己的mapping文件。


D2RQ 的框架图

本教程的项目代码放在github上,下载地址为《从零开始学习知识图谱》项目源代码

二. 环境准备

1. 操作系统

支持操作系统:windows、macOS、Linux。为了方便大家搭建开发环境,笔者尽可能在windows下构建,系列篇未特意说明时操作系统都是windows。Linux安装可以参考VirtualBox虚拟机安装UbuntuVirtualBox虚拟机安装CentOS8进行安装。

2. jdk

安装参见windows系统安装JDK

3. D2RQ

安装参见从零开始学习知识图谱 之 二

三. D2RQ 的使用

1. 生成 mapping 文件

使用 D2RQ 自带的 generate-mapping 工具, 根据数据库自动生成mapping文件。

1
PS C:\my\d2rq> ./generate-mapping -u root -p root -o kg_demo_mapping_hudong_fenlei.ttl jdbc:mysql:///hudong_fenlei?useSSL=false

其中 -u root 是数据库的用户名,-p root 是数据库的密码。 -o 指定生成 mapping 文件的名字。jdbc:mysql: /// 后面跟着的数据库的名字。 ?useSSL=false语句设置SSL为false以避免提示警告,数据库没设置主键会提示警告。

注意:笔者是Windows PowerShell命令窗口运行命令,若在cmd中去掉前面的./,即:generate-mapping开始。下面的命令也如此。

通过上述命令,我们将得到如下的 mapping 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
@prefix map: <#> .
@prefix db: <> .
@prefix vocab: <vocab/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix d2rq: <http://www.wiwiss.fu-berlin.de/suhl/bizer/D2RQ/0.1#> .
@prefix jdbc: <http://d2rq.org/terms/jdbc/> .

map:database a d2rq:Database;
d2rq:jdbcDriver "com.mysql.jdbc.Driver";
d2rq:jdbcDSN "jdbc:mysql:///hudong_fenlei?useSSL=false";
d2rq:username "root";
d2rq:password "root";
jdbc:autoReconnect "true";
jdbc:zeroDateTimeBehavior "convertToNull";
.

# Table lemmas
map:lemmas a d2rq:ClassMap;
d2rq:dataStorage map:database;
d2rq:uriPattern "lemmas/@@lemmas.title_id@@";
d2rq:class vocab:lemmas;
d2rq:classDefinitionLabel "lemmas";
.
map:lemmas__label a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property rdfs:label;
d2rq:pattern "lemmas #@@lemmas.title_id@@";
.
map:lemmas_title a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_title;
d2rq:propertyDefinitionLabel "lemmas title";
d2rq:column "lemmas.title";
.
map:lemmas_title_id a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_title_id;
d2rq:propertyDefinitionLabel "lemmas title_id";
d2rq:column "lemmas.title_id";
d2rq:datatype xsd:integer;
.
map:lemmas_abstract a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_abstract;
d2rq:propertyDefinitionLabel "lemmas abstract";
d2rq:column "lemmas.abstract";
.
map:lemmas_infobox a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_infobox;
d2rq:propertyDefinitionLabel "lemmas infobox";
d2rq:column "lemmas.infobox";
.
map:lemmas_subject a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_subject;
d2rq:propertyDefinitionLabel "lemmas subject";
d2rq:column "lemmas.subject";
.
map:lemmas_disambi a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_disambi;
d2rq:propertyDefinitionLabel "lemmas disambi";
d2rq:column "lemmas.disambi";
.
map:lemmas_redirect a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_redirect;
d2rq:propertyDefinitionLabel "lemmas redirect";
d2rq:column "lemmas.redirect";
.
map:lemmas_curLink a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_curLink;
d2rq:propertyDefinitionLabel "lemmas curLink";
d2rq:column "lemmas.curLink";
.
map:lemmas_interPic a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_interPic;
d2rq:propertyDefinitionLabel "lemmas interPic";
d2rq:column "lemmas.interPic";
.
map:lemmas_interLink a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_interLink;
d2rq:propertyDefinitionLabel "lemmas interLink";
d2rq:column "lemmas.interLink";
.
map:lemmas_exterLink a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_exterLink;
d2rq:propertyDefinitionLabel "lemmas exterLink";
d2rq:column "lemmas.exterLink";
.
map:lemmas_relateLemma a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_relateLemma;
d2rq:propertyDefinitionLabel "lemmas relateLemma";
d2rq:column "lemmas.relateLemma";
.
map:lemmas_all_text a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property vocab:lemmas_all_text;
d2rq:propertyDefinitionLabel "lemmas all_text";
d2rq:column "lemmas.all_text";
.

但是这个 mapping 文件我们还不能使用,需要做一些修改:

  • vocab 这个我们不用,因此删除开头和正文中带有 vocab 的部分

    1
    2
    3
    @prefix vocab: <vocab/> .
    d2rq:class vocab:lemmas;
    ......
  • 因为我们有互动和百度两个图谱,因此需要指定不同的前缀,对于互动百科

    1
    @prefix : <http://www.kghudong.com#> .
  • 因为是中文图谱,因此要指定文字编码:

    1
    d2rq:jdbcDSN "jdbc:mysql:///hudong_fenlei?useUnicode=true&characterEncoding=utf8&useSSL=false";

为了实现生成及修改全自自动,需编写dos下的bat实现,get_ttl.bat代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@echo off&setlocal enabledelayedexpansion

set db=hudong_fenlei
set file=kg_demo_mapping_%db%.ttl

call generate-mapping -u root -p root -o %file% jdbc:mysql:///%db%?useSSL=false

:: call findstr /i /v /C:"@prefix vocab" "%file%">>%file%.bk
:: move /y %file%.bk %file%

for /f "tokens=1,* delims=:" %%b in ('findstr /n ".*" "%file%"')do (
set "var=%%c"
if "!var!" neq "@prefix vocab: <vocab/> ." (
if "!var!" equ "" (
>>%file%.bk echo,!var!) ^
else if "!var!" equ "@prefix jdbc: <http://d2rq.org/terms/jdbc/> ." (
>>%file%.bk echo,!var!
>>%file%.bk echo,@prefix : ^<http://www.kghudong.com#^> .) ^
else (
echo;"!var!"|find "jdbcDSN"&&(
>>%file%.bk echo, d2rq:jdbcDSN ^"jdbc:mysql:///%db%?useUnicode=true^&characterEncoding=utf8^&useSSL=false^";)||(
set "var=!var:vocab= !"
>>%file%.bk echo,!var!))
)
)
move /y %file%.bk %file%

运行get_ttl.bat是修改后的,得到如下 mapping 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
@prefix map: <#> .
@prefix db: <> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix d2rq: <http://www.wiwiss.fu-berlin.de/suhl/bizer/D2RQ/0.1#> .
@prefix jdbc: <http://d2rq.org/terms/jdbc/> .
@prefix : <http://www.kghudong.com#> .

map:database a d2rq:Database;
d2rq:jdbcDriver "com.mysql.jdbc.Driver";
d2rq:jdbcDSN "jdbc:mysql:///hudong_fenlei?useUnicode=true&characterEncoding=utf8&useSSL=false";
d2rq:username "root";
d2rq:password "root";
jdbc:autoReconnect "true";
jdbc:zeroDateTimeBehavior "convertToNull";
.

# Table lemmas
map:lemmas a d2rq:ClassMap;
d2rq:dataStorage map:database;
d2rq:uriPattern "lemmas/@@lemmas.title_id@@";
d2rq:class :lemmas;
d2rq:classDefinitionLabel "lemmas";
.
map:lemmas__label a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property rdfs:label;
d2rq:pattern "lemmas #@@lemmas.title_id@@";
.
map:lemmas_title a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_title;
d2rq:propertyDefinitionLabel "lemmas title";
d2rq:column "lemmas.title";
.
map:lemmas_title_id a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_title_id;
d2rq:propertyDefinitionLabel "lemmas title_id";
d2rq:column "lemmas.title_id";
d2rq:datatype xsd:integer;
.
map:lemmas_abstract a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_abstract;
d2rq:propertyDefinitionLabel "lemmas abstract";
d2rq:column "lemmas.abstract";
.
map:lemmas_infobox a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_infobox;
d2rq:propertyDefinitionLabel "lemmas infobox";
d2rq:column "lemmas.infobox";
.
map:lemmas_subject a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_subject;
d2rq:propertyDefinitionLabel "lemmas subject";
d2rq:column "lemmas.subject";
.
map:lemmas_disambi a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_disambi;
d2rq:propertyDefinitionLabel "lemmas disambi";
d2rq:column "lemmas.disambi";
.
map:lemmas_redirect a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_redirect;
d2rq:propertyDefinitionLabel "lemmas redirect";
d2rq:column "lemmas.redirect";
.
map:lemmas_curLink a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_curLink;
d2rq:propertyDefinitionLabel "lemmas curLink";
d2rq:column "lemmas.curLink";
.
map:lemmas_interPic a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_interPic;
d2rq:propertyDefinitionLabel "lemmas interPic";
d2rq:column "lemmas.interPic";
.
map:lemmas_interLink a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_interLink;
d2rq:propertyDefinitionLabel "lemmas interLink";
d2rq:column "lemmas.interLink";
.
map:lemmas_exterLink a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_exterLink;
d2rq:propertyDefinitionLabel "lemmas exterLink";
d2rq:column "lemmas.exterLink";
.
map:lemmas_relateLemma a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_relateLemma;
d2rq:propertyDefinitionLabel "lemmas relateLemma";
d2rq:column "lemmas.relateLemma";
.
map:lemmas_all_text a d2rq:PropertyBridge;
d2rq:belongsToClassMap map:lemmas;
d2rq:property :lemmas_all_text;
d2rq:propertyDefinitionLabel "lemmas all_text";
d2rq:column "lemmas.all_text";
.

2. 网页端访问

前面我们说 D2RQ提供虚拟的RDF访问么,现在我们来使用它。

首先进入到 d2rq文件夹内,运行

1
2
PS C:\my\d2rq> ./d2r-server kg_demo_mapping_hudong_fenlei.ttl
09:49:26 INFO JettyLauncher :: [[[ Server started at http://localhost:2020/ ]]]

命令开启服务器,然后通过网页端进行访问 http://localhost:2020/

点进 lemmas,随意打开一个,就可以看到该词条的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
lemmas #1
Resource URI: http://localhost:2020/resource/lemmas/1
Home | All lemmas
Property Value
rdfs:label lemmas #1
:lemmas_abstract 博焱,1987827出生于台湾,中国台湾男演员,毕业于中国文化大学体育学系。2008年,出演首部电视剧《篮球火》,从而正式进入演艺圈 。2009年,主演台湾偶像剧《终极三国》,在剧中饰演张飞 。2010年,所在组合“武虎将”获得第十四届全球华语榜中榜暨亚洲影响力大典最佳新晋组合。2011年,出演台湾偶像剧《旋风管家》。2013年,出演台湾偶像剧《女王的诞生》 。2014年,出演清朝古装剧《龙飞凤舞》。编辑摘要
:lemmas_all_text 演艺经历/博焱 编辑 博焱 2008年,出演首部电视剧《篮球火》,在剧中饰演齐啸云,从而正式进入演艺圈 。2009年,与胡宇威、陈德修、陈乃荣合作主演台湾偶像剧《终极三国》,在剧中饰演与关羽是出生入死的好朋友张飞 ;同年,与胡宇崴、班杰、邵翔、寺唯宏正组成“武虎将”,并推出《对手》、《朋友》两首单曲,收录于《强辩之〈终极三国〉三团鼎立SUPER大斗阵》合辑中 ;同年,武虎将推出首本写真集《我爱武虎将》 。2010年,与蔡颐榛、李昂霖、蔡芷纭合作主演魔法偶像剧《萌学园之萌骑士传奇》,在剧中饰演中等部-见习魔法士烈焱坚尼 ;同年,出演魔法偶像剧《萌学园2圣战再起》 ;328日,所在组合“武虎将”获得第十四届全球华语榜中榜暨亚洲影响力大典最佳新晋组合 。 博焱剧照 2011年,出演改编自日本漫画家畑健二郎的漫画作品《旋风管家!》的台湾偶像剧《旋风管家》,在剧中饰演康太郎的管家袁也枫。2013年,在台湾偶像剧《刺猬男孩》中饰演小蔡;同年,出演孙耀威、杨谨华、蔡淑臻主演的台湾偶像剧《女王的诞生》,在剧中饰演唐美宝的弟弟唐育成 。2014年,出演王建复、余秉谚、傅子纯、柯叔元主演的清朝古装剧《龙飞凤舞》,在剧中饰演春禾五霸之一的江水清。 主要作品/博焱 编辑 参演电视剧 上映时间 剧名 扮演角色 导演 合作演员 2014 龙飞凤舞 江水清 孙树培 王建复, 余秉谚 2013 女王的诞生 唐育成 陈戎晖 孙耀威, 杨谨华 2013 刺猬男孩 小蔡 王小棣 , 安哲毅 何以奇, 李佳颖 2011 旋风管家 袁也枫 朴信惠, 彭若仪 2010 爱似百汇 用餐的客人 辰亦儒 , 炎亚纶 2010 萌学园2圣战再起 烈焱坚尼 叶中强 李昂霖, 林奕勋, 阿本, 蔡芷纭, 蔡颐榛 2010 萌学园之萌骑士传奇 烈焱坚尼 叶中强 李昂霖, 林奕勋, 曾子余, 蔡芷纭, 蔡颐榛 2009 真情伴星月 利比亚留学生 黄克义 仇政, 贾孝国 2009 终极三国 张飞 潘静如, 黄家新, 冯家瑞 胡宇崴, 陈德修 2008 篮球火 齐啸云 林合隆, 杨利 吴尊, 言承旭, 罗志祥 音乐作品 专辑 专辑名称 发行时间 强辩之终极三国三国鼎立SUPER大斗阵 2009-03-04 国语 参演MV 年份 专辑名称 曲目 2009年   强辩之终极三国三国鼎立SUPER大斗阵 对手 - 武虎将、东城卫、强辩 2009年   Paradise 24小时疯狂 - 陈乃荣(武虎将客串) 出版书籍 年份 书名 出版社 2009-09-11   我爱武虎将 台湾角川 获奖记录/博焱 编辑 全球华语榜中榜 ▪ 2010-03-28 第十四届 全球华语榜中榜暨亚洲影响力大典最佳新晋组合 (获奖) 人物评价/博焱 编辑 博焱是一个NG大王,他在拍《终极三国》时NG掉好几饼带子,作为新人拍戏经验不是很足,进入状态很慢 (网易评) 。博焱主演的魔法偶像剧《萌学园》深受大小朋友的喜爱 (ETtoday评) 。博焱是一位体型精力都一级棒的帅哥,他不仅身材完美,武艺也很高超 (腾讯评) 。
:lemmas_curLink http://www.baike.com/wiki/%E6%9E%97%E4%BC%AF%E5%BD%A6
:lemmas_disambi 博焱
:lemmas_exterLink {}
:lemmas_infobox {}
:lemmas_interLink {}
:lemmas_interPic http://a3.att.hudong.com/57/28/50200009239445155794289843634_s.jpg,http://www.huimg.cn/public/images/a-baikelogo2.gif,http://www.huimg.cn/lib/0.gif,http://www.huimg.cn/lib/0.gif,http://a3.att.hudong.com/57/28/50200009239445155794289843634_s.jpg,//www.huimg.cn/public/images/a-baikelogo2.gif,//a7.baike.com/46/56/4656D0B2C95947F0E226C489FE26466F_90.png?title=%E5%8D%9A%E7%84%B1
:lemmas_redirect 林伯彦
:lemmas_relateLemma {"\u6797\u4f2f\u5f66": "http://www.baike.com/wiki/%E6%9E%97%E4%BC%AF%E5%BD%A6?prd=zhengwenye_left_tongyici"}
:lemmas_subject 人物,名人,明星,歌手,演员,综艺
:lemmas_title 博焱
:lemmas_title_id 1 (xsd:integer)
rdf:type :lemmas
The server is configured to display only a limited number of values (limit per property bridge: 50).



Metadata
<http://localhost:2020/data/lemmas/1>
dc:date 2019-11-05T01:50:19.403Z
prv:containedBy <http://localhost:2020/dataset>
void:inDataset <http://localhost:2020/dataset>
rdf:type prv:DataItem
rdf:type foaf:Document

3. 三元组的生成

在生成 mapping 文件后,通过

1
PS C:\my\d2rq> ./dump-rdf -o hudong_fenlei.nt kg_demo_mapping_hudong_fenlei.ttl

命令将数据转换为Ntriples。这一步比较慢,生成的文件也比较大,像互动百科有 430W 词条,得到的nt文件大概59个G,因此要做好预留空间。