使用Youtube官方API获取频道及视频数据

news/2024/11/29 3:52:02/

2020-06-04更新

下面附上笔者提供的源码(已经验证过功能。后续会在同一个工程中更新Facebook和Insgram的相关爬虫代码)。
https://github.com/zhangjz777/yfi_source
下面是工程关于Youtube相关的代码注意事项介绍。

  • 如遇到此报错“java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String”,是由于pom引入的jar包中包含的低版本servlet-api 导致的,请自行排除多余的依赖。详情可以参考这篇文章:SpringBoot报错:java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;
  • 请确保你的IDE中,有lombok插件支持。
  • 请确保你已正确申请youtube api key,代码中的key是笔者的,现已弃用。
  • 因笔者水平有限,若有什么代码问题,请文明交流,留在你的评论或者私信。

一、前期调研

开始决定做Youtube的时候先是查阅了百度和Google上的一些搜索结果。当我以youtube爬虫作为搜索关键词时,结果并不尽人意:在这其中部分爬虫是以下载视频为目的,没有获取视频其他信息,因此并不是我所需要的。还有部分商业网站提供付费接口服务,但是秉着能省则省的原则,这种肯定是不会考虑的,因此只能另辟奇径。当我切换以“youtube api”为关键词搜索时,发现了下面这个。

​ 没错,Google官方提供了关于Youtube API给开发者使用。也给予此,开发者也没有必要自己花费大力气去破解youtube的api。因此,本篇文章本质上是一篇讲解youtube 官方API使用教程的文章,若已有相关开发经验的读者可以阅读另外两篇,如果你尚未了结此api库的使用方法那这篇文章或许可以给你一些参考,同时我也会附上一些我在实际开发过程中遇到的一些问题和相关的注意事项供你参考。

二、Youtube API的使用

本文中的使用方式是在Java工程中,其他语言的使用教程请参考官方文档。

1、如何才能使用YouTube API?

(1)登录Google云平台

首先你需要登录Google的云平台(请自备Google邮箱账号)

https://console.developers.google.com/apis/api/youtube.googleapis.com

(2)开通Youtube API V3服务

登录成功后在图示中找到Youtube API v3开通

按照如下顺序创建API秘钥用于发起请求时的权限验证

(3)阅读API开发文档

将API复制到别处保存后,就可以前往API使用文档页面了。

https://developers.google.com/youtube/v3/docs

这里包含了常用的业务模型,在笔者的实际开发过程中使用了Channel、Playlist、Videos、Search进行操作。

2、API的使用方式

(1)在Java项目中引入jar包依赖

下面使用到的版本号可根据官方文档提供的填入

        <!-- YouTube Data V3 support --><dependency><groupId>com.google.apis</groupId><artifactId>google-api-services-youtube</artifactId><version>v3-rev182-1.22.0</version></dependency><!-- Required for any code that makes calls to the YouTube Analytics API --><dependency><groupId>com.google.apis</groupId><artifactId>google-api-services-youtubeAnalytics</artifactId><version>v3-rev182-1.22.0</version></dependency><!-- Required for any code that makes calls to the YouTube Reporting API --><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的频道下的所有视频,根据视频发布时间排序

参考文档

https://developers.google.com/youtube/v3/docs/search

首先你需要的channel id 如何获取呢?根据我的观察,应该是有两种情况的。

频道首页url自带channel id

图中链接为:https://www.youtube.com/channel/UCXuqSBlHAE6Xw-yeJA0Tunw

即channel 后边的 “UCXuqSBlHAE6Xw-yeJA0Tunw” 即为当前频道的channel id,可以在api 中指定该参数。

频道首页url不包含channel id

图中链接为:https://www.youtube.com/user/EVOTV

然而,官方提供的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 idsearch.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 idList<String> videoIds = allRecord.stream().map(SearchResult::getId).map(ResourceId::getVideoId).collect(Collectors.toList());videoIds.forEach(System.out::println);}

查询指定video的详细信息(使用参数video id)

参考文档

https://developers.google.com/youtube/v3/docs/videos

video id的来源

在视频详情页中:https://www.youtube.com/watch?v=P_7piye1Who

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)

参考文档

https://developers.google.com/youtube/v3/docs/channels

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());
}

查询指定播放列表下的视频

参考文档

https://developers.google.com/youtube/v3/docs/playlistItems

播放列表是Youtube区别于Facebook和Ins而独有的,笔者理解播放列表其实相当于对视频合集的分类。

那如何获取playlist id呢?其实与video id一样,点进去一个播放列表详情页,查看它的url

https://www.youtube.com/watch?v=2i1PdfaAKFA&list=PL8mG-RkN2uTwChYF-gaygFQero5g5IXgr

其中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());// 打印所有视频idallVideoIds.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拒绝相应,请仔细阅读官方文档。

配额消耗情况表格:

三、订阅功能实现

​ 看到这里可能有读者会问:假如我想实现一个订阅功能呢?我希望订阅一个频道后,Youtube可以将更新信息同步推送给我指定的服务器。

​ 答案是可以的。

​ 笨重但比较直接的想法是采用轮询机制,即每隔一段时间去请求频道下的视频接口,根据返回的视频名称判断是否是新视频。这样做的缺点显而易见,一是除非轮询时间间隔特别短,否则基本没法保证时效性。二是频繁的访问查询接口会浪费掉大量的api 配额,因此这不是一种优雅的解决方案。

​ 为了避免这个问题官方提供了发布订阅系统,一种基于Webhooks实现的订阅推送(对于Webhooks机制不清楚的同学可以了解后再去尝试),可以实现几乎实时的更新推送。

详细资料参考官方的这篇文档

https://developers.google.com/youtube/v3/guides/push_notifications

其主要流程就是,在下面这个网址中添加订阅频道和回调地址

https://pubsubhubbub.appspot.com/subscribe

这样你就会在你的服务器上接收到这样的更新信息

<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015"xmlns="http://www.w3.org/2005/Atom"><link rel="hub" href="https://pubsubhubbub.appspot.com"/><link rel="self" href="https://www.youtube.com/xml/feeds/videos.xml?channel_id=CHANNEL_ID"/><title>YouTube video feed</title><updated>2015-04-01T19:05:24.552394234+00:00</updated><entry><id>yt:video:VIDEO_ID</id><yt:videoId>VIDEO_ID</yt:videoId><yt:channelId>CHANNEL_ID</yt:channelId><title>Video title</title><link rel="alternate" href="http://www.youtube.com/watch?v=VIDEO_ID"/><author><name>Channel title</name><uri>http://www.youtube.com/channel/CHANNEL_ID</uri></author><published>2015-03-06T21:40:57+00:00</published><updated>2015-03-09T19:05:24.552394234+00:00</updated></entry>
</feed>

四、页面展示

当我们获取到了足够的数据后,如何在我们页面上嵌入Youtube的视频呢?其实很简单,通过iframe直接嵌入到你的document中就可以实现了(注意替换你的video id)。

<iframe src="https://www.youtube.com/embed/KZ7Z2x4FIWw?autoplay=0&amp;autohide=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" style="height: 2rem;"></iframe>

最终实现效果:

五、总结

对于Youtube API的使用笔者也是第一次接触,使用上难免会有不准确之处,欢迎评论区交流,指正笔者的错误。


http://www.ppmy.cn/news/382195.html

相关文章

Chrome翻译用不了了,怎么弄?解决

​节前有段时间就感觉chrome的翻译使用非常不顺&#xff0c;经常需要点两次才能翻译出来。有次访问 translate.google.cn 也是挺慢的。不过近期用的频率不高&#xff0c;就没有怎么当回事儿。 国庆放假期间&#xff0c;看到一个帖子说 Google翻译在中国大陆停服了。突然感觉到…

解决谷歌翻译用不了的问题

关于Google翻译和FCM到底是怎么回事 本文来源TG群友投稿 截止日前&#xff0c;Google翻译和FCM在中国大陆出现异常&#xff0c;但是出现异常的原因却不同。 FCM&#xff1a;Firebase Cloud Messaging&#xff0c;是Google Cloud Messaging的升级版本。 已经不是一种服务而是…

Youtube视频搬运、赚外国人的钱,项目操作解析

现在大家都知道短视频很赚钱&#xff0c;但是在国内的视频实在是太多的相同的作品&#xff0c;想要搬运又全都是搬运的&#xff0c;账号权重也是一直都起不来&#xff0c;所以有人就把赚钱的心思打到国外去了。 今天我们要说的这个项目就是Youtube视频搬运&#xff0c;Youtube&…

连着网,却上不去,浏览器打不开

以管理员身份运行cmd&#xff08;开始—程序—附件—鼠标右键命令提示符&#xff0c;以管理员身份运行&#xff09;&#xff0c;在弹出的命令提示符窗口中输入以下命令&#xff0c;&#xff1a; 第一次输入&#xff1a;netsh int ip reset 重置TCP/IP协议 重置TCP/IP协议后&am…

Youtube是什么平台?有什么用?能同时登录吗?

Youtube是什么平台&#xff1f;有什么用&#xff1f;一个浏览器可以同时登录多个Youtube账号吗&#xff1f;今天我们小编就来跟大家简单聊聊。 Youtube是什么平台&#xff1f; YouTube是源自美国的视频分享网站&#xff0c;也是目前全球最大的视频搜索和分享平台&#xff0c;…

Google.cn刚上不去了

Google.cn刚上去了&#xff0c;本来输入的google的最简域名 g.cn 无法打开站点&#xff0c;于是输入完整域名&#xff0c;结果还是一样&#xff0c; 不过没几分钟就好了&#xff0c;很奇怪&#xff01;

最新解决谷歌翻译无法使用的教程

谷歌翻译无法使用是谷歌官方关闭了中国地区翻译服务。 废话不多说直接上教程&#xff0c;本质就是通过修改hosts文件让translate.googleapis.com域名的IP解析到国内的谷歌服务器IP&#xff0c;网上大部分的教程也是如此。 但是有个问题就是这个IP不稳定可能用了几天就不用了&am…

使用youtube语音识别功能给视频加自动字幕

起因 自己英文比较差&#xff0c;国外大佬的演进听不清楚&#xff0c;也没有字幕&#xff0c;虽然有PPT&#xff0c;但还是想完整的了解下。 于是在想自己能用哪种语音识别的工具把英文字幕加上。最近一直在youtube上看视频&#xff0c;发现youtube可以把字幕转换成各国语言的…