【爬蟲-3】將爬下來的 Facebook 粉絲專頁資料存入資料庫 SQLite
本篇延續先前將爬下的 Facebook 貼文儲存為檔案的介紹,將要進一步提到如何將爬下的貼文儲存在資料庫中。不過,首先我們要知道…
為什麼要使用資料庫?
這個問題和「儲存資料要做什麼用呢?」是一體兩面的問題。通常儲存資料是為了以下原因之一:
- 為了備份,不用做查詢,例如:備份貼文
- 為了做頻繁、甚至條件複雜的搜尋,例如:找所有 Like 數>20的貼文
- 為了頻繁的修改、複寫資料,例如:記錄持續增加的按讚人數
這三種狀況分別適合用不同的方式儲存資料。
- 為了備份而不用查詢時,用檔案儲存就可以了(請見前一篇文章)
- 要做頻繁、甚至條件複雜的搜尋時,若用檔案儲存需要一個個打開檔案、讀取檔案、判斷條件,搜尋速度會很慢,用資料庫就可以幫忙處理這類需求。資料庫系統可以幫忙處理搜尋、比對條件的工作,這是用資料庫儲存資料最大的好處。(本篇內容即為說明此狀況)
- 當儲存的資料要做頻繁的修改和複寫時,也必須用到資料庫。當內容頻繁修改時(例如有多隻爬蟲程式同時修改資料庫內容),為了避免遺失更新(Lost Update)、資料不一致(Data Inconsistency)的問題,也可能會存成檔案後,再存進資料庫。(屬於較複雜的狀況,不在本篇討論範圍)
安裝 DB Browser for SQLite
本文將會使用最輕量化的 SQLite 資料庫軟體。SQLite 的好處是我們不用刻意為了學習資料庫功能而再安裝一個資料庫系統,SQLite 預設直接使用檔案的形式在本地端操作資料庫,只要我們使用的語言(包括 Python)支援SQLite,就可以透過 API 操作資料庫。
假設讀者對資料庫還不是很熟悉,在終端機使用 SQL 指令對新手有些困難,那麼我們可以下載任一個支援 SQLite 的視覺化介面來操作,例如 DB Browser for SQLite。
Windows 和 Mac 的朋友都可以到 Windows DB Browser for SQLite 下載;有裝 brew cask 的 Mac 朋友也可以直接打開終端機,輸入:
brew cask install db-browser-for-sqlite
安裝完成後打開 DB Browser for SQLite,出現下面畫面:
建立簡易資料庫,新增資料表並定義資料表的欄位
我們點選左上角的「New Database」新建立一個資料庫,選用 Fanpage_data
做為資料庫名稱。注意資料庫檔案存放的位置,待會程式要使用資料庫時也需要直接從這個位置開啟。建立完資料庫後(其實就是一個檔案),主畫面的長相如下:
一個資料庫裡面可以有很多個資料表(Table),每個資料表存放不同的資料,不同資料表的資料可以透過宣告 主鍵 (Primary) / 外來鍵(Foreign key) 來表達其互相的關係,這會在文章的最後一個部分談到。
我們先建立一個 PostInfo
資料表。在選項 “Fields” 建立我們想要的資料欄位,並指定各資料欄位的 Data type,例如以下:
- postID:因為每個 postID 都不會重複,所以可以做為資料表的主鍵(Primary key),在 PK 框中打勾。雖然 postID 可能都是數字,但並不會用來計算,所以data type 選用 text。
- fanpageName:此欄位不得為空(在 Not 框中打勾),data type 為 text
- content:data type 為 text
- likes:讚數/回覆數/分享數等臉書指標可能會用來計算,將 data type 設為 int
- comments:data type 為 int
- shares:data type 為 int
- createTime:此欄位不得為空(在 Not 框中打勾),data type 為 text
一個簡易資料庫就建立完成了!此時的畫面將如下圖:
以 Python 操作 SQLite 的常用方法
在 Python 中要存取 SQLite 資料庫,要先 import 模組,然後透過 connect
連接資料庫,再透過 execute
執行 SQL 語言(Structured Query Language)來操作資料庫內容。幾個主要的SQLite 方法如下:
import sqlite3conn = sqlite3.connect(db) # 連接資料庫 db
cur = conn.cursor() # 創一個cursor物件
cur.execute(sql[, parameters]) # cursor物件是用來執行sql語言,改變資料庫內容
conn.commit() # 把之前的改變確實反應到資料庫中
conn.close() # 關閉資料庫連線
第五行的 SQL 語言是資料庫和其他應用程式的溝通橋樑,舉凡輸入、刪除、查詢資料庫… 等多種行為都要透過 SQL 語言操作資料庫。資料庫的操作也會在文章的最後一個部分稍微聊聊。目前我們還是先專注在我們的目標,只進行資料的輸入就好。
修改程式,自動將資料存入資料庫
終於來到最後一步了!還記得我們在前一篇文章定義過的三個 function 嗎?
get_all_post()
拿文章本身的資料(文章 id / 內容 / po 文時間)get_activation()
拿文章中粉絲互動的資料(Likes / Comments/ Shares)write_file()
把資料按照自己定義的格式寫進檔案
這次我們不把資料寫進檔案,我們要寫進資料庫。所以get_all_post()
和get_activation()
不用修改,只要把 write_file()
改成 write_sql()
。
我們先上 write_sql()
的 code:
- Line 1 -3:和
write_file()
完全相同 - Line 4:字串
insert into table values ...
是 SQL 語言,這個指令會把values
之後的內容輸入進資料庫。由於每次輸入都要代換不同的資料,所以在value
後放和資料表欄位數相等的?
(此例有 文章 id / 粉專名稱 / 文章內容 / Like數 / Comment數 / Share數 / po 文時間,共7個欄位) - Line 5 -9:每次寫進資料庫一個 post 的所有資料,每次 loop 的 write_out 都會包含一篇 post 的 [postID, fanpageName, content, postLikeCount, postCommentCount, postShareCount, createTime] ,用
cursor.execute()
寫入資料庫。
實際執行,產生資料庫
最後把上次的執行檔案之中相關的 code 稍加修改後(記得建立資料庫連線!),就可以執行看看。下面是從頭到尾的完整 code:
執行完之後請檢查 Fanpage_data.db 資料庫檔案,順利的話就會發現 PyLadies.TW 的粉專內容通通被存進資料庫裡囉!
一點資料庫知識補充
什麼是主鍵 (Primary key)?什麼是外來鍵 (Foreign key)?
每個資料表一定會有其中一個欄位作為主鍵,主鍵欄位裡的值不能重複,用來標示所儲存資料,並確保資料的唯一性。 PostInfo
資料表中,PostID 就是主鍵,可以保證不儲存到兩筆重複的 post。
我們的文章只新增了一個資料表,但資料庫裡通常會有很多資料表。當不同資料表儲存的資料事實上是相同的資料時,為了不要讓資料出現不一致的問題,會宣告主鍵和外來鍵,讓資料庫知道兩者之間的關聯。例如:假設除了我們剛建立的 PostInfo
資料表之外,我們又另外建立 FanpageInfo
資料表,兩個資料表分別儲存如下資料:
PostInfo
資料表的主鍵是 PostID,FanpageInfo
資料表的主鍵是 FanpageID。PostInfo
資料表和FanpageInfo
資料表都有 FanpageID 的欄位,此時我們會宣告PostInfo
資料表的 FanpageID 為外來鍵,讓資料庫知道兩者之間的關聯。
用 SQL (Structured Query Languages) 語言查詢資料
前面提到將資料存入資料庫的好處是能夠做頻繁、甚至條件複雜的搜尋,例如:找所有 Like 數>20的貼文。我們現在就用 SQLite 試試!
輸入「找所有 Like 數>20的貼文」的 SQL 語言指令之後按下執行鍵:SELECT content, likes FROM PostInfo WHERE likes > 20
資料庫就會把符合查詢條件的結果吐回來了!
其他的 SQL 語言學習資源
當然,要查到想要的資料需要下正確的 SQL 語言查詢條件,而 SQL 語言並不能在這篇文章裡談完 ಠ_ಠ 。建議大家看看 SQL 語言的教學文章,例如:MySQL 超新手入門(1)重新開始。
雖然 MySQL 和SQLite 是不同的資料庫軟體,但由於 SQL 語言是關聯式資料庫的一種標準,因此不同的資料庫軟體之間仍然能夠通用。
最後,祝大家與自己的爬蟲程式和資料庫相處愉快!