請啟用JavaScript來查看內容

[Python爬蟲實例] YouTube-使用 YouTube Data API

前言

來到Python網路爬蟲實例系列第四篇,本篇使用 Python 透過官方 YouTube Data API v3 來爬取 YouTube,包含頻道資訊、影片清單、影片資訊等等資料。

從在 Google 雲端平台(Google Cloud Platform)創建一個專案開始,包括獲取 API 授權憑證、取得 API key,到撰寫抓取程式皆會一步步講解如何操作。最後同樣會附上完整程式碼供參考。

而如果你是想下載 YouTube 影片的話,本篇內容並不會提及,這部分可參考其他套件,例如 pytube
之前還有另外一個套件 Youtube-dl,但在寫這篇文章時發現美國唱片業協會(RIAA)點名 Youtube-dl 程式碼違反著作權法,並要求GitHub將它們移除(來源)。

備註:此文僅教育學習,切勿用作商業用途,個人實作皆屬個人行為,本作者不負任何法律責任

套件

本次主要使用到的套件:


流程

要使用官方 YouTube Data API 抓取資料前,要先取得 API Key。

先到 Google 雲端平台(Google Cloud Platform)創建一個專案,並將 YouTube Data API 加入置專案內,獲取 API 授權憑證、取得 API key。

爬取資料這邊以 六指淵 Huber 頻道當範例,到上傳的影片列表抓取最新的前五則影片,再到影片內取得影片相關資訊及留言。

六指淵 Huber 頻道
六指淵 Huber 頻道

🔑 取得 YouTube Data API Key

先到Google 雲端平台(Google Cloud Platform)創建一個專案,如果是第一次進來會跳出服務條款,點選同意後繼續。
點擊右上角顯示"建立專案",或到上方的"選取專案"處建立。

服務條款
服務條款

右上角"新增專案"。

創建專案
創建專案

輸入"專案名稱",點選"建立"。
(專案ID也可以修改)

創建專案
創建專案

等它跑完後會進到此頁面,接下來要將我們需要使用的 API 加進來啟用。
點選"啟用 API 和服務"。

啟用 API
啟用 API

找到 YouTube Data API v3。

啟用 API
啟用 API

點擊"啟用"。

啟用 API
啟用 API

最後要建立憑證,並取得 API Key。
點擊右方"建立憑證",或者點擊左方的"憑證"。

建立憑證
建立憑證

如果你是點擊左方"憑證",則依下圖所示建立 API 金鑰 (API Key)。

建立憑證
建立憑證

如果你是點擊右方"建立憑證",則依下圖選擇,並點擊"我需要那些憑證"。

建立憑證
建立憑證

它會跳到此頁面,將 API 金鑰複製下來,待之後程式使用。
(也可以"為金鑰新增限制")

建立憑證
建立憑證

到這邊就完成 YouTube Data API Key 的取得,看似步驟很多,但實際操作過一遍就會了解了。

* 官方說明文檔

📘 爬蟲程式

本次是直接使用 API,因此在送出請求時就不需要加入 header 中 User-Agent 欄位了。

先在上方定義剛剛取得的 API Key,待之後發出請求時帶入。
(YOUR_YOUTUBE_API_KEY 換成剛剛在上一步取得的 API Key)

1
YOUTUBE_API_KEY = "YOUR_YOUTUBE_API_KEY"

API 說明

官方 API 文件內,可以查詢各種資源及各種請求方法。

在本次程式中會用到幾種資源 (這邊都只需使用 list 方法):

本教學都只使用到 list 方法(GET),因此網址串好後可以直接貼到瀏覽器上測試,觀看它回傳的資料。


因為每一次送出請求皆需要帶上 API Key,而且回傳資料要轉換JSON,所以可以另外寫一個用於組合網址、請求的函式,也可在裡面做當請求失敗的處理。

1
2
3
4
5
6
7
8
9
def get_html_to_json(self, path):
    """組合 URL 後 GET 網頁並轉換成 JSON"""
    api_url = f"{self.base_url}{path}&key={self.api_key}"
    r = requests.get(api_url)
    if r.status_code == requests.codes.ok:
        data = r.json()
    else:
        data = None
    return data

* 為了較清楚的了解流程,以下範例程式內沒有加入太多的例外處理。

取得頻道"上傳影片"清單的 ID

想要抓取頻道內上傳的影片前,需要先抓到"上傳影片"清單的 ID,再用此 ID 去取的影片清單。

使用channels路徑來取得,需帶上"id"、"key"、"part"等查詢參數。
id 代表頻道 ID;key 代表我們的 API Key;part 代表想取得的資源屬性。

頻道ID的取得方式可以先到頻道首頁,觀察網址後方帶的數值,例如:
https://www.youtube.com/channel/UC7ia-A8gma8qcdC6GDcjwsQ
"channel/"後方的 UC7ia-A8gma8qcdC6GDcjwsQ 就代表此頻道的 ID。

不過有時會發現網址是呈現以下形式:
https://www.youtube.com/c/六指淵Huber
別擔心,此時隨便點擊頻道底下的其中一支影片,在點中間的頻道名稱進入後,一樣即可看到網址後方的數值。

如何取得頻道 ID
如何取得頻道 ID

至於"part"需帶什麼數值可以參考官方 API 文檔說明,也可以一個一個嘗試看看會回傳什麼資料。

part 查詢參數
part 查詢參數

因為請求方式是使用 GET,因此可以直接貼到瀏覽器上測試。
https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=UC7ia-A8gma8qcdC6GDcjwsQ&key={YOUR_API_KEY}

取得上傳影片清單的 ID
取得上傳影片清單的 ID

==> 回應資料範例

其中 uploads 後面那串就是此頻道"上傳影片"清單的 ID,這邊是 UU7ia-A8gma8qcdC6GDcjwsQ

1
2
3
4
5
6
7
8
9
def get_channel_uploads_id(self, channel_id, part='contentDetails'):
    """取得頻道上傳影片清單的ID"""
    path = f'channels?part={part}&id={channel_id}'
    data = self.get_html_to_json(path)
    try:
        uploads_id = data['items'][0]['contentDetails']['relatedPlaylists']['uploads']
    except KeyError:
        uploads_id = None
    return uploads_id

取得清單中的影片列表

好的,接下來取得"上傳影片"清單的影片吧~

使用playlistItems路徑來取得,需帶上"playlistId"、"key"、"part"等查詢參數。
playlistId 代表播放列表 ID;key 代表我們的 API Key;part 代表想取得的資源屬性。

這邊代的參數大部分與上方雷同,可以自行參考官方 API 文檔說明,就不多說介紹了。
我們要抓此頻道上傳影片,所以 playlistId 帶入上一步抓到的"上傳影片"清單的 ID。

https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails&playlistId=UU7ia-A8gma8qcdC6GDcjwsQ&key={YOUR_API_KEY}

取得影片ID
取得影片ID

==> 回應資料範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def get_playlist(self, playlist_id, part='contentDetails', max_results=10):
    """取得影片清單ID中的影片"""
    path = f'playlistItems?part={part}&playlistId={playlist_id}&maxResults={max_results}'
    data = self.get_html_to_json(path)
    if not data:
        return []

    video_ids = []
    for data_item in data['items']:
        video_ids.append(data_item['contentDetails']['videoId'])
    return video_ids

取得影片資訊

針對每一支影片,來取得其影片相關資訊吧~

使用videos路徑來取得,需帶上"id"、"key"、"part"等查詢參數。
id 代表影片 ID;key 代表我們的 API Key;part 代表想取得的資源屬性。

https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&id=lM7ltxkXE40&key={YOUR_API_KEY}

取得影片資訊
取得影片資訊

==> 回應資料範例

回傳資料有影片標題、頻道名稱、發布時間、縮圖網址、觀看數、留言數……等等許多相關資訊。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def get_video(self, video_id, part='snippet,statistics'):
    """取得影片資訊"""
    path = f'videos?part={part}&id={video_id}'
    data = self.get_html_to_json(path)
    if not data:
        return {}
    # 以下整理並提取需要的資料
    data_item = data['items'][0]

    try:
        time_ = datetime.strptime(data_item['snippet']['publishedAt'], '%Y-%m-%dT%H:%M:%SZ')
    except ValueError:
        # 日期格式錯誤
        time_ = None

    url_ = f"https://www.youtube.com/watch?v={data_item['id']}"

    info = {
        'id': data_item['id'],
        'channelTitle': data_item['snippet']['channelTitle'],
        'publishedAt': time_,
        'video_url': url_,
        'title': data_item['snippet']['title'],
        'description': data_item['snippet']['description'],
        'likeCount': data_item['statistics']['likeCount'],
        'dislikeCount': data_item['statistics']['dislikeCount'],
        'commentCount': data_item['statistics']['commentCount'],
        'viewCount': data_item['statistics']['viewCount']
    }
    return info

取得留言資訊

那該如何取得影片底下的留言呢?

使用commentThreads路徑來取得,需帶上"videoId"、"key"、"part"等查詢參數。
videoId 代表播放列表 ID;key 代表我們的 API Key;part 代表想取得的資源屬性。

https://www.googleapis.com/youtube/v3/commentThreads?part=snippet&videoId=lM7ltxkXE40&key={YOUR_API_KEY}

取得留言資訊
取得留言資訊

==> 回應資料範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def get_comments(self, video_id, page_token='', part='snippet', max_results=100):
    """取得影片留言"""
    path = f'commentThreads?part={part}&videoId={video_id}&maxResults={max_results}&pageToken={page_token}'
    data = self.get_html_to_json(path)
    if not data:
        return [], ''
    # 下一頁的數值
    next_page_token = data.get('nextPageToken', '')

    # 以下整理並提取需要的資料
    comments = []
    for data_item in data['items']:
        data_item = data_item['snippet']
        top_comment = data_item['topLevelComment']
        try:
            time_ = datetime.strptime(top_comment['snippet']['publishedAt'], '%Y-%m-%dT%H:%M:%SZ')
        except ValueError:
            # 日期格式錯誤
            time_ = None

        if 'authorChannelId' in top_comment['snippet']:
            ru_id = top_comment['snippet']['authorChannelId']['value']
        else:
            ru_id = ''

        ru_name = top_comment['snippet'].get('authorDisplayName', '')
        if not ru_name:
            ru_name = ''

        comments.append({
            'reply_id': top_comment['id'],
            'ru_id': ru_id,
            'ru_name': ru_name,
            'reply_time': time_,
            'reply_content': top_comment['snippet']['textOriginal'],
            'rm_positive': int(top_comment['snippet']['likeCount']),
            'rn_comment': int(data_item['totalReplyCount'])
        })
    return comments, next_page_token

其他查詢參數

還有其他常用的查詢參數:

像是 maxResults 可以限制回傳筆數,搭配 commentThreads 帶入 maxResults=10 代表最多回傳 10 筆留言。

又例如 pageToken 是用來換頁的,什麼意思呢?
像是 API 有限制留言最多一次只能抓 100 筆留言,就算你帶入 maxResults=200 也只能抓到 100 筆。
但在回傳的資料中可以發現一個 nextPageToken 欄位,代表你再用一樣的網址請求一次,帶上 pageToken=QURTSl9pMmhMYktBc25xOHZtZTg3enJBTFFXNTFPbldJY05zTFFTTjdMM25VYThmdTBwWS1CNGFPZm1kQUU0WkJac0dWZkw3cWI2V1owOA== 就能取得下一頁的資料(例如:101~200 筆留言)。

nextPageToken 欄位
nextPageToken 欄位

在像是有多個留言(commentThreads)或多支影片(playlistItems)時需要用到。
以上一段取得留言為例,會像是如下:
https://www.googleapis.com/youtube/v3/commentThreads?part=snippet&videoId=lM7ltxkXE40&pageToken=QURTSl9pMmhMYktBc25xOHZtZTg3enJBTFFXNTFPbldJY05zTFFTTjdMM25VYThmdTBwWS1CNGFPZm1kQUU0WkJac0dWZkw3cWI2V1owOA==&key={YOUR_API_KEY}


完整程式碼

附上完整程式碼:youtube_spider_api.py
(對超連結右鍵 > 另存連結為)

注意事項

要注意的是這個 API 有額度的限制,為每天 10,000 個單位(官方說明),如果想將影片的留言都抓下來,可能就會耗費較多額度。
像上方使用的查詢都是消耗 1 單位。

配額頁面可以查看此專案目前耗費的單位。

配額頁面
配額頁面

延伸練習

  1. 在上方教學中,影片清單只有取得前 5 則影片,那如果我想取得前 100 則影片呢?是不是會遇到什麼問題?需不需要加上什麼參數?
    * 提示:參考教學中爬取留言的方法,使用 pageToken 參數。

  2. 上方教學只說抓取留言,那我如何想取得留言的回覆呢?是要帶入什麼參數嗎?或者是使用不同的方法?
    * 提示:part 中的 replies,或Comments資源。

結語

我會陸續寫一些網站的Python網路爬蟲實例,如果你正好是剛開始想學爬蟲的新手、想知道某個網站如何爬取資料,或者遇到其他問題,歡迎過來參考和在底下留言~👇




參考:
官方 YouTube Data API 範例文檔


每一個成功者都有一個開始。
勇於開始,才能找到成功的路。


🔻 如果覺得喜歡,歡迎在下方獎勵我 5 個讚~
分享

Jia
作者
Jia
軟體工程師