日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python 知识图谱demo_古诗词知识图谱Demo

發(fā)布時間:2024/1/1 python 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python 知识图谱demo_古诗词知识图谱Demo 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

早前調(diào)研了知識圖譜的基礎(chǔ)概念和技術(shù)框架,最近這兩個月倒騰了一個古詩詞的圖譜demo,僅以此文記錄一下實驗過程。從零開始做這個Demo,整個過程大致分為三大步驟:數(shù)據(jù)采集,數(shù)據(jù)存儲以及圖譜應(yīng)用,全文將按這三步進(jìn)行記錄。

一、數(shù)據(jù)采集

既然是從零開始,那第一步就是要爬取數(shù)據(jù)。搜了幾個詩詞網(wǎng)站,對比網(wǎng)頁排版結(jié)構(gòu)和內(nèi)容豐富程度,個人覺得古詩詞網(wǎng)是個不錯的選擇,在這里感謝站長為經(jīng)典文化傳承作出的貢獻(xiàn)。

1. 網(wǎng)頁分析

F12打開詩詞列表頁的源碼,查看頭部信息如下圖:

請求的url格式固定,只有頁碼改變;請求的類型為get。

多看幾個頁面,可以發(fā)現(xiàn)請求頭中Cookie的hm_lvt和hm_lpvt為兩個時間戳,不同頁面只有hml_pvt發(fā)生改變;old_url取值為當(dāng)前頁碼;Referer也是隨頁碼改變的固定格式url。

請求列表頁面的返回結(jié)果為json列表,可以非常方便地提取需要的信息,而不用去html中定位并解析目標(biāo)元素,省去了爬蟲中的一半工作量:

每一個json對應(yīng)一首詩詞,包含標(biāo)題、正文、作者、朝代、標(biāo)簽、體裁、作者介紹、譯注、賞析等信息,這種結(jié)構(gòu)化的數(shù)據(jù),也免去了數(shù)據(jù)抽取和整理的很多工作。

2. 爬蟲代碼

這一類網(wǎng)站廣告很少,也沒有收費業(yè)務(wù),帶有公益性質(zhì),網(wǎng)站服務(wù)器一般也扛不住爬蟲的壓力,常常會采取一些反爬措施,比如封禁IP。為了爬取這些網(wǎng)站,一方面要降低爬取速度;另一方面要維護代理池,在被封的時候更換IP。爬取過程中及時保存爬蟲結(jié)果,并記錄爬取失敗的頁面,方便以后再重爬。

def crawl_pages(page_list, save_path, ip_pool, retry_times=5):

fail_list = list()

lvt_code = int(time.time())

ip = random.choice(ip_pool)

for page in page_list:

time.sleep(3 * random.random())

lpvt_code = int(time.time())

page_url = 'https://www.gushici.com/poetry_list?page={0}'.format(page)

referer = 'https://www.gushici.com/p_{0}'.format(page)

headers = {'Host': 'www.gushici.com',

'Connection': 'keep-alive',

'Accept': '*/*',

'X-Requested-With': 'XMLHttpRequest',

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',

'Referer': referer,

'Accept-Encoding': 'gzip, deflate, br',

'Accept-Language': 'zh-CN,zh;q=0.9',

'Cookie': 'JSESSIONID=48304F9E8D55A9F2F8ACC14B7EC5A02D; Hm_lvt_98209c07e81fcbdd5f79bd9e94c617eb={0}; Hm_lpvt_98209c07e81fcbdd5f79bd9e94c617eb={1};\old_url=/p_{2}'.format(lvt_code, lpvt_code, page)}

times = 0

flag = False

while times <= retry_times:

times += 1

try:

response = requests.get(page_url, headers=headers, proxies={'https': ip}, verify=False, timeout=10)

flag = True

with open(os.path.join(save_path, page), 'w', encoding='utf-8') as f:

json.dump(response.text, f, ensure_ascii=False)

break

except:

ip = random.choice(ip_pool)

if not flag:

fail_list.append(page)

with open(os.path.join(save_path, 'fail'), 'w', encoding='utf-8') as f:

json.dump(fail_list, f, ensure_ascii=False)

二、數(shù)據(jù)存儲

知識圖譜的數(shù)據(jù)層有多重存儲方式,本文選擇采用Neo4j搭建。Noe4j是一個高性能的輕量級圖形數(shù)據(jù)庫,應(yīng)對小型知識圖譜綽綽有余。雖然關(guān)系型數(shù)據(jù)庫通過多重join也可以實現(xiàn)數(shù)據(jù)間的復(fù)雜關(guān)系查詢,但是多表數(shù)據(jù)join然后過濾篩選導(dǎo)致性能會非常差,而圖數(shù)據(jù)庫很好地解決這樣的問題,它只用遍歷相關(guān)節(jié)點,不用操作全量數(shù)據(jù),性能會大大提升。

1. Neo4j安裝

從Neo4j官網(wǎng)可以下載開源的Noe4j社區(qū)版,解壓到D盤,然后配置環(huán)境變量。注意:Neo4j依賴于Java運行環(huán)境,安裝Neo4j前請檢查本機是否安裝Java并配置Java的環(huán)境變量。

打開cmd命令行窗口,進(jìn)入安裝目錄下的bin文件夾,執(zhí)行“neo4j install-service”命令,安裝Neo4j服務(wù)。然后執(zhí)行“neo4j start”命令,啟動Neo4j的服務(wù)。

2. Neo4j操作

Neo4j是一種圖數(shù)據(jù)庫,其中并沒有數(shù)據(jù)表的概念,只包含節(jié)點和邊,節(jié)點表示實體,邊表示實體間的關(guān)系(分為有向關(guān)系和無向關(guān)系),節(jié)點和邊可以包含鍵值對表示的屬性。

用慣了關(guān)系型數(shù)據(jù)庫,初次接觸圖數(shù)據(jù)庫感覺有點別扭。為了便于自己理解,我是這樣來類比的:

節(jié)點和關(guān)系是圖數(shù)據(jù)庫中定義的最原始的兩個基類,節(jié)點(關(guān)系)的標(biāo)簽表示由節(jié)點(關(guān)系)基類派生出來的一類節(jié)點(關(guān)系)。當(dāng)導(dǎo)入數(shù)據(jù)之后,具有屬性值的某一個節(jié)點(關(guān)系),就是該標(biāo)簽對應(yīng)的節(jié)點(關(guān)系)派生類所生成的實例。

(1)導(dǎo)入數(shù)據(jù)

Neo4j提供import命令,可以批量導(dǎo)入csv格式的數(shù)據(jù)。針對json格式的數(shù)據(jù),可以轉(zhuǎn)為csv格式,然后用import導(dǎo)入,注意json中的雙引號需要進(jìn)行轉(zhuǎn)義。為避免格式轉(zhuǎn)換過程中的錯誤,可以調(diào)用apoc函數(shù)庫中的json導(dǎo)入工具。

apoc的安裝方式為:從github中下載apoc的jar包,將jar包復(fù)制到Neo4j安裝目錄的plugins路徑下,在neo4j.conf中配置apoc.import.file.enabled=true,表示允許apoc導(dǎo)入文件。重啟Neo4j服務(wù),調(diào)用apoc.load.json即可導(dǎo)入json數(shù)據(jù)。

(2)查詢數(shù)據(jù)

Neo4j的查詢語言為Cypher(第一眼看成了Cython,然而這兩個半點不沾邊)。官網(wǎng)有完整版的Cypher手冊,本文只挑選最基礎(chǔ)的幾個語句簡要介紹。

A.增:

新建節(jié)點: CREATE (node: NodeType {AttributeKey : AttributeValue})

// 創(chuàng)建一個姓名為Jack的Person類的節(jié)點,并返回該節(jié)點

CREATE (a:Person {name:"Jack"})

RETURN a

新建關(guān)系:不能單獨創(chuàng)建關(guān)系,必須指明關(guān)系的起始節(jié)點和終止節(jié)點。--表示無向關(guān)系,->和 EndNode

// 創(chuàng)建兩個Person之間Knows的關(guān)系,并返回節(jié)點和關(guān)系

CREATE (a:Person)-[k:KNOWS]-(b:Person)

RETURN a, k, b

B.查:

查詢節(jié)點:MATCH (node: NodeType {AttributeKey : AttributeValue}) WHERE node.AttributeKey = AttributeValue

// 查詢1970年后出生的Person節(jié)點,并返回節(jié)點

MATCH (n)

WHERE n.born > 1970

RETURN n;

查詢關(guān)系:MATCH StartNode - (relationship: RelationshipType {AttributeKey : AttributeValue}) -> EndNode WHERE relationship.AttributeKey = AttributeValue

// 查詢自從2015年起居住(LIVES_IN)在NewYork城市(City)的名叫Mike的人(Person),并返回節(jié)點和關(guān)系

MATCH (p:Person {name:"Michel"})-[s:LIVES_IN]->(c:City {name:"NewYork"})

WHERE s.since = 2015

RETURN p,s,c

C.改:

修改屬性:MATCH (variable : NodeType|RelationshipType) SET variable = {AttributeKey : AttributeValue}

// 查詢名為Jack的Person類節(jié)點,并將名字改為Michel,年齡改為23

MATCH (p:Person)

WHERE p.name = "Jack"

SET p = {name: "Michel", age: 23}

D.刪:

刪除節(jié)點:與該節(jié)點相關(guān)的關(guān)系也需要刪除。MATCH (node) - [relationship] - () DELETE node, relationship

// 刪除名為Jack的Person節(jié)點及關(guān)聯(lián)關(guān)系

MATCH (p:Person)-[relationship]-()

WHERE p.name = "Jack"

DELETE relationship, p

刪除屬性:MATCH (node) - [relationship] - () REMOVE node.AttributeKey, relationship.AttributeKey

// 刪除名為Michel的Person節(jié)點的年齡屬性

MATCH (p:Person)

WHERE p.name = "Michel"

REMOVE p.age

3. Neo4j實踐

本文設(shè)計的知識圖譜包含三類節(jié)點:詩詞(Poem)、作者(Author)、標(biāo)簽(Tag)。作者與詩詞是寫作(WRITE)的關(guān)系,詩詞、作者與標(biāo)簽是標(biāo)識(LABEL)關(guān)系。

// 在三類節(jié)點上創(chuàng)建索引

create index on :Poem(uuid);

create index on :Author(name);

create index on :Tag(tag);

// 將數(shù)據(jù)導(dǎo)入Neo4j

call apoc.periodic.iterate('call apoc.load.json("web_file_poetry.json") yield value as poem',

'merge (p:Poem{uuid: poem.poem_id})

set p.title = poem.title, p.content=poem.poem, p.tag=poem.tag, p.appreciation=poem.appreciation, p.background=poem.background

// 作者節(jié)點

merge (a:Author{name: poem.poet, dynasty: poem.dynasty})

// 作者到詩詞的關(guān)系

merge (a)-[r1:WRITE]->(p)',

{batchSize:100000, iterateList:true, parallel:true});

// 建立詩詞、作者與標(biāo)簽之間的關(guān)系

match (a:Author)-[:WRITE]->(p:Poem)

where p.tag <> ''

unwind split(trim(p.tag), ",") as tag

// 標(biāo)簽節(jié)點

merge (t:Tag{tag: tag})

// 詩詞到標(biāo)簽的關(guān)系

merge (p)-[r1:LABEL]->(t)

// 作者到標(biāo)簽的關(guān)系

merge (a)-[r2:LABEL]->(t);

圖數(shù)據(jù)庫創(chuàng)建成功之后,可以查詢看看效果,Neo4j的可視化做的還是挺好看的。

三、圖譜應(yīng)用

知識問答是基于知識圖譜的一項應(yīng)用,前沿的問答系統(tǒng)多采用深度學(xué)習(xí)、自然語言處理等技術(shù)。本文采用最簡單的正則匹配( ̄▽ ̄)~*

1.首先,定義可以回答的問題類型:

查找詩詞的正則:

source_list = [

'[\"\'“‘《]?(?:是|出自|來[自|源])(?:哪[首|篇|個|里|兒|]?|什么)的?(?:[詩詞][文句]?|文章|句子)?',

'[\"\'“‘《]?的(?:來源|出處|(?:整[首|篇]|完整|全)[詩詞文])',

'(?:含有?|包[含括])[\"\'“‘《]?的(?:[詩詞][文句]?|文章|句子)'

]

source_list = list(map(re.compile, source_list))

查找作者的正則:

author_list = [

'[\"\'“‘《]?的(?:作者|[詩詞]人)',

'[\"\'“‘《]?是(?:誰|哪[位個])(?:作者|[詩詞]人)?',

]

author_list = list(map(re.compile, author_list))

查找標(biāo)簽的正則:

tag_list = [

'(?:寫|描[寫繪述]|表達(dá))(\S+?)的?[詩詞]',

]

tag_list = list(map(re.compile, tag_list))

整合問題正則:

rules = {

'source': source_list,

'author': author_list,

'tag': tag_list,

}

2.其次,根據(jù)正則判斷問題類型,并提取問題中的要素

def match(question):

match_result = None

for mode, temp_list in rules.items():

for temp in temp_list:

text = temp.findall(question)

if text:

match_result = (mode, text[0])

break

return match_result

3.最后,從Neo4j中根據(jù)問題要素查找并返回問題答案

def parse(question):

match_result = match(question)

if match_result is None:

return None

mode, text = match_result

res = None

cql = None

if mode == 'source':

cql = 'match (p:Poem) where p.content contains "{0}" return p.title, p.content'.format(text)

elif mode == 'author':

cql = 'match (a:Author)-[:WRITE]->(p:Poem) where p.content contains "{0}" return a.name'.format(text)

elif mode == 'tag':

tag_list = jieba.lcut(text)

cql = 'match (p:Poem)-[:LABEL]->(t:Tag) where t.tag in {0} return p.content, t.tag'.format(tag_list)

else:

pass

if cql:

res = neo4j_graph.run(cql).to_data_frame()

return res

后記:

本文只是搭建了一個非常小的圖譜demo,后續(xù)還有很多地方需要完善,如有遺漏或錯誤,請大家不吝指出,歡迎交流。

總結(jié)

以上是生活随笔為你收集整理的python 知识图谱demo_古诗词知识图谱Demo的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。