项目地址:
https://github.com/CrisJk/Agriculture-KnowledgeGraph-Data.git
说明
本项目为一些用于获取知识图谱中三元组关系的python脚本。包括爬取Wikidata数据的爬虫、爬取复旦知识工场数据的爬虫(由于知识工场限制爬取,这部分暂时不好用)、提取所有中文维基页面的脚本以及将Wikidata三元组数据对齐到中文维基页面语句的脚本。
运行环境
python3、 Scrapy、neo4j(仅对齐时需要)
wikidataCrawler
用来爬取wikidata上定义的所有关系
wikidata中的所有关系都汇总在该网页上(链接) ,wikidataCrawler将该网页下的汇总的所有关系及其对应的中文名称爬取下来,存储为json格式
使用方法
进入到wikidataCrawler目录下,运行scrapy crawl relation
即可爬取wikidata中定义的所有关系。可以得到relation.json
和chrmention.json
。
relation.json
内容: 关系的id,关系所属的大类,关系所属子类,对应的链接,关系的英文表示chrmention.json
内容: 关系的id,关系的中文表示(对于不包含中文表示的数据暂时不做处理)。
将relation.json
和chrmention.json
的数据进行合并,运行mergeChrmentionToRelation.ipynb
即可,得到的结果存储在result.json
中,匹配失败的存在fail.json
中
wikientities
用来爬取实体,返回json格式
进入到wikientities目录下,运行scrapy crawl entity
。可以得到 entity.json
。
entity.json
是以predict_labels.txt中的实体为搜索词,在wikidata上搜索返回的json内容。
entity.json
中还包括搜索词(即实体)以及实体所属的类别(和predict_labels.txt中一样)也加入json中存储。
由于我目前想做一个农业领域的知识图谱,因此predict_label.txt中很多词都是关于农业的,若想爬取其他实体,则自己修改predict_label.txt中的数据即可。
wikidataRelation说明
用来爬取实体和实体间的关系三元组,返回三元组
Wikidata是一个开放的全领域的知识库,其中包含大量的实体以及实体间的关系。下图是一个wikidata的实体页面
从图中可以看到wikidata实体页面包含实体的描述和与该实体相关联的其它实体及对应的关系。
wikidataRelation爬取得到的是实体和实体间的三元关系,例如合成橡胶
和石油
之间存在material used
的关系,因此可以得到如下json格式的三元组:
{"entity1": "合成橡胶", "relation": "material used", "entity2": "石油"}
使用方法
首先运行preProcess.py,得到readytoCrawl.json。然后进入到wikidataRelation目录下,运行scrapy crawl entityRelation。可以得到entityRelation.json
。
entityRelation.json
是利用entity.json
中的所有实体为基础,获取与这些实体相关的其他实体和关系。
wikidataProcessing
用来处理得到的三元组关系(entityRelation.json)
将得到的entityRelation.json 处理成csv,并且存入neo4j数据库。该文件夹下的两个文件hudong_pedia.csv
和hudong_pedia2.csv
是爬取互动百科相关页面得到的,对应的就是wikientities目录下的predict_label.txt
中的实体。运行relationDataProcessing.py
可以得到new_node.csv
(即从wikidata实体页面中爬取得到的实体不包含在predict_label.txt
中的部分)、wikidata_relation.csv
(predict_label.txt中实体之间的关系)以及wikidata_relation2.csv
(predict_label.txt中实体和新发现实体间的关系),将该目录下所有csv导入到neo4j中,具体操作参见Agriculture_KnowledgeGraph 中的项目部署部分。
CN_DBpediaCrawler
CN_DBpedia限制访问,需要向复旦大学知识工场申请API,否则只能限制每分钟爬取次数,这里的爬虫还有些问题,暂时不能使用。
wikiextractor
wikiextractor是用来获取维基百科语料的工具,维基百科有wiki dump可以直接下载:下载链接 ,下载好之后,利用wikiextractor工具进行处理,可以剔除掉一些无用的信息,直接得到维基百科语料库。wikiextractor工具链接 ,下载并安装后,将wiki dump放在wikiextractor目录下,执行命令
bzcat zhwiki-latest-pages-articles.xml.bz2 | python WikiExtractor.py -b 500K -o extracted -
可以得到处理好的维基百科语料,在目录extractor下。
由于得到的语料既有简体也有繁体,所以要进行繁简体转换。将本项目wikiextractor\extracted目录下的三个python文件复制到你处理好的维基百科语料的目录(extractor)下,运行convLan.py便可以将繁体转化为简体。
TrainDataBaseOnWiki
用于将从wikidata知识库中获取的三元组关系对齐到中文维基的语料库上
首先必须将wikidataProcessing
目录下的csv导入到neo4j中,才能成功运行。运行extractTrainingData.py
后,可以得到train_data.txt
,其内容包含:
entity1entity2statementrelation
得到train_data.txt后,使用dataScrubbing.py处理得到的数据,包括:
错误处理
部分句子有换行,把换行去掉
第一次产生的数据train_data.txt,由于之前程序在切割字符串时出了问题,因此relation这一列不对,这里重新处理一下
选择农业相关的数据
从所有训练集中挑选出与农业有关的数据
运行
python dataScrubbing.py handleError
执行错误管理模块
运行
python dataScrubbing.py selectAgriculturalData
执行数据选择模块
主要代码:
# -*- coding: utf-8 -*-
2
3 # Define here the models for your spider middleware
4 #
5 # See documentation in:
6 # http://doc.scrapy.org/en/latest/topics/spider-middleware.html
7
8 from scrapy import signals
9
10
11 class WikidatacrawlerSpiderMiddleware(object):
12 # Not all methods need to be defined. If a method is not defined,
13 # scrapy acts as if the spider middleware does not modify the
14 # passed objects.
15
16 @classmethod
17 def from_crawler(cls, crawler):
18 # This method is used by Scrapy to create your spiders.
19 s = cls()
20 crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
21 return s
22
23 def process_spider_input(self, response, spider):
24 # Called for each response that goes through the spider
25 # middleware and into the spider.
26
27 # Should return None or raise an exception.
28 return None
29
30 def process_spider_output(self, response, result, spider):
31 # Called with the results returned from the Spider, after
32 # it has processed the response.
33
34 # Must return an iterable of Request, dict or Item objects.
35 for i in result:
36 yield i
37
38 def process_spider_exception(self, response, exception, spider):
39 # Called when a spider or process_spider_input() method
40 # (from other spider middleware) raises an exception.
41
42 # Should return either None or an iterable of Response, dict
43 # or Item objects.
44 pass
45
46 def process_start_requests(self, start_requests, spider):
47 # Called with the start requests of the spider, and works
48 # similarly to the process_spider_output() method, except
49 # that it doesn’t have a response associated.
50
51 # Must return only requests (not items).
52 for r in start_requests:
53 yield r
54
55 def spider_opened(self, spider):
56 spider.logger.info('Spider opened: %s' % spider.name)
import scrapy
2 import time
3 import re
4 from wikidataCrawler.items import WikidatacrawlerItem
5 from scrapy_splash import SplashRequest
6 class relationSpider(scrapy.spiders.Spider):
7 name = "relation"
8 allowed_domains = ["wikidata.org"]
9 start_urls = [
10 "https://www.wikidata.org/wiki/Wikidata:List_of_properties/Summary_table"
11
12 ]
13 def parse(self , response):
14 count = 0
15 rcount = 0
16 relationItem_list = list()
17 relationType = list()
18 link_list = list()
19 rtype_list = list()
20 for headline in response.xpath('//span[contains(@class,"mw-headline")]'):
21 rtype = re.sub("[^A-Za-z]","",headline.xpath('.//text()').extract()[0])
22 rtype_list.append(rtype)
23 table_number_list = [5,7,9,2,14,20,13,5]
24 rcount = 1
25 rrcount = 0
26 rrrcount = 0
27 for table in response.xpath('//table[contains(@class,"wikitable")]'):
28 rsubtype = re.sub("[^A-Za-z\s]","",table.xpath('.//th/text()').extract()[0])
29 for li in table.xpath('.//li/a'):
30 relationId = li.xpath('./small/text()').extract()[0]
31 relationId = re.sub("[^A-Za-z0-9]","",relationId)
32 link = li.xpath('./@href').extract()[0]
33 link = re.sub("[[]\']","",link)
34 link = "https://www.wikidata.org"+link
35 link_list.append(link)
36 rmention = li.xpath('.//text()').extract()[0]
37 rmention = re.sub("[^A-za-z0-9\s]","",rmention)
38 if((rtype_list[rcount] == 'Organization' and rsubtype == 'Generic') or(rtype_list[rcount] == 'Works' and rsubtype == 'Film')):
39 continue
40 tmp =WikidatacrawlerItem()
41 relationItem_list.append(WikidatacrawlerItem())
42 relationItem_list[count]['rid'] = relationId
43 relationItem_list[count]['rtype'] = rtype_list[rcount]
44 relationItem_list[count]['rsubtype'] = rsubtype
45 relationItem_list[count]['link'] = link
46 relationItem_list[count]['rmention'] = rmention
47 yield relationItem_list[count]
48 count+=1
49 rrrcount += 1
50 if(rrrcount == table_number_list[rrcount] ):
51 rrrcount = 0
52 rrcount += 1
53 rcount += 1
54
55 print('number of relation types is %d' %count)
56 splash_args = {
57 'wait': 0.5,
58 }
59 for url in link_list:
60 chrelationItem = WikidatacrawlerItem()
61 request = scrapy.Request(url, callback=self.parse_relation_pages)
62 request.meta['item'] = chrelationItem
63 rid = url.split(":")[2]
64 request.meta['rid'] = rid
65 yield request
66
67
68
69 def parse_relation_pages(self , response):
70 chrelationItem = response.meta['item']
71 chrelationItem['rid'] = response.meta['rid']
72 zh_pattern = re.compile(r'\\\"language\\\":\\\"zh\\\",\\\"value\\\":\\\"(.*?)\\\"')
73 zhhans_pattern = re.compile(r'\\\"language\\\":\\\"zh-hans\\\",\\\"value\\\":\\\"(.*?)\\\"')
74 for script in response.xpath('//script').extract():
75 zh = re.findall(zh_pattern,script)
76 if(len(zh)>0):
77 zh[0] = re.sub(r'\\\\',r'\\',zh[0])
78 chrelationItem['chrmention'] = zh[0].encode('latin-1').decode('unicode_escape')
79 break;
80
81 else:
82 zh_hans = re.findall(zhhans_pattern,script)
83 if(len(zh_hans) > 0 ):
84
85 zh_hans[0] = re.sub(r'\\\\',r'\\',zh_hans[0])
86 chrelationItem['chrmention'] = zh_hans[0].encode('latin-1').decode('unicode_escape')
87 break;
88 else:
89 chrelationItem['chrmention'] = "no chinese label"
90 return chrelationItem
本文暂时没有评论,来添加一个吧(●'◡'●)