应用场景
代码语言:txt复制在平台初期或者后期都需要一些标准的官方信息来填充平台缺乏的内容,以往可能是通过爬虫进行爬取,但是这块受限于一些法律或者内容的付费独家信息和内容准确性的问题。因此需要一种渠道拿到我们希望获取的各类数据,比如,城市信息、人物信息、书籍刊物、歌曲、电影等等。
代码语言:txt复制这类信息最直接的方式就是维基百科,里面基本可以搜索到我们能获取的数据,因此我们就考虑怎么从维基百科拉取标准化数据。
数据研究
代码语言:txt复制最开始我们所了解到的一个平台是[dbpedia](http://dbpedia.org/), 基于wikipedia爬取的数据然后标准化落入图形数据库中,关于图形数据库的介绍大家可以自己前往学习了解,其主要是三元组(主谓宾),这里可能也短短几句话描述不完。他使用的查询语言主要是Cypher、Gremlin和Sparql三种,这里我们专注介绍Sparql,本文主要是介绍如何一步步优化,达到我们的目标。
数据库对比分析
代码语言:txt复制在最开始我们使用的是dbpedia,但是这其中绕了一点弯路,因为dbpedia是基于wiki的数据更新的,但是它的数据不会实时更新,导致的一些问题就是很多东西在dbpedia没有。后面了解到wikidata,其完全是wikipedia的数据库。下面是一个对比表格:
数据库 | prefix | 语言/测试链接 | 更新方式 | 来源 | 导出 | SDK |
---|---|---|---|---|---|---|
wikidata | 自带查询 | Spraql | 实时更新 | 官方 | 不支持 | 有/无需 |
dbpedia | 自行设置 | Spraql | 手动更新 | 非官方 | 支持 | 有 |
从上面对比,可以很明显发现,我们需要的是获取准确标准化准确内容,并且需要实时的与wikipedia同步,因此选择wikidata,那么接下来我们就看看如何一步步实践在wikidata上利用sparql语法查询到我们需要的内容。
wikidata实践以及优化策略
代码语言:txt复制所有的语言或者db都应该从最简单的方式入手学习,所以从最简单的“hello world”入手,当然这里不是真实的hello world,这里只是一个最简单的实践例子。
“hello world”
代码语言:txt复制现在我们希望使用wikidata查询江西有哪些包含行政区域实体,那么可以组成以下语法,具体如何构建的我们一步步来分析。
代码语言:txt复制SELECT ?cityName
WHERE
{
?item ?label "江西"@zh.
?item wdt:P31 wd:Q1615742.
?item wdt:P150 ?city.
?city wdt:P1448 ?cityName.
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],nl" }.
}
代码语言:txt复制首先根据中文名找到江西的所有信息item,然后根据属性P31性质,筛选出Q1615742,该属性可以通过链接查看https://www.wikidata.org/wiki/Q1615742,可以看到起代表的意思是中国的省份,这样就可以拿到唯一的一个item信息。
代码语言:txt复制第二步从item中获取P150属性,P150也可以通过该链接查看https://www.wikidata.org/wiki/Property:P150,它具体的意思就是行政区域实体。
代码语言:txt复制第三部再根据拿到的行政区域实体,获取其P1448属性的名字,P1448就是其官方名字。
代码语言:txt复制这样经过三个步骤就获取到了我们想要的数据了。
查询示例
代码语言:txt复制wiki是有提供一些查询命令,这些都是一些示例,[wikidata-query-example](https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service/queries/examples),在做查询时,可以根据相似的语句进行修改,然后来尝试。
脱离页面测试工具
代码语言:txt复制如果需要服务端爬取,那么最基本的就是你需要用代码的方式来运行,wikidata的好处在于可以将查询结果灵活获取后分析结果数据,通过代码的方式落入我们自己需要的数据存储中。
代码语言:txt复制在使用https://query.wikidata.org/的时候,打开浏览器network,你会发现其每次运行时候会发生一条请求,该请求样式如下:
代码语言:txt复制https://query.wikidata.org/sparql?query={sparql}
代码语言:txt复制知道这个以后,就可以很简单的拼凑该请求了,然后可以看到它的返回时一个json·结构,那么只需要构建一个HTTP请求即可,以下是node.js的测试运行代码。请注意opt的header信息,如果不加这个会出现403的情况。
代码语言:txt复制const request = require('request'); // need to npm install request
const queryUrlMode = 'https://query.wikidata.org/sparql?query={sparql}';
let querySparql = '
SELECT ?cityName
WHERE
{
?item ?label "江西"@zh.
?item wdt:P31 wd:Q1615742.
?item wdt:P150 ?city.
?city wdt:P1448 ?cityName.
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],nl" }.
}
';
let queryUrl = queryUrlMode.replace('{sparql}', encodeURIComponent(querySparql));
let opt = {};
opt.headers = {
'accept': 'application/sparql-results json',
'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/72.0.3626.121 Safari/537.36',
};
// 请注意这里一定要加header,不然会出现403的情况
request.get(queryUrl, opt, (err, data) => {
console.log(data);
});
真实使用场景
代码语言:txt复制我们的场景时需要使用wikidata来补充我们艺人的信息,例如艺人的出生日、出生地、流派、别名、性别等等,我们只有一个艺人名字,那么接下来我们就尝试使用一个艺人来做测试,比如赵薇。
代码语言:txt复制首先我们来构建查询语句如下:
代码语言:txt复制SELECT DISTINCT ?item ?itemLabel
WHERE
{
?item ?label "赵薇"@zh.
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],nl" }.
}
代码语言:txt复制运行成功后,你可以看到我们能获取的数据了,那么接下来有个问题,这里使用的是zh,这就需要我们知道当前语种,当然有js脚本根据unicode是可以判断当前语种的,或者包含的语种的,最大的麻烦在于找到这些号段。
代码语言:txt复制function checkIncludeLang(str){
const languageMapping = [
{
'lang' : 'zh',
'reg' : /[u4e00-u9fa5]/,
},
{
'lang' : 'en',
'reg' : /[u0041-u005a]/,
},
{
'lang' : 'en',
'reg' : /[u0061-u007a']/
},
{
'lang' : 'th',
'reg' : /[u0e00-u0e7f]/
}
];
let languages = new Set();
languageMapping.forEach(item => {
let patt = new RegExp(item['reg']);
if(patt.test(str)){
languages.add(item['lang']);
}
});
return languages;
}
代码语言:txt复制我们做了如下一系列优化策略,在任何场景下,都是可以借鉴和参考,将有助于你们能够找到你们所希望获取到的数据集合。
代码语言:txt复制接下来我们看下详细的代码处理过程,看下如何来做的。
转义支持
代码语言:txt复制由于需要组装成链接的查询url语句,因此在对query部分的参数,需要进行urlencode,这里最好使用encodeURIComponent,因为我们需要对query部分参数做转义。具体的代码策略如下,上面已经说明到了。
代码语言:txt复制let queryUrl = queryUrlMode.replace('{sparql}', encodeURIComponent(querySparql));
####多语种支持
代码语言:txt复制那么改进一下策略,首先判断字符串中包含的语种,例如既包含en又包含中文,这时候可以使用UNION来取并集,改进代码如下,注意这里的en和th是手动填写的,可以利用上面我们提到的方法,首先去获取字符串包含的语种,然后再构建语句。
代码语言:txt复制SELECT DISTINCT ?item ?itemLabel
WHERE
{
{
?item ?label "Bodyslam"@en.
} UNION
{
?item ?label "Bodyslam"@th.
} UNION
{
?item wdt:P106 wd:Q177220.
} UNION
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],nl" }
}
条件限制、属性扩充
代码语言:txt复制大家上面看到的是赵薇,赵薇在维基百科只有一个,因此相对来说查询较为简单,直接使用名字就好,但是如果查询的时候出现很多相似接口内,比如说alan walker,我们应该如何筛选出,哪些是我们需要的结果呢,这里就需要根据你的应用场景来了,因为我们需要查询艺人,所以我们可以对查询的结果针对属性进行限制,增加如下限制条件,查询优化如下:
代码语言:txt复制SELECT DISTINCT ?item ?itemLabel
WHERE
{
{
?item ?label "Alan Walker"@en.
}
{
?item wdt:P106 wd:Q639669.
} UNION
{
?item wdt:P106 wd:Q183945.
} UNION
{
?item wdt:P31 wd:Q215380.
} UNION
{
?item wdt:P31 wd:Q216337.
} UNION
{
?item wdt:P31 wd:Q9212979.
} UNION
{
?item wdt:P106 wd:Q177220.
}
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],nl" }
}
代码语言:txt复制在条件限制的情况下,可能会导致匹配结果急剧下降,这时候需要分析限制条件是否需要进一步扩充来满足更多的查询结果,下面是基于我们查询的是歌曲艺人相关信息做的属性扩充。
大小写命名扩充
代码语言:txt复制由于名字的原因在英文大小写上有区分,而sparql在大小写上是敏感的,如果单纯的使用全局匹配,然后filter的方式,查询相当慢,慢的无法使用导致超时。那么我们应该如何解决该类问题呢?
代码语言:txt复制最开始思考的是使用全排列搜索,类似将ala,转化为ala、alA、aLa、aLA、Ala、AlA、ALA、ALa,在字符串小的时候,还好,但是对于长度比如说6,就2的6次方,已经到64了,相对来说不是可扩展的方式。
代码语言:txt复制因为人名涉及的一般有几种情况,最原始名字、全部大写、全部小写、首字母大写、空格后首字母大写,基于这些考虑,就可以将原有的复杂情况,缩小的更短。这里也是根据使用场景来,比如城市名字,大部分也是类似的原则,基于上面原则就可以更快捷的找到我们需要的内容。查询优化代码如下:
代码语言:txt复制SELECT DISTINCT ?item ?itemLabel
WHERE
{
{
?item ?label "bts"@en.
} UNION
{
?item ?label "BTS"@en.
} UNION
{
?item ?label "Bts"@en.
}
{
?item wdt:P106 wd:Q639669.
} UNION
{
?item wdt:P106 wd:Q183945.
} UNION
{
?item wdt:P31 wd:Q215380.
} UNION
{
?item wdt:P31 wd:Q216337.
} UNION
{
?item wdt:P31 wd:Q9212979.
} UNION
{
?item wdt:P106 wd:Q177220.
}
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],nl" }
}
结果还是无法满意?
代码语言:txt复制对于失败数据,进行再进一步分析、分类。是否匹配到数据,属性限制导致(是否需要增加属性),是否存在特殊的命名,不符合我们上面说的“大小写命名扩充”的一类。基于这些再进一步优化,经过这类处理后,应该大部分在维基百科获取的数据都能通过这样的脚步的方式查询到。
总结
代码语言:txt复制wikidata的数据相对还是比较正确和权威的,通过这类方法可以补充我们很多官方信息,并且这些方法也是正规途径,没有额外的法律风险,希望本文对大家有所帮助。
代码语言:txt复制你是否学会了?如果没有,就尝试使用wikidata来获取你自己想要的数据吧。
代码语言:txt复制本次研究人分析人员:corbinli、danhuang。