使用YouTube Data API 如何避免视频信息获取遗漏?

How to avoid omissions in video information acquisition when using the YouTube Data API?

假设/我想要实现的目标

我想用YouTube Data API V3 无遗漏的获取视频ID,看看是代码出问题还是YouTube的视频设置出问题(API 边).

问题

以下代码用于从YouTube数据中获取视频信息API,但我获取的ID数量与实际发布的视频数量不符。

from apiclient.discovery 
import build
id = "UCD-miitqNY3nyukJ4Fnf4_A" #sampleID

token_check = None
nextPageToken = None
id_info = []

while True:
    if token_check != None:
        nextPageToken = token_check

    Search_Video = youtube.search().list(
        part = "id",
        channelId = id,
        maxResults = 50,
        order = 'date',
        safeSearch = "none",
        pageToken = nextPageToken
    ).execute()

    for ID_check in Search_Video.get("items", []):
        if ID_check["id"]["kind"] == "youtube#video":
            id_info.append(ID_check["id"]["videoId"])

    try:
        token_check = Search_Video["nextPageToken"]
    except:
        print(len(id_info)) #check number of IDs
        break

我也用YouTube Data API函数获取了频道的videoCount信息,发现videoCount的值与获取到的ID数量不匹配上面的代码,这就是我发布这个的原因。

根据channels() API,这个频道有440个视频,但上面的代码只得到412个视频(在10:30 a.m.JST)。

补充信息

・Python 3.9.0

・YouTube 数据 API v3

您必须承认 Search.list API 端点 没有清晰的行为 。这意味着您 不应期望从中获得精确的结果 。 Google 没有记录这种行为,但是这个论坛有很多用户遇到这种情况的帖子。

如果要获取给定频道上传的所有视频的 ID,则应采用以下两步过程:

第一步:获取频道上传播放列表ID

调用 Channels.list API endpoint, queried with its request parameter id set to the ID of the channel of your interest (or, otherwise, with its request parameter mine set to true) for to obtain that channel's uploads playlist ID, contentDetails.relatedPlaylists.uploads.

def get_channel_uploads_playlist_id(youtube, channel_id):
    response = youtube.channels().list(
        fields = 'items/contentDetails/relatedPlaylists/uploads',
        part = 'contentDetails',
        id = channel_id,
        maxResults = 1
    ).execute()

    items = response.get('items')
    if items:
        return items[0] \
            ['contentDetails'] \
            ['relatedPlaylists'] \
            .get('uploads')
    else:
        return None

请注意函数 get_channel_uploads_playlist_id 只应调用一次 以获取上传的播放列表 给定频道的 ID;随后根据需要多次使用该 ID。

第 2 步:检索播放列表的所有视频 ID。

调用 PlaylistItems.list API endpoint, queried with its request parameter playlistId 设置为从 get_channel_uploads_playlist_id 获得的 ID:

def get_playlist_video_ids(youtube, playlist_id):
    request = youtube.playlistItems().list(
        fields = 'nextPageToken,items/snippet/resourceId',
        playlistId = playlist_id,
        part = 'snippet',
        maxResults = 50
    )
    videos = []

    is_video = lambda item: \
        item['snippet']['resourceId']['kind'] == 'youtube#video'
    video_id = lambda item: \
        item['snippet']['resourceId']['videoId']

    while request:
        response = request.execute()

        items = response.get('items', [])
        assert len(items) <= 50

        videos.extend(map(video_id, filter(is_video, items)))

        request = youtube.playlistItems().list_next(
            request, response)

    return videos

请注意,使用 Google's APIs Client Library for Python (as you do), API result set pagination 时非常简单:只需使用 Python API 对象的 list_next 方法对应于相应的分页 API 端点(如上所示):

request = API_OBJECT.list(...)

while request:
    response = request.execute()
    ...
    request = API_OBJECT.list_next(
        request, response)

另请注意,上面我使用了两次 fields 请求参数。这是一个很好的做法:只从 API 询问实际使用的信息。

还有一个重要的注意事项:PlaylistItems.list 端点不会 return 对应于频道的 私人 视频的项目,当使用 API 键。当您的 youtube 对象是通过调用函数 apiclient.discovery.build 并向其传递参数 developerKey.

构造时发生的

PlaylistItems.list returns 项对应的私人视频仅供频道所有者使用。当 youtube 对象是通过调用函数 apiclient.discovery.build 传递给它的参数 credentials 构造的,并且如果 credentials 指的是拥有相应播放列表的频道时,就会发生这种情况。 =41=]

另一个重要说明:根据 Google staff设计 为通过 return 编辑的项目数量设置了 20000 上限20=] 查询给定频道的上传播放列表时的端点。这是不幸的,但却是事实。