python - Youtube Data Api Page Token Question (python)
问题描述
我尝试下载 2019 年的视频元数据。每次运行我的代码时它都超过了配额限制。在那段时间里,我有不到 100 个视频。谁能告诉我一个更好的方法来编写代码?
try:
request = youtube.search().list(
part = 'id, snippet',
type = 'video',
publishedAfter = '2018-12-31T23:59:59Z',
publishedBefore = '2020-01-01T00:00:00Z',
order = 'date',
fields = 'nextPageToken,items(id,snippet)',
pageToken = None,
maxResults = 50
)
response = request.execute()
nextPageToken = None
while True:
request = youtube.search().list(
pageToken = nextPageToken,
part = 'id, snippet',
type = 'video',
fields = 'nextPageToken,items(id,snippet)',
maxResults = 50
)
response = request.execute()
nextPageToken = response['nextPageToken']
items = response['items']
if response['nextPageToken'] == None:
break
for each_item in items:
video_id = each_item['id']['videoId']
sub_items = each_item['snippet']
for sub_item in sub_items:
video_item[sub_item] = sub_items[sub_item ]
video_data[video_id] = video_item
except Exception as e:
print('Error in get_video_data: {0}'.format(e))
谢谢!
解决方案
请确认您对Search.list
端点的 API 调用是针对该一年期间的整套 YouTube 视频运行的;您的 API 调用未指定任何其他过滤条件,这意味着您的查询(在分页时)可能会返回数百万个视频条目。
如果实际上您正在寻找自己的视频,那么您的Search.list
端点调用应该包括 theforMine
或channelId
request 参数:
- 当您使用其参数
youtube
从方法构造对象时(即您正在发出授权请求),然后使用请求参数,如下所示:discovery.build
credentials
forMine
request = youtube.search().list(
forMine = True,
part = 'id,snippet',
type = 'video',
publishedAfter = '2018-12-31T23:59:59Z',
publishedBefore = '2020-01-01T00:00:00Z',
order = 'date',
fields = 'nextPageToken,items(id,snippet)',
maxResults = 50
)
请注意,根据下面更新和修复部分中记录的调查结果,这种替代方法被证明是不可行的。
- 当您使用其参数
youtube
从方法构造对象时(即您没有发出授权请求),然后使用请求参数,如下所示:discovery.build
developerKey
channelId
request = youtube.search().list(
channelId = CHANNEL_ID,
part = 'id,snippet',
type = 'video',
publishedAfter = '2018-12-31T23:59:59Z',
publishedBefore = '2020-01-01T00:00:00Z',
order = 'date',
fields = 'nextPageToken,items(id,snippet)',
maxResults = 50
)
请注意,这CHANNEL_ID
是您的频道(或任何其他频道)的 ID。
上述两种 API 调用之间的区别如下:发出授权请求时(上面的第一个项目符号),您将获得您频道的所有视频,包括那些非公开的视频(即那些privacyStatus
设置为private
或unlisted
);另一方面,当使用 API 密钥(上面的第二个项目符号)时,即使是您自己频道的 ID,您也只会获得公共视频(即那些privacyStatus
设置为的视频)。public
CHANNEL_ID
现在,不幸的是,您上面的代码还有另一个问题:您的两个Search.list
端点调用不相同,以pageToken
请求参数为模。那是因为第二次调用没有获取到请求参数publishedAfter
和publishedBefore
.
这种差异意味着您没有正确分页第一个 API 调用的结果集(实际上,即使将参数传递pageToken
给第二个 API 调用)。
幸运的是,您正在使用的用于 Python 的 Google 的 API 客户端库以简单的Python 方式实现了API 结果集分页(我将在上面第二个项目符号的情况下举例说明):
request = youtube.search().list(
channelId = CHANNEL_ID,
part = 'id,snippet',
type = 'video',
publishedAfter = '2018-12-31T23:59:59Z',
publishedBefore = '2020-01-01T00:00:00Z',
order = 'date',
fields = 'nextPageToken,items(id,snippet)',
maxResults = 50
)
video_data = {}
while request:
response = request.execute()
for item in response['items']:
video_id = item['id']['videoId']
video_item = item['snippet']
video_data[video_id] = video_item
request = youtube.search().list_next(
request, response)
上面的代码表明不需要完整地重复第一个 API 调用,并添加一个pageToken
参数;有更简单的陈述就足够了:
request = youtube.search().list_next(
request, response)
该语句使用对象的nextPageToken
属性值response
从旧request
对象构造一个具有正确设置pageToken
属性的新对象。
更新和修复
Search.list
在对请求参数的调用进行进一步的测试和调查后,forMine
如上所述,我得出以下结论:publishedAfter
publishedBefore
给出的参数
forMine=True
没有任何参数publishedAfter
,publishedBefore
并使 API 调用按预期工作;与任何参数
forMine=True
一起给出的参数publishedAfter
和publishedBefore
/或两者都会产生 HTTP 错误400 Bad Request
以及 JSON 错误响应:
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"errors": [
{
"message": "Request contains an invalid argument.",
"domain": "global",
"reason": "badRequest"
}
],
"status": "INVALID_ARGUMENT"
}
}
Google 自己的问题跟踪器记录了一份最近的错误报告,该报告准确地描述了上述行为。谷歌工作人员的官方回应如下:
状态:无法修复(预期行为)
这是按预期工作的。基本上,如果是 for_content_owner 请求,您只能设置其中一个资源过滤器,但频道 ID 和发布后都是资源过滤器。开发者网站上似乎没有指定此要求:https ://developers.google.com/youtube/v3/docs/search/list 。
推荐阅读
- angular - 将文件从 Angular 9 上传到 NestJs
- c# - IComparer 不在 LINQ 表达式中使用
- python - 如何在 python 3.6 中修复 ModuleNotFoundError
- c - 在客户端的 createnamedpipe 中访问被拒绝
- asp.net-core - 使用声明而不是 ASP.Net 身份中的策略进行授权
- amazon-ec2 - AWS beanstalk (eb init) - 在窗口机器上设置时间
- python - SQLAlchemy sais moduel not callebel
- ios - 如何找到数组中每个类别的总和?
- java - Java 通用地图索引函数
- c++11 - 在opengl中使用球坐标