Neo4j入門之中國電影票房排行淺析
什麼是Neo4j?
Neo4j是一個高效能的NoSQL圖形資料庫(Graph Database),它將結構化資料儲存在網路上而不是表中。它是一個嵌入式的、基於磁碟的、具備完全的事務特性的Java持久化引擎,但是它將結構化資料儲存在網路(從數學角度叫做圖)上而不是表中。Neo4j也可以被看作是一個高效能的圖引擎,該引擎具有成熟資料庫的所有特性。
作為圖形資料庫,Neo4j最讓人驚喜的功能就是它可以直觀地展示圖,也就是節點與節點之間的關係,當然,它還有其它的優勢,比如:
- 很容易表示連線的資料;
- 檢索/遍歷/導航更多的連線資料是非常容易和快速的;
- 能輕鬆地表示半結構化資料;
- Neo4j CQL查詢語言命令類似於SQL,可讀性好,容易學習;
- 使用簡單而強大的資料模型;
- 不需要複雜的連線來檢索連線的/相關的資料。
在本文中,筆者希望能夠通過一個簡單的例子來展示Neo4j的使用以及它的強大之處,這無疑更適合於初學者,因為筆者也是剛入門。
以下,筆者並不會過多地介紹Neo4j的操作,只是希望讀者能對Neo4j的功能有直觀的感受,至於教程之類的,可以參考文章最後的參考文獻。
下面,讓我們進入本次的Neo4j之旅~
專案展示
由於《流浪地球》的大熱以及筆者對此的欣賞,因此,此次的專案為分析中國電影票房排行。我們的資料來自於百度百科,用 爬蟲
得到我們需要的資料,主要是電影的相關資訊,如排名,票房,上映日期等,以及電影的主演。將資料儲存為CSV檔案,並匯入至Neo4j,最後得到電影的簡單分析及視覺化。
整個專案主要是以下三步:
- 資料獲取:利用爬蟲實現;
- 資料匯入:利用Py2neo實現;
- 資料展示:利用Neo4j實現。
其中,Py2neo為Neo4j的Python介面。
整個專案的結構如下圖:
接下來,筆者將詳細地介紹每一步的操作及程式碼,let's go ~
資料獲取
資料的獲取始終是一個重要的問題,好在我們有爬蟲這個工具。為了能夠展示中國電影票房排行中的電影資訊以及演員與電影的關係,首先的重要一步就是獲取我們需要的需要。
我們需要兩份資料。第一份資料,就是中國電影票房排行資料,網址為: https://baike.baidu.com/item/... ,頁面如下:
我們製作爬蟲,將這個表格爬取下來,並將表格的第一行(欄位名稱)作為電影的相關資訊,然後儲存為movies.csv。整個過程的Python程式碼(movie.py)如下:(因為這是公開資料,這個爬蟲是合理的。)
# -*- coding: utf-8 -*- import requests import pandas as pd from bs4 import BeautifulSoup url = "https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD%E7%94%B5%E5%BD%B1%E7%A5%A8%E6%88%BF/4101787" # 請求頭部 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'} r = requests.get(url, headers=headers) soup = BeautifulSoup(r.text.encode('ISO-8859-1'),'lxml') table = soup.find('table')('tr') movies = [] for line in table[1:]: movie = {'rank': int(line('td')[0].text), 'src': line('td')[1]('a')[0]['href'], 'name': line('td')[1].text, 'box_office': line('td')[2].text, 'avg_price': int(line('td')[3].text), 'avg_people': int(line('td')[4].text), 'begin_date': line('td')[5].text.strip(), } # print(movie) movies.append(movie) # print(movies) df = pd.DataFrame({'rank': [movie['rank'] for movie in movies], 'src': [movie['src'] for movie in movies], 'name': [movie['name'] for movie in movies], 'box_office': [movie['box_office'] for movie in movies], 'avg_price': [movie['avg_price'] for movie in movies], 'avg_people': [movie['avg_people'] for movie in movies], 'begin_date': [movie['begin_date'] for movie in movies] }) # print(df.head()) df.to_csv(r'./movies.csv', index=False)
movies.csv中的資料如下:
OK,第二份資料,每部電影(共20部)的主演,以《流浪地球》為例,網址為: https://baike.baidu.com/item/... ,頁面如下:
我們只需要爬取每部電影的主演就夠了,也就是上圖中的紅色部分,實現的Python程式碼(actor.py)如下:
# -*- coding: utf-8 -*- import requests import pandas as pd from bs4 import BeautifulSoup def get_actors(src): url = "https://baike.baidu.com"+src # 請求頭部 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'} r = requests.get(url, headers=headers) soup = BeautifulSoup(r.text.encode('ISO-8859-1'),'lxml') names = soup.find_all('dt', class_="basicInfo-item name") values = soup.find_all('dd', class_="basicInfo-item value") actors = [] for name, value in zip(names, values): # print(name.text) if '主' in name.text and '演' in name.text: # print(name.text.replace('', ''), value.text) actors = value.text.strip().split(',') actors = [actor.strip().replace('\xa0', '').replace('\n[6]', '') for actor in actors if actor] # print(actors) return ','.join(actors) df = pd.read_csv('./movies.csv') actors_list = [] for name, src in zip(list(df['name']), list(df['src'])): actors = get_actors(src) # print(name, actors) actors_list.append(actors) new_df = pd.DataFrame({'actors': actors_list}) new_df['name'] = df['name'] # print(new_df) new_df.to_csv(r'./actors.csv', index=False)
生成的actor.csv資料如下:
OK,資料收集的任務就到此完成了,有了爬蟲,輕鬆搞定資料難題。
匯入資料
接著,我們需要用到剛才儲存的movies.csv和actor.csv,利用Py2neo來將資料匯入至Neo4j中。
首先,需要確保你的電腦已安裝好Neo4j,Py2neo,並開啟了Neo4j服務,具體的安裝流程可參考網址: https://www.w3cschool.cn/neo4... 。
利用Py2neo,我們就可以用Python輕鬆地實現將資料匯入至Neo4j,實現的功能為:建立電影節點以及演員節點,並建立兩者之間的關係,關係名稱為“ACT_IN”。實現的Python程式碼()如下:
# -*- coding: utf-8 -*- import pandas as pd from py2neo import Graph, Node, Relationship, NodeMatcher # 讀取csv檔案 movies_df = pd.read_csv(r'./movies.csv') actors_df = pd.read_csv(r'./actors.csv') # 連線Neo4j服務 graph = Graph(host="localhost://7474", auth=("neo4j", "jc147369")) # 建立電影節 for i in range(movies_df.shape[0]): rank = str(movies_df.iloc[i, :]['rank']) name = movies_df.iloc[i, :]['name'] box_office = movies_df.iloc[i, :]['box_office'] avg_price = str(movies_df.iloc[i, :]['avg_price']) avg_people = str(movies_df.iloc[i, :]['avg_people']) begin_date = movies_df.iloc[i, :]['begin_date'] node = Node("Movie", name=name, rank=rank, box_office=box_office, avg_price=avg_price, avg_people=avg_people, begin_date=begin_date ) # print(movies_df.iloc[i, :]['rank']) graph.create(node) print('create movie nodes successfully!') # 建立演員節點 all_actors = set() for i in range(actors_df.shape[0]): actor_list = actors_df.iloc[i, :]['actors'].split(',') for actor in actor_list: all_actors.add(actor) for actor in all_actors: node = Node("Actor", name=actor) graph.create(node) print('create actor nodes successfully!') # 建立演員——電影關係 for i in range(actors_df.shape[0]): name = actors_df.iloc[i, :]['name'] matcher = NodeMatcher(graph) movie_node = matcher.match("Movie", name=name).first() actors = actors_df.iloc[i, :]['actors'].split(',') # print(name, actors) for actor in actors: actor_node = matcher.match("Actor", name=actor).first() relationship = Relationship(actor_node, 'ACT_IN', movie_node) graph.create(relationship) print('create relationships successfully!') print('You can check Neo4j now!')
只要你的電腦已安裝好Neo4j,Py2neo,並開啟了Neo4j服務,不出意外,那麼你的Neo4j已經默默地儲存了這些資料,而它們將會給出帶來巨大的驚喜:天吶,這竟然是個資料庫!
在瀏覽器中輸入“localhost:7474”,並點選左上方的資料庫圖示,就能看到下圖:
可以看到,在Neo4j中,我們建立了142個節點,分為兩類:Movie和Actor,以及136對關係,關係名稱為ACT_IN. 當然,這些都是枯燥的,那麼我們來看看資料展示這步吧,它會給我們帶來什麼驚喜?
資料展示
好不容易到了資料展示這一步,之前的努力都不會白費!
在Neo4j的前端頁面(也就是網址: http://localhost :7474)中的命令列中輸入命令:
MATCH (Movie) RETURN (Movie);
執行命令後,很快就能得到整個圖(包含電影節點、演員節點以及關係)的視覺化展示,由於影象過大,不能看清細節,因此,就區域性放大來看,同時得到一些簡單的分析。
首先是圖一,吳京主演的電影。
在中國電影票房排行榜的前20名中,吳京主演了《戰狼2》與《流浪地球》,且兩者沒有其他更多的相同主演。
接著是圖二,沈騰主演的電影。
在中國電影票房排行榜的前20名中,沈騰主演了《西虹市首富》、《瘋狂的外星人》以及《羞羞的鐵拳》,這顯示了沈騰的票房號召力(他也是筆者喜歡的喜劇演員),不過開心麻花團隊的其他成員在這三部電影的拍攝中應該見不到面。
接著是圖三,《捉妖記》及《捉妖記2》。
捉妖記系列電影無疑是成功的,兩部電影都進了票房的前20,他們的共同主演就多了,有曾志偉,吳君如,井柏然,白百何。
接著是圖四,主要是看看Neo4j幫我們挖掘了哪些潛在的關係。
從《唐人街探案2》到《捉妖記2》,這個不算長的鏈條給了我們一些驚喜,原來,劉昊然可以通過尚語賢再通過曾志偉認識李宇春,一個very interesting的分析。當然,這個是筆者的分析,他倆到底認不認識筆者是不知道滴~
分析到此結束,如果讀者想看原圖,可以參看該專案的github地址: https://github.com/percent4/N... 。
總結
感謝讀者不厭其煩地看到了這裡,看完了這篇“又臭又長”的文章,好在圖比較多,應該能稍微減輕點閱讀的壓力。
再說說該專案的不足之處:那就是Neo4j的操作語法展示的比較少,約等於沒有,這主要是筆者也是初入門,不熟。如果後續對Neo4j的操作語法CQL熟練了,我們就能能到更多有趣的結果,而不是這麼一句簡單的分析(有敷衍的嫌疑)。
最後,對此專案感興趣的讀者,可以移步該專案的github地址: https://github.com/percent4/N... 。
注意:不妨瞭解下筆者的微信公眾號: Python爬蟲與演算法(微訊號為:easy_web_scrape), 歡迎大家關注~
參考文獻
- Neo4j_百度百科: https://baike.baidu.com/item/...
- neo4j教程: https://www.w3cschool.cn/neo4...
- The Py2neo v3 Handbook : https://py2neo.org/v3/index.html
- Neo4j簡介及Py2Neo的用法: https://cuiqingcai.com/4778.html