下面附上笔者提供的源码(已经验证过功能。后续会在同一个工程中更新Facebook和Insgram的相关爬虫代码)。
下面是工程关于Youtube相关的代码注意事项介绍。
一、前期调研
开始决定做Youtube的时候先是查阅了百度和Google上的一些搜索结果。当我以youtube爬虫作为搜索关键词时,结果并不尽人意:在这其中部分爬虫是以下载视频为目的,没有获取视频其他信息,因此并不是我所需要的。还有部分商业网站提供付费接口服务,但是秉着能省则省的原则,这种肯定是不会考虑的,因此只能另辟奇径。当我切换以“youtube api”为关键词搜索时,发现了下面这个。
没错,Google官方提供了关于Youtube API给开发者使用。也给予此,开发者也没有必要自己花费大力气去破解youtube的api。因此,本篇文章本质上是一篇讲解youtube 官方API使用教程的文章,若已有相关开发经验的读者可以阅读另外两篇,如果你尚未了结此api库的使用方法那这篇文章或许可以给你一些参考,同时我也会附上一些我在实际开发过程中遇到的一些问题和相关的注意事项供你参考。
二、Youtube API的使用
本文中的使用方式是在Java工程中,其他语言的使用教程请参考官方文档。
1、如何才能使用YouTube API? (1)登录Google云平台
首先你需要登录Google的云平台(请自备Google邮箱账号)
(2)开通Youtube API V3服务
登录成功后在图示中找到Youtube API v3开通
按照如下顺序创建API秘钥用于发起请求时的权限验证
(3)阅读API开发文档
将API复制到别处保存后,就可以前往API使用文档页面了。
这里包含了常用的业务模型,在笔者的实际开发过程中使用了Channel、Playlist、Videos、Search进行操作。
2、API的使用方式 (1)在Java项目中引入jar包依赖
下面使用到的版本号可根据官方文档提供的填入
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-youtube</artifactId>
<version>v3-rev182-1.22.0</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-youtubeAnalytics</artifactId>
<version>v3-rev182-1.22.0</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-youtubereporting</artifactId>
<version>v1-rev10-1.22.0</version>
</dependency>
另外请确保你有可以用的本地网络代理可以访问外网,具体使用方式
其中host表示,你设置的代理ip,port表示代理使用的端口。
System.setProperty("http.proxyHost", host);
System.setProperty("http.proxyPort", port);
System.setProperty("https.proxyHost", host);
System.setProperty("https.proxyPort", port);
(2)代码示例 查询指定channel id的频道下的所有视频,根据视频发布时间排序
参考文档
首先你需要的channel id 如何获取呢?根据我的观察,应该是有两种情况的。
频道首页url自带channel id
图中链接为:
即channel 后边的 “UCXuqSBlHAE6Xw-yeJA0Tunw” 即为当前频道的channel id,可以在api 中指定该参数。
频道首页url不包含channel id
图中链接为:
然而,官方提供的api库里,并没有根据user name 来搜索视频的api。这时,该怎么处理呢?
其实很简单,在当前页面下打开调试窗口(F12),在Elements这一栏中搜索channel,找到与当前频道名字相同的链接,这就是该用户“隐藏”的channel id。
代码请求参考如下
YouTube youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, request -> {
}).setApplicationName("youtube-cmdline-search-sample").build();
YouTube.Search.List search = youtube.search().list("id,snippet");
String apiKey = "你在Google云平台申请到的api key";
search.setKey(apiKey);
// 接口返回数据模型
search.setType("video");
// 设置需要接口返回的字段
search.setFields("items(id/kind,id/videoId,snippet/title,snippet/thumbnails/default/url),nextPageToken,pageInfo,prevPageToken");
// 返回的最大记录条数
search.setMaxResults(50);
// 设置要查询的channel id
search.setChannelId(channelId);
search.setOrder("date");
SearchListResponse searchResponse;
while (true) {
try {
searchResponse = search.execute();
break;
} catch (GoogleJsonResponseException e) {
if (403 == e.getDetails().getCode()) {
// 配额用尽,使用下一个
LogUtil.error("youtube api 配额用尽,尝试替换api key");
seatch.setKey("替换新的api key");
return;
} else {
LogUtil.error("其它异常,结束任务");
throw e;
}
}
}
List<SearchResult> searchResultList = searchResponse.getItems();
List<SearchResult> allRecord = new ArrayList<>();
if (searchResultList != null) {
PageInfo pageInfo = searchResponse.getPageInfo();
// 根据分页获取全部数据
allRecord.addAll(searchResultList);
while (true) {
// 设置分页的参数
search.setPageToken(searchResponse.getNextPageToken());
searchResponse = search.execute();
if (searchResponse == null ||
AppUtil.isNull(searchResponse.getItems())) {
break;
}
List<SearchResult> items = searchResponse.getItems();
if (AppUtil.isNull(items)) {
break;
}
allRecord.addAll(items);
if (items.size() < 50) {
break;
}
}
if (AppUtil.isNull(allRecord)) {
return;
}
// 获取所有的video id
List<String> videoIds = allRecord.stream()
.map(SearchResult::getId)
.map(ResourceId::getVideoId)
.collect(Collectors.toList());
videoIds.forEach(System.out::println);
}
查询指定video的详细信息(使用参数video id)
参考文档
video id的来源
在视频详情页中:
v后边的参数“P_7piye1Who”即为当前视频的video id。
获取使用前一个api 获取频道下的视频,返回的也是video id。
YouTube youtubeVideo = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, request -> {
}).setApplicationName("youtube-cmdline-search-sample").build();
YouTube.Videos.List search = youtubeVideo.videos().list("id,snippet");
// 设置要查询的字段信息
search.setFields("items(id,snippet/publishedAt,snippet/title,snippet/description,snippet/tags,snippet/channelId,snippet/thumbnails)");
String apiKey = "申请到的API key";
if (apiKey == null) {
return null;
}
search.setKey(apiKey);
search.setMaxResults(50);
StringBuilder videoIdStr = new StringBuilder();
if (videoIds.size() > 1) {
for (int i = 0; i < videoIds.size(); i++) {
videoIdStr.append(videoIds.get(i));
if (i != videoIds.size() - 1) {
videoIdStr.append(",");
}
}
} else {
videoIdStr.append(videoIds.get(0));
}
// 此处可以放入最多50个id进行查询,以逗号分隔
search.setId(videoIdStr.toString());
VideoListResponse response;
while (true) {
try {
response = search.execute();
break;
} catch (GoogleJsonResponseException e) {
if (403 == e.getDetails().getCode()) {
// 配额用尽,使用下一个
LogUtil.error("youtube api 配额用尽,尝试替换api key");
String newApiKey = "尝试替换新的配额";
} else {
LogUtil.error("其它异常,结束任务");
throw e;
}
}
}
if (response == null) {
return null;
}
List<Video> items = response.getItems();
if (AppUtil.isNull(items)) {
return null;
}
for (Video item : items) {
// 标签数据
List<String> tags = item.getSnippet().getTags();
StringBuilder stringBuilder = new StringBuilder();
if (AppUtil.isNotNull(tags)) {
for (String tag : tags) {
stringBuilder.append(tag).append(",");
}
}
String description = item.getSnippet().getDescription();
if (description == null) {
description = "";
}
System.out.println("description:" + description);
System.out.println("channel id:" + channel.getId());
System.out.println("title:" + item.getSnippet().getTitle());
System.out.println("thumbnails" + item.getSnippet().getThumbnails().getDefault().getUrl());
System.out.println("releaseTime:" + new Date(item.getSnippet().getPublishedAt().getValue()));
}
查询指定频道的信息(使用channel id)
参考文档
YouTube youtubeChannel = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, request -> {
}).setApplicationName("youtube-cmdline-search-sample").build();
YouTube.Channels.List search = youtubeChannel.channels().list("id,snippet");
search.setFields("items(snippet/publishedAt,snippet/title,snippet/description,snippet/thumbnails)");
String apiKey = "从Google云平台获取的api key";
search.setKey(apiKey);
search.setMaxResults(50);
String channelId = "需要查询的channel id";
search.setId(channelId);
ChannelListResponse response;
while (true) {
try {
response = search.execute();
break;
} catch (GoogleJsonResponseException e) {
if (403 == e.getDetails().getCode()) {
// 配额用尽,使用下一个
LogUtil.error("youtube api 配额用尽,尝试替换api key");
String newApiKey = getUsableApiKey(apiKey);
return null;
} else {
LogUtil.error("其它异常,结束任务");
throw e;
}
}
}
if (response == null || AppUtil.isNull(response.getItems())) {
return null;
}
List<Channel> items = response.getItems();
if (AppUtil.isNull(items)) {
return null;
}
Channel channel = items.get(0);
System.out.println("channelId:" + channelId);
System.out.println("频道名称:" + channel.getSnippet().getTitle());
System.out.println("缩略图 thumbnails:" + channel.getSnippet().getThumbnails().getDefault().getUrl());
System.out.println("频道简介:" + channel.getSnippet().getDescription());
}
查询指定播放列表下的视频
参考文档
播放列表是Youtube区别于Facebook和Ins而独有的,笔者理解播放列表其实相当于对视频合集的分类。
那如何获取playlist id呢?其实与video id一样,点进去一个播放列表详情页,查看它的url
其中list= 后边的 “PL8mG-RkN2uTwChYF-gaygFQero5g5IXgr”即为当前播放列表的playlist id。同时,也可以通过官方提供的api获取一个频道下所有的播放列表,这里不再赘述。
下面是演示代码
YouTube youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, request -> {
}).setApplicationName("youtube-cmdline-search-sample").build();
YouTube.PlaylistItems.List search = youtube.playlistItems().list("id,snippet");
String apiKey = "获取的youtube api key";
search.setKey(apiKey);
// 需要接口返回的字段信息
search.setFields("items(snippet/resourceId/videoId),nextPageToken,pageInfo,prevPageToken");
search.setMaxResults(50);
String playlistId = "获取的播放列表";
search.setPlaylistId(playlistId);
PlaylistItemListResponse searchResponse;
while (true) {
try {
searchResponse = search.execute();
break;
} catch (GoogleJsonResponseException e) {
if (403 == e.getDetails().getCode()) {
// 配额用尽,使用下一个
LogUtil.error("youtube api 配额用尽,尝试替换api key");
String newApiKey = "尝试获取新的api ";
if (newApiKey == null) {
// 结束当前任务
LogUtil.error("系统当前配额已用完");
return;
}
if (!apiKey.equals(newApiKey)) {
// 返回新的api-key则再次尝试
search.setKey(newApiKey);
} else {
// api-key相同则退出
return;
}
} else {
LogUtil.error("其它异常,结束任务");
throw e;
}
}
}
List<PlaylistItem> searchResultList = searchResponse.getItems();
List<PlaylistItem> allRecord = new ArrayList<>();
if (searchResultList != null) {
PageInfo pageInfo = searchResponse.getPageInfo();
if (pageInfo.getTotalResults() < 50) {
// 添加第一页数据
List<String> allVideoIds = searchResultList.stream()
.map(PlaylistItem::getSnippet)
.map(PlaylistItemSnippet::getResourceId)
.map(ResourceId::getVideoId)
.collect(Collectors.toList());
// 打印所有视频id
allVideoIds.forEach(System.out::println);
return;
} else {
// 获取多页数据
allRecord.addAll(searchResultList);
while (true) {
// 请求下一页的数据
search.setPageToken(searchResponse.getNextPageToken());
searchResponse = search.execute();
if (searchResponse == null ||
AppUtil.isNull(searchResponse.getItems())) {
break;
}
List<PlaylistItem> items = searchResponse.getItems();
if (AppUtil.isNull(items)) {
break;
}
allRecord.addAll(items);
if (items.size() < 50) {
break;
}
}
}
}
if (AppUtil.isNull(allRecord)) {
return;
}
List<String> videoIds = allRecord.stream()
.map(PlaylistItem::getSnippet)
.map(PlaylistItemSnippet::getResourceId)
.map(ResourceId::getVideoId)
.collect(Collectors.toList());
videoIds.forEach(System.out::println);
(3)API相关注意事项 分页相关注意事项
以上绝大部分的请求api 的最大返回条数为50条,分页大小设置超过接口限制则会返回错误提示。
再请求下一页数据时,必须用到上一次请求的 一个参数,像下面这样:
search.setPageToken(searchResponse.getNextPageToken());
searchResponse = search.execute();
配额限制相关
每个Google账户申请到的api key每日有10000个配额的限制。
每个接口消耗的配额并不是相同的,比如Search接口每次消耗100个单位的配额,而Video的相关只消耗1-2个单位。具体每个接口的消耗配额数量可以参考文档。
因此,可以事先做好配额估算,是非常必要的,可以提前多申请几个api key 用于轮换。
另一个需要注意的点是,配额消耗完,接口并不是返回错误信息提醒,而是直接抛出异常。因此,需要在代码中做捕获处理,像这样:
try {
searchResponse = search.execute();
break;
} catch (GoogleJsonResponseException e) {
if (403 == e.getDetails().getCode()) {
System.out.println("配额用尽,使用下一个");
}
}
另外,你也可以在Google云平台上申请扩充配额,但是这个流程想当繁琐复杂,对于个人开发者来说几乎不能实现。此外高并发的请求API会导致Google拒绝相应,请仔细阅读官方文档。
配额消耗情况表格:
原文链接:http://www.wzcl.net/social/youtube/9762.html,转载请注明出处~~~
免责声明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
评论0