YouTube Data API (v3) JAVA で paging された検索結果の取得方法がわからず、
前準備と、実際のページング処理について
小一時間まさぐった結果をメモします。
参考サイト
-
Maven Repository: com.google.apis » google-api-services-youtube
-
Youtube Data API v3 java search with pagination issue - Stack Overflow
前準備
1. APIの有効かと、キーの発行
google developer console
でYouTube Data APIを有効化し、認証情報を作成します。
search API のみで、登録/更新は行わないので、「APIキー」を発行します。
APIキーのみの発行だと、セキュリティの警告が出るので、
[Referer] の値による検証も設定しました。
2. maven pom.xml の記述
Maven Repository: com.google.apis » google-api-services-youtube
で最新版のpom.xml 記述を取得します。
google alaytics API も maven 登録されているので、
google API の java liblary は 大概登録されているようです。
<!-- https://mvnrepository.com/artifact/com.google.apis/google-api-services-youtube -->
com.google.apis
google-api-services-youtube
v3-rev178-1.22.0
サンプル実装
search を実行して、ページングされた結果を収集して取得するプログラムです。
プログラムの下部に特記事項を記載します。
package xyz.monotalk.batch.utils;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.SearchListResponse;
import com.google.api.services.youtube.model.SearchResult;
import xyz.monotalk.batch.apps.BatchRuntimeException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* YoutubeUtils
*
* @author Kem
*/
public class YoutubeUtils {
private static final String API_KEY = "xxx";
/**
* Global instance of the HTTP transport.
*/
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
/**
* Global instance of the JSON factory.
*/
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
/**
* Global instance of the max number of videos we want returned (50 = upper limit per page).
*/
private static final long NUMBER_OF_VIDEOS_RETURNED = 25;
/**
* Global instance of Youtube object to make all API requests.
*/
private static YouTube youtube;
/**
* searchVideo
*
* @param query
* @return
*/
public static List searchVideo(String query) {
youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, new HttpRequestInitializer() {
public void initialize(HttpRequest request) throws IOException {
}
}).setApplicationName("yourAppName").build();
List searchResultList = new ArrayList<>();
String queryTerm = query;
YouTube.Search.List search = null;
try {
search = youtube.search().list("id,snippet");
} catch (IOException e) {
throw new BatchRuntimeException(e);
}
search.setKey(API_KEY);
search.setQ(queryTerm);
search.setType("video");
// 1.検索結果フィールドに"nextPageToken"を指定
search.setFields("items(*),nextPageToken");
HttpHeaders headers = new HttpHeaders();
// 2."Referer" に 稼働させる予定のドメイン名を指定
headers.set("Referer", "www.yourdomain.com/example");
headers.setContentType("application/json");
search.setRequestHeaders(headers);
search.setMaxResults(NUMBER_OF_VIDEOS_RETURNED);
SearchListResponse searchResponse = null;
do {
if (searchResponse != null && searchResponse.getNextPageToken() != null) {
search.setPageToken(searchResponse.getNextPageToken());
}
try {
searchResponse = search.execute();
} catch (IOException e) {
throw new BatchRuntimeException(e);
}
println("START---------------------");
try {
println(searchResponse.toPrettyString());
println(searchResponse.getNextPageToken());
} catch (IOException e) {
e.printStackTrace();
}
println("END-----------------------");
searchResultList.addAll(searchResponse.getItems());
// 検索結果の有無しは、searchResponse.getItems().isEmpty() で判定する
// この実装だと、loopが1回余分に回るがあまり気にしないでおく。
} while (!searchResponse.getItems().isEmpty());
return searchResultList;
}
private static void println(String msg) {
System.out.println(msg);
}
}
特記事項1
検索結果フィールドに”nextPageToken”を指定すると、
次ページ検索用のPageTokenが戻りのJSONに含まれるようになります。
PageTokenはSearchListResponse#getNextPageToken() で取得可能です。
特記事項2
APIキー発行時に、[Referer] の値による検証を設定しているので、
headers.set("Referer", "www.yourdomain.com/example");
で”Referer”値に稼働予定のドメイン名を指定しています。
※偽装ができるということになりますが..
特記事項3
ページングの検索結果の終了判定には、
searchResponse.getItems().isEmpty()
を使用しています。
結果が空なのに、PageTokenだけ返ってきたりしているので、
検索結果のリストが空かどうかで判定を実装しました。
補足
1. nextPageToken が Null か どうかで do-while-loopするとどうなるか?
最終的に以下のOUTPUTになり、止まりませんでした。
START---------------------
{
"items" : [ ],
"nextPageToken" : "CLYHEAA"
}
CLYHEAA
END-----------------------
START---------------------
{
"items" : [ ],
"nextPageToken" : "CM8HEAA"
}
CM8HEAA
END-----------------------
START---------------------
{
"items" : [ ]
}
null
END-----------------------
2. while (!searchResponse.isEmpty())
でdo-while-loopするとどうなるか?
最終的に以下のOUTPUTになり、止まりませんでした。
こちらは"nextPageToken"
がNullでも止まらないです。。
START---------------------
{
"items" : [ ],
"nextPageToken" : "CLYHEAA"
}
CLYHEAA
END-----------------------
START---------------------
{
"items" : [ ],
"nextPageToken" : "CM8HEAA"
}
CM8HEAA
END-----------------------
START---------------------
{
"items" : [ ]
}
null
END-----------------------
3. Referer の記載を誤った場合のエラー
headers.set("Referer","www.yourdomain.com/example");
の値を設定した値以外にした場合、以下のエラーが出力されます。
※403エラーとなります。
xyz.monotalk.batch.apps.BatchRuntimeException: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "usageLimits",
"message" : "The referrer www.xxxxxxxxxx.xyz/example does not match the referrer restrictions configured on your API key. Please use the API Console to update your key restrictions.",
"reason" : "ipRefererBlocked",
"extendedHelp" : "https://console.developers.google.com/apis/credentials?project=xxxxxxxxxxxxxx"
} ],
"message" : "The referrer www.xxxxxxxxxx.xyz/example does not match the referrer restrictions configured on your API key. Please use the API Console to update your key restrictions."
}
at xyz.monotalk.batch.utils.YoutubeUtils.searchVideo(YoutubeUtils.java:79)
at xyz.monotalk.batch.utils.YoutubeUtilsTest.testSearchVideo(YoutubeUtilsTest.java:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Echonest APIだと、Artist検索にまるっと、動画が含まれていたりして、
便利だった?のですが、LastFm API だとそうはいかず、自ら取りに行かざるえないですが、
勉強になりました。
以上です。
コメント