如何在YouTube Api限額的情況下獲取更多視頻

文章來自自己的博客


YouTube視頻

谷歌限制了YouTube api v3的請求量,一天10000配額,這裏不是10000次請求,每次請求根據不同參數消耗不同配額。爲了擺脫這種限制而獲得更多的新發布視頻,做了以下內容的方案。

需求:

運營配置YouTube的channelId,後臺需要根據這些channelId去獲取最近發佈的可以在小屏播放的video信息,以增加用戶活度。

問題:

YouTube限額問題,谷歌限制域名只能使用一個ApiKey,配置多會被封禁,按照現有全部用api檢索會導致頻道越配越多,獲得的視頻越來越少。

解決:

思路1:

出於問題中關鍵點,系統不知道channel下面發佈的情況,只能被動查詢,這樣可能會導致查詢消耗了配置結果返回爲空或者很少視頻的情況;所以考慮使用訂閱模式去事先得知頻道的情況。

查找了很多資料;最坑的竟然是YouTube api官網給的方法。。。。(youtubeApi)。我試着去使用它介紹的發佈訂閱,對於Google的集線器我研究了很久,畢竟不熟悉,而且沒有相關的java實現。

方式1:

1.啓動自己的回調服務器,隨便弄個可以外網訪問的服務返回200和請求參數中的hub_chanlenge即可。

2.訂閱你需要訂閱的頻道的atom:類似:https://www.youtube.com/xml/feeds/videos.xml?channel_id=CHANNEL_ID 這種。

3.返回204即成功。

我的嘗試:

我使用的自己的雲服務器,使用谷歌的集線器,然後去訂閱YouTube,發現509等錯誤,莫名其妙後使用了自己寫的atom作爲發佈方,結果成功了。不過,可笑的是,這個集線器它並不能正常工作,我在修改atom再次發佈的時候,它竟然沒能好好工作;沒向我的回調函數發送信息。我崩潰了,我去谷歌搜索了很多相關問題,發現YouTube已經不將視頻信息發佈到上面所說的xml中了,而且在這之前YouTube爲了用戶體驗,每個頻道只發送3條消息給訂閱用戶(YouTube自帶的那個鈴鐺訂閱)我去你…..

方式2:

再對問題思考,依然擺脫不了需要提前得知頻道下視頻的發佈情況,我試着去YouTube網站videos下查看視頻與api返回的視頻做對照,發現可以使用解析http的標籤獲取發佈的視頻和時間(其實一開始也想過使用爬蟲,奈何怕蹲牢啊。。)。我試着使用httpClient解析這個頁面,果然得到了我想要的答案。

這樣我就可以提前知道頻道的發佈情況,進而對使用api檢索得到的結果有了大的優化。相關代碼如下:

YouTubeTest

public class YoutubeTest {
  
    private static String matching = "</li><li>";
    private final static String CONTENT = "class=\"yt-lockup-content\"";

    private static final String GET_VEDIO_INFO_PRE = "https://youtube.com/get_video_info?video_id=";

    public static void main(String[] args) throws Exception {
        http("UC24_Z2L-8Ki183AI9zJJzNQ");
    }




    private static void http(String channelId) throws Exception {
        String url = "https://www.youtube.com/channel/" + channelId + "/videos";
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpget = new HttpGet(url);
        CloseableHttpResponse response = httpclient.execute(httpget);
        try {
            HttpEntity entity = response.getEntity();
            String responseYoutube = EntityUtils.toString(entity);
            List<String> countList = new ArrayList<>(100);
            int length = responseYoutube.length();
            int i1, i2 = 0;
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < length; i++) {
                i1 = responseYoutube.indexOf(CONTENT, i);
                if (i1 > 0) {
                    i2 = responseYoutube.indexOf("</div>", i1);
                    if (i2 > 0) {
                        countList.add(responseYoutube.substring(i1, i2));
                        i = i2;
                    }
                } else {
                    break;
                }
            }
            long endEachTime = System.currentTimeMillis();
            System.out.println("遍歷耗時:" + (endEachTime - startTime) + "ms");
            List<VideoInfo> videoInfos = new ArrayList<>(30);
            countList.forEach((s) -> {
                int hrefStart = s.indexOf("v=");
                int hrefEnd = s.indexOf("\"", hrefStart);
                VideoInfo videoInfo = new VideoInfo();
                int lastIndex = s.indexOf("</li></ul>");
                if (lastIndex > 0) {
                    String substring = s.substring(s.indexOf(matching) + matching.length(), s.indexOf("</li></ul>"));
                    int time = analysisTime(substring);
                    if (time == -2) {
                        System.out.println(channelId + "返回參數中有解析錯誤的html標籤:" + s);
                    }
                    videoInfo.setPublishTime(time);
                    videoInfo.setVideoId(s.substring(hrefStart + 2, hrefEnd));
                    System.out.println(substring);
                    videoInfos.add(videoInfo);
                }
            });

            videoInfos.forEach(System.out::println);
            System.out.println("打印耗時:" + (System.currentTimeMillis() - endEachTime) + "ms");
            System.out.println(countList.size());
        } finally {
            response.close();
        }

    }

    private final static String CH_SECONDS_PRE = "秒前";
    private final static String CH_MINUTES_PRE = "分鐘前";
    private final static String CH_HOURS_PRE = "小時前";
    private final static String CH_DAYS_PRE = "天前";

    private static int analysisTime(String substring) {
        boolean matches = substring.substring(0, 2).trim().matches("^[0-9]*[1-9][0-9]*$");
        int time=-2;
        if(matches){
            time = Integer.parseInt(substring.substring(0, 2).trim());
            if (substring.contains(CH_SECONDS_PRE)) {
                time = time + 0;
            } else if (substring.contains(CH_MINUTES_PRE)) {
                time = time + 100;
            } else if (substring.contains(CH_HOURS_PRE)) {
                time = time + 200;
            } else if (substring.contains(CH_DAYS_PRE)) {
                time = time + 300;
            } else {
                time = -1;
            }
        }

        return time;
    }

 

 

VideoInfo

@Setter
@Getter
@ToString
public class VideoInfo {
    private String videoId;
    private int publishTime;

}

 

 

這裏使用的是香港,所以這裏匹配獲取時間的時候使用了繁體,解釋下這裏面的匹配規則。

class=”yt-lockup-content”是返回的html中視頻主題標籤的class,從此開始一個個獲取。

analysisTime 秒則直接使用,分鐘則爲100起,以此類推。

其實在F12調試的時候,這個URL請求獲得的是一段json,不知道爲什麼變成了html,對這方面不是很熟悉,之後會想辦法去優化這塊。

GET_VEDIO_INFO_PRE這個地址是YouTube的公共API,目前還是可以使用的,可以檢索一些視頻的信息。

發佈了21 篇原創文章 · 獲贊 25 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章