【爬蟲】用Facebook API搜集粉絲專頁資訊

Patzie!
PyLadies Taiwan
Published in
15 min readOct 11, 2017

前言

這系列的文章是想寫給曾發下宏願,要兩個月上完一系列E-Learning的Python初學者課程(e.g. Coursera, Udemy, Codecademy…都算),卻不幸在兩週後併發懶惰症的程式超新手們看的。

這樣的前提假設也就代表你的電腦裡已經安裝了Python 3(文章以Python 3的編寫為主)和某種IDE開發環境(e.g. Jupyter Notebook, Sublime Text, Atom…任一種),大腦裡也安裝了Python的基本資料型態(也就是String, List, Dictionary)、Function以及Loop的概念,不過還沒用它們來做什麼有趣的事情。

Python是個相對容易上手的程式語言,有許多容易使用的套件,所以即使不知道網路的原理、HTTP協定有哪些請求方法,還是可以直接使用套件把網路的資料拿下來做有趣的事。這也是改變學習曲線的一種方法:

先會一點工具 → 用它進行有趣的小任務 → 獲得學習更多東西的動機

本篇會用Python提供的工具,爬取Facebook上的資料,以供未來做資料分析應用使用。以下列出將提到的小節:

  • Facebook Graph API 提供的功能
  • Facebook Graph API Explorer:預覽要用的資訊參數
  • 怎麼自動拿資料?兩種拿到資料(JSON格式)的方式: 1. Facebook SDK for Python 2.從URL爬 Graph API response,用到套件:requests, json
  • 簡單談談Python 的中文編碼問題

【任務一:爬蟲】用Facebook Graph API搜集粉絲專頁資訊

Facebook Graph API 提供的功能

API(Application Programming Interface)就是把應用程式裡面的一些可以開放給外部人士使用的物件寫成Function,讓外部人士可以透過這個程式介面來操作應用程式或者獲取程式中的資料,而不會不小心改寫或影響應用程式本身。像Facebook Graph API可以讓開發者取出資料,也可以發文、按讚、上傳照片等平常用Web和行動介面就可以進行的事。

不過在這篇文章,我們只會練習取出Facebook粉絲專頁的資訊。(當然如果你想取出人的資訊、活動的資訊,而且你剛好也有權限,那麼也是可以比照辦理的。)

Facebook的世界裡有三個元素:Nodes, Edges 和 Fields,如果把Facebook的世界具象化,會長成這樣:

Resource: http://caseorganic.com/2010/12/tools-for-visualizing-facebook-social-graphs/

藍色點點(Node)代表Facebook上的任何東西,例如人、粉專、一張照片、一則文章;密密麻麻的線條(Edge)則是Node之間的互動,例如人和人成為朋友、人到粉專上留言、人對相片按讚;而Field沒有出現在圖上,Field是Node的資訊,例如人的生日、粉專的成立日期等等。

Facebook Graph API Explorer:預覽要用的資訊參數

知道Facebook的世界觀之後,就要下對參數來拿到想要的資料。要找到正確的參數,Facebook Graph API Explorer是個好工具。(補充:Facebook一共開放了Graph API, Atlas API, Instagram API, Marketing API 四種API)

創完帳號後,最上面會看到自己的Access Token,這是你連上自己的 Facebook帳戶 的鑰匙,在下一個部分會用。(Token會過期,如果過一段時間不能用了,就重新整理網頁再拿下一個Token就好)

每個Node都有ID和名字。假設今天想拿PyLadies Taiwan粉絲專頁的資料,就在Access Token下面的瀏覽列裡,在 “ ? “ 前貼上粉專的名字或ID。就可以在左方的Field欄裡選取各種Field。Submit後,右邊大方格會出現JSON格式的資料,可以用來測試要下什麼Field才會拿到想要的資料。

Facebook Graph API Explorer image (1)

Field的說明可以在官方文件Graph API document裡查,不過直接試比較快。拿資料的時候要注意Node的設定:如果要拿的是粉絲專頁過去發了哪些貼文,Node就是Page的ID、Field是”posts”;如果要拿的是粉專某一篇貼文下面的回應,Node就是該篇文章的ID、Field”是comments”。

因為Facebook的資料量太大,不管拿什麼東西都有預設每次吐回來的最高資料量,有種改善方式是在Field的最後面加上 .limit( )。以粉專文章為例,預設是每次只會吐回10篇,”posts” 後面加上 “.limit(100)” 可以吐100篇,不過還是有極限,達到上限的話只能重run一次Graph API,要求拿下100篇文章。

Facebook Graph API很好心,JSON格式的資料裡就有下100篇文章的位置。

Facebook Graph API Explorer image (2):PyLadies沒有那麼多文章,我們拿個10篇就好

怎麼自動拿資料? — 1. 套件Facebook SDK Python

現在已經知道要下什麼參數了,但要怎麼自動拿回資料呢?第一種方式是使用 Facebook SDK for Python

首先要先下載Facebook SDK Python這個套件。

> pip install facebook-sdk

(已經安裝過pip的人才能直接執行上面這步。如果沒安裝過pip也不用緊張,請google “pip 套件管理工具”。 第一次安裝pip有點麻煩,但是先安裝pip,才能安裝facebook-sdk,還有許許多多以後要用到的套件唷)

# Python 3.4
# -*- coding: utf-8 -*-
# OS: OS 10
# IDE: Sublime Text
# facebook-sdk version 2.0.0
import facebook

Facebook 的官方網站上有他們的Official SDK (Software Development Kit),同時上面也有一些第三方套件,不過Python的SDK沒有被列在官方網站上。Python的SDK可以在網站上加入Facebook插件、登入Facebook和做Graph API call。

我們只想練習拿粉專裡一些資訊,首先來取得PyLadies Taiwan的基本資料~

token = 'EAACE...' #貼上前面拿到的Access token
graph = facebook.GraphAPI(access_token = token) #套件會用你的token連接到facebook
fanpage_info = graph.get_object('pyladies.tw', field = 'id') #指定拿pyladies.tw 這個粉專的id和讚數print(fanpage_info) #印出來看看長什麼樣子,會是JSON格式的資料
print("Fanpage id = ", fanpage_info['id'])

執行之後會拿到JSON格式的資料。接下來看看怎麼拿到粉專裡的貼文:

posts = graph.get_connections(id = '1759525920983198', connection_name = 'posts', summary = True)
print(posts) #這行會印出一大堆貼文的資料
print ("共有", len(posts), "篇PO文")

(再次強調拿資料的時候要注意Node的設定:如果要拿的是粉絲專頁過去發了哪些文章,Node就是Page的ID、Field是”posts”;如果要拿的是粉專某一篇文章下面的回應,Node就是該篇文章的ID、Field是”comments”。)

印出posts之後會看到一大串JSON格式的資料,而且原本的中文部分變成了Unicode,暫時先不管他,我們在文章的最後一部分會討論這該怎麼辦。(JSON格式的資料很長一串,如果要看它的結構來擷取想要的資料,可以使用輔助工具:http://json.parser.online.fr/

但這個套件有個缺點:他有很多Field名稱跟Facebook Graph API不一樣、或是不能用。本來只要下剛剛我們在Graph API Explorer查到的參數,就可以拿到資料了,但有些參數Facebook SDK Python就是拿不到。例如貼文留言下面的回覆,Explorer顯示 fields=comments{comments}可以拿到,SDK卻一直噴Error。

怎麼自動拿資料? — 2.用requests套件爬Graph API response

既然有些資料沒辦法直接call Facebook SDK Python的 Function取得,我們還有一招:直接開網站來拿Graph API response。直接在瀏覽器開以下的網址,然後把粗體的部分置換成前面用過的資料:https://graph.facebook.com/v2.10/ NodeID?fields=comments{comments}&access_token=yourAccessToken

就會看到下好參數的資料以JSON格式出現在網頁上了。網址裡的版本用最新的就可以(目前最新的版本是v2.10,關於版本的差異請看這裡)。

接下來我們要用requests套件把它爬下來。你可以開一個新的Python文件來比較兩種方式的不同:

import requests
import json
url = 'https://graph.facebook.com/v2.9/1759525920983198?fields=id,posts&access_token=yourAccessToken'

request這個套件會代表你的電腦(Client端)向遠方的某台機器(Server端)送出請求,要求某些資源或資料。關於requests和其他的爬蟲方法,PyLadies Taiwan在2016年的爬蟲系列活動曾經提到過,詳情請參考:從0到1的網頁爬蟲 — 02投影片

把這些要求來的資料存在responseresponse是一個已經被打包的物件,用.text拆開之後,再用Python內建的json套件把response裡的東西load出來。

response = requests.get(url)
html = json.loads(response.text)

現在我們想要的資料全都存在html裡了!試著把它印出來…

message = html['posts']['data'][0]['message']
print(message.encode('utf-8'))

執行之後,會得到這樣的結果:

[Python 入門-03][Flask Web 開發實戰-02]除了整系列報名外,Meetup 報名的管道還是在喔!
要報名的請記得準時開搶!
Python 入門 Event 3
...(以下略)

簡單談談Python的中文編碼問題

前面講到Facebook SDK Python印出posts,還有response時,都避開了常常令人頭痛的中文編碼問題,這裏簡單提一下。(關於編碼的問題,PyLadies Taiwan的爬蟲系列活動也曾經提到過,詳情請參考:從0到1的網頁爬蟲 — 04投影片。)

關於中文編碼,一定要認識的編碼系統有Unicode, Big5, UTF-8。Unicode以拉丁字母為基礎,Big5和UTF-8則是能表達中文的文字編碼系統。他們之間的關係是這樣子的:

Image resource: 淺談 python2及 python3處理中文字串編碼的差異

不管是從Facebook SDK Python、或者Graph API response,我們拿回來的資料都是Unicode的型態,必須encode為UTF-8才會變成中文。在Python 3,如果拿到的資料是字串,那麼只要str.encode()就可以解決。但是如果拿回來的是字典,就必須從字典中拿出字串,才能進行解碼。

另外,有些套件裡的Function也會自動提供解碼的功能,碰到解碼上的困難時,不妨回去查查官方文件。以前面的 request 套件和 json 套件為例,那兩行code之間其實有這樣的小插曲(請注意#標註的部分):

response = requests.get(url)  # Python 3的request會自動decode為Unicode
html = json.loads(response.text)
# '.text',會根據網頁的編碼方式encode,回到UTF-8
# 但因為response是json format而不是string,沒辦法encode
# 所以必須用json.loads 把string拿出來,才能encode

…這個故事告訴我們,編碼魔鬼真的都藏在細節裡呢。下次碰到編碼問題時,不妨回去查查所用套件的文件囉。

這裏集合了上面所有的Code:

  • Facebook SDK for Python
# Python 3.4
# -*- coding: utf-8 -*-
# OS: OS 10
# IDE: Sublime Text
# facebook-sdk version 2.0.0
import facebook
token = 'EAACE...' #貼上前面拿到的Access token
graph = facebook.GraphAPI(access_token = token) #套件會用你的token連接到facebook
fanpage_info = graph.get_object('pyladies.tw', field = 'id') #指定拿pyladies.tw 這個粉專的id和讚數print(fanpage_info) #印出來看看長什麼樣子,會是JSON格式的資料
print("Fanpage id = ", fanpage_info['id'])
posts = graph.get_connections(id = '1759525920983198', connection_name = 'posts', summary = True)
print(posts) #這行會印出一大堆貼文的資料
print ("共有", len(posts), "篇PO文")
  • 從URL爬 Graph API response
# Python 3.4
# -*- coding: utf-8 -*-
# OS: OS 10
# IDE: Sublime Text
import requests
import json
url = 'https://graph.facebook.com/v2.9/1759525920983198?fields=id,posts&access_token=yourAccessToken'
response = requests.get(url)
html = json.loads(response.text)
message = html['posts']['data'][0]['message']
print(message)

然後呢?

寫到這,我們終於費盡千辛萬苦地拿到了PyLadies Taiwan粉絲專頁上的文字了!(跳)

可是等一等…如果只是拿到這個結果,那還不如我直接去粉絲專頁複製這些文字,不是嗎!?

當然不是,現在我們要的所有資料都存在html裡了,只是還沒把它轉成好用的格式,那是下一篇文章的事情了。

下一次我們會把 JSON格式的資料,轉成平常大家習慣的Excel檔,這樣我們就能從資料裡看出一些端倪囉。

小知識:根據2017年6/25 – 6/27的貼文資料,中時電子報的粉專平均每天會貼90+篇文章,每篇文章平均有483個讚、65個分享次數;蘋果日報的粉專平均每天貼130+的文章,每篇文章平均有將近1592個讚、111個分享次數。這些都是有數字資料根據才能得到的資訊呢!

參考資料

--

--