好きなチャンネルの新着動画をYouTubeAPIでサイトに表示させて自動更新する方法

youtube

今回はyoutubeAPIから取得した動画をワードプレスで作ったブログに埋め込んで自動更新し、更新作業なしでユーザーに最新動画が表示される機能を作ってみようと思います。

youtubeAPIから動画を取得して表示させるのは簡単ですがyoutubeAPIは1日に実行できる回数が少ないので、現実的に運用させるには「ページ更新=API実行」にならないよう工夫する必要がありますんでコツを教えちゃいます。

細かい知識は不要、すぐに実装したい!という方は目次の「完成したコピペコード」をクリックして下さい。

youtubeAPIのユニット消費を節約する工夫

youtubeAPIはAPIキー毎に付与されるユニット数(ポイントみたいな物、使い切るとAPIが使えなくなる)が少ないので「ページ更新=API実行」という仕様にしてしまうと、100回くらいページを更新するだけですぐユニットが枯渇してしまい、24時間経過するまでAPIが実行できなくなってしまいます

参考:YouTube Data API の概要

なので、youtubeAPIで動画を取得してサイトに表示させる際の基本設計は、youtubeの動画を格納するデータベースを作ってそこに一旦保存し、ユーザーにはデータベースから動画を表示させるという方法がオススメです。

そして「youtubeAPI→データベース」の工程は、wp_schedule_eventかサーバーのcronで自動化する事でyoutubeAPIのユニットの消費をページの更新回数と切り離してコントロールできます

必要な工程を考えてみる

今回はワードプレスへの実装なのでPHPで書いてみます、実装までに必要な工程はこんな感じ。

  1. youtubeAPIキーの取得
  2. 好きなチャンネルIDの取得
  3. youtube API searchリソースへリクエスト実行
  4. 任意の情報をDBへ格納
  5. wp_schedule_eventで自動更新
  6. ショートコードでページに表示させる

それぞれの要件について考えてみます、技術的にはそこまで難しくないのでPHPの練習問題に丁度レベルだと思うのでチャレンジしてみて下さい。

youtubeAPIキーの取得

まずはGoogle Cloud PlatformでyoutubeAPIキーを準備します、すでに持っていれば流用してokです。

googleのアカウント作成からAPIキーの作成までをまとめた記事を書いてますんで、必要な方は「YouTube APIを利用するための手順を画像付きで丁寧に解説する」を読んでみてください。

好きなチャンネルIDの取得

youtubeのチャンネルURLには一意の文字列が含まれていて「https://www.youtube.com/channel/〇〇〇」〇の部分がチャンネルIDになります。

詳しくは「YouTube APIの仕組みとブラウザだけでデータ取得する方法」 で解説してますんで読んでみてください。

youtube API searchリソースへリクエスト実行

youtubeAPIで特定チャンネルの動画を取得するには「https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=チャンネルID&order=date&key=APIキー」で取得できます。

ブラウザで上記のリクエストを実行すると、

json形式でデータ出力されるのでjson_decode後、変数に格納して必要なデータにアクセスすると値が表示されます。

リクエストURLで指定できるパラメータは複数あり、動画の件数を指定したりライブ配信だけ取得したりと色々できるので、パラメータをカスタマイズしたい方はリクエストURLを簡単に作れるツール「YouTube Data APIリクエストジェネレーター」を試してみてください。

任意の情報をDBへ格納

次はyoutube API searchリソースで取得したデータをデータベースに保存していきます。

データベースといっても情報を保存して読み書き出来ればいいだけなので、簡単なのはtxtファイルを簡易データベースとして利用する方法と、ワードプレスのmysqlデータベースにテーブルを新規作成する方法の2つだと思います。

今回は大量データにも対応できるように後者のmysqlにデータベースを作ってみます。

常に最新動画を表示する仕様なので古い動画データを保存し続ける必要はありません。なので、データベース更新のタイミングでデータベースごと削除と作成を行ってリセットするようにします。

youtubeAPIから取得したデータは何が入っているか分からないので、悪さをさせないようにsanitize関数を使って入力値をチェックしデータを無害化するのも忘れずに。

wp_schedule_eventで自動更新

ワードプレスには自動更新を行う便利なwp_schedule_event関数があるので、先ほど作成した動画を取得するコードを自動実行するように設定します。

#30分に1回設定
function my_add_intervals($schedules) {
    $schedules['30min'] = array(
        'interval' => 1800,
        'display' => __('30分に1度実行')
    );
    return $schedules;
}
add_filter( 'cron_schedules', 'my_add_intervals');

#cron登録処理
if ( !wp_next_scheduled( '作成した関数名' ) ) {
    date_default_timezone_set('Asia/Tokyo');
    wp_schedule_event( time(), '30min', '作成した関数名' );
}

上記の例では30分に1度更新するようにしてますが、my_add_intervals内のintervalパラメータに任意の秒数を指定する事で好きな時間に関数を実行させる事ができます。

ただし1回の実行ごとにyoutubeAPIのユニット数を100消費しますんで、実質100回/日分しか指定できません。つまり86,400秒(1日)/100回=864秒/回=14.4分/回となり、ユニット数の許す限り最速で更新したとしても理論上、864秒=14.4分が上限となります。

実際はAPIのリクエストが失敗したり色々細かい事が起こるんで、ピッタリ100回でAPIが使えなくなるという事ではないです。余裕をもって最速で20分に1回更新が現実的な所だと思います。

またワードプレスに登録したcronイベントは「WP Crontrol」というプラグインで確認する事ができるのでインストール推奨です。

ショートコードでページに表示させる

データベースに保存したYouTubeの動画データを呼び出すにはショートコードを記述します。今回は複数チャンネルの動画取得にも対応させたいので、ショートコードからチャンネルIDと表示したい動画の数をパラメータとして受け取るようにします。

ショートコードからパラメータを入力するやり方は最近使うようになったんですが、頻繁に使うショートコードならPHPコードをその都度書き換えるのも面倒なので、とても便利な事に気付きました。

完成したコピペコード

触る必要があるのは「function.php」「エディタに記述するショートコード」「カスタムCSS」の3本立てです。

function.php

#動画収集パーツ
function youtube_api_exe($youtube_apikey, $channelid){

    global $wpdb;
    $ycl_table_name = $wpdb->prefix . 'ycl_youtube_db';
    $option = array(
        'key' => $youtube_apikey,
        'part' => 'snippet',
        'forContentOwner' => null,
        'forMine' => null,
        'relatedToVideoId' => null,
        'channelId' => $channelid,
        'channelType' => null,
        'maxResults' => 6,
        'onBehalfOfContentOwner' => null,
        'order' => 'date',
        'pageToken' => null,
        'publishedAfter' => null,
        'publishedBefore' => null,
        'q' => null,
        'regionCode' => 'JP',
        'safeSearch' => null,
        'topicId' => null,
        'type' => 'video',
        'videoCaption' => null,
        'videoCategoryId' => null,
        'videoDefinition' => null,
        'videoDimension' => null,
        'videoDuration' => null,
        'videoEmbeddable' => null,
        'videoLicense' => null,
        'videoSyndicated' => null,
        'videoType' => null
    );

    $search_url = "https://www.googleapis.com/youtube/v3/search?".http_build_query($option, '&');
    $decode_search_url = htmlspecialchars_decode( $search_url );
    $youtube_search_res = wp_remote_get($decode_search_url);
    $time_stamp = date_i18n("Y-m-d H:i:s");

    #エラーハンドリング
    if ( is_wp_error( $youtube_search_res ) ) {
        $error_message = $youtube_search_res->get_error_message();
        error_log('error'.$error_message, 3, "./ycl-errors.log");
    } else if (wp_remote_retrieve_response_code( $youtube_search_res ) !== 200) {
        error_log($time_stamp." response code: ".wp_remote_retrieve_response_code( $youtube_search_res )."\n", 3, "./ycl-errors.log");
    } else {
        $youtube_search_res = json_decode($youtube_search_res["body"], true);
        $youtube_search_contents = $youtube_search_res["items"];
        foreach ($youtube_search_contents as $youtube_search_content){
            $wpdb->query( $wpdb->prepare( 
                "INSERT IGNORE INTO $ycl_table_name (import_date, channelId, channelTitle, videoId, publishedAt, title, description_text, thumbnails) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)", 
                $time_stamp, 
                sanitize_text_field($youtube_search_content['snippet']['channelId']), 
                sanitize_text_field($youtube_search_content['snippet']['channelTitle']), 
                sanitize_text_field($youtube_search_content['id']['videoId']), 
                sanitize_text_field($youtube_search_content['snippet']['publishedAt']), 
                sanitize_text_field($youtube_search_content['snippet']['title']), 
                sanitize_text_field($youtube_search_content['snippet']['description']), 
                sanitize_text_field($youtube_search_content['snippet']['thumbnails']['high']['url'])
            ));
        }
    }
}

#定期実行用
function youtube_content_get() {

    global $wpdb;
    $ycl_table_name = $wpdb->prefix . 'ycl_youtube_db';
    $ycl_charset_collate = $wpdb->get_charset_collate();
    
    #DBリセット処理
    $wpdb->query("DROP TABLE IF EXISTS $ycl_table_name");

    #DB作成・更新
    require_once(ABSPATH. 'wp-admin/includes/upgrade.php');
    $ycl_sql = "CREATE TABLE $ycl_table_name (
        id int NOT NULL AUTO_INCREMENT,
        import_date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
        channelId varchar(50) NOT NULL,
        channelTitle text NOT NULL,
        videoId varchar(50) NOT NULL,
        publishedAt datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
        title varchar(1000) DEFAULT '' NOT NULL,
        description_text text NOT NULL,
        thumbnails varchar(1000) DEFAULT '' NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY (videoId)
    ) $ycl_charset_collate;";
    dbDelta($ycl_sql);
    
    /*
    複数のチャンネルIDでそれぞれ最新動画を取得したい場合はyoutube_api_exeごと増やす
        youtube_api_exe("APIキー", "チャンネルID①");
        youtube_api_exe("APIキー", "チャンネルID②");
        youtube_api_exe("APIキー", "チャンネルID③");
    */

    youtube_api_exe("APIキー", "チャンネルid");
    
}
add_action ('youtube_content_get_cron', 'youtube_content_get');

#30分に1回設定
function my_add_intervals($schedules) {
    $schedules['30min'] = array(
        'interval' => 1800,
        'display' => __('30分に1度実行')
    );
    return $schedules;
}
add_filter( 'cron_schedules', 'my_add_intervals');

#cron登録処理
if ( !wp_next_scheduled( 'youtube_content_get_cron' ) ) {
    date_default_timezone_set('Asia/Tokyo');
    wp_schedule_event( time(), '30min', 'youtube_content_get_cron' );
}

function youtube_content_list_view($atts) {

    #db接続
    global $wpdb;
    $ycl_table_name = $wpdb->prefix . 'ycl_youtube_db';
    extract(shortcode_atts(array(
        'channelid' => '',
        'num'       => '',
    ), $atts));

    #入力値のサニタイズ
    $channelid = sanitize_text_field($channelid);
    $num = sanitize_text_field($num);

    $wpdb->query( $wpdb->prepare(
    	"SELECT * FROM $ycl_table_name WHERE channelId = %s LIMIT %d",
        $channelid, $num
    ));
    $ycl_db_res = json_decode(json_encode($wpdb), true);

    /*
    データベースに格納されている情報は下記の通り
    ・id:連番
    ・import_date:動画取り込み日時
    ・channelId:チャンネルID
    ・channelTitle:IDチャンネルタイトル
    ・videoId:動画ID
    ・publishedAt:動画公開日
    ・title:動画タイトル
    ・description_text:動画の概要欄の先頭から100文字
    ・thumbnails:サムネイルのURL
    各要素にデータを表示するには$ycl_db_res['カラム名']で表示されます、$ycl_db_res['title']とか$ycl_db_res['publishedAt']とか
    */

    #表示するhtml
    $view = '';
    $view .= '<div class="ycl-container">'."\n";
    foreach ($ycl_db_res['last_result'] as $ycl_db_res){
        $view .= '
        <div class="ycl-item">
            <a href="https://www.youtube.com/watch?v='.$ycl_db_res["videoId"].'" target="_blank">
                <img src="'.$ycl_db_res["thumbnails"].'">
                <p class="ycl-title">'.$ycl_db_res["title"].'</p>
            </a>
        </div>
        '."\n";
    }
    $view .= '</div>';
    return $view;
}
add_shortcode('ycl', 'youtube_content_list_view');

動画を表示させるショートコード

[ycl channelid=チャンネルID num=動画の表示数]

記事の本文・サイドバー・ヘッター・フッター・テーマの好きな場所どこでも使えます。

カスタムCSS

flexで横に並べて三分割で表示してみました。

/* 親要素(コンテナ) */
.ycl-container {
  display: flex;
  flex-wrap: wrap;
}
/* 子要素(アイテム) */
.ycl-item {
  width: calc(100% / 3);
  margin: 10px auto;
  padding: 5px 10px;
  box-sizing: border-box;
  color: #fff;
}
.ycl-item a{
  text-decoration: none
}
.ycl-item p{
  margin:0em 0em 0em 0em;
}

デフォルトではaタグとpタグをdivで囲っている状態なので、flexboxとか使えばピッタリ横並びのボックスになると思います。

width: calcは今回動画を6個取得して2段3列に表示するためなので、動画の表示個数に応じて調整して下さい。サイドバーに動画1個だけ表示するとかなら消しちゃってokです。

ちなみにCSSはテーマエディターよりも「外観 > カスタマイズ > 追加CSS」から記述した方がプロパティの予測変とかしてくれるので便利だったりします。

使い方

使い方は簡単ですが動画の収集と表示に分けてポイントを書いておきます。PHPコードを多少いじる必要があるので、;とか{}が過不足するとエラーが表示されるので気を付けて作業して下さい。

動画の収集

動画の収集は「APIキー分散(制限回避)」と「複数チャンネル対応」を簡単に行う事ができます。

function.phpの108行目に記述されている「youtube_api_exe(“APIキー”, “チャンネルid”);」がyoutubeAPIを実行するトリガーなので、収集したいチャンネルの分だけ増やす事で簡単に複数チャンネルの最新動画を自動更新できます。

コードの一部を抜粋して具体的に説明すると、

    /*
    複数のチャンネルIDでそれぞれ最新動画を取得したい場合はyoutube_api_exeごと増やす
        youtube_api_exe("APIキー①", "チャンネルID①");
        youtube_api_exe("APIキー②", "チャンネルID②");
        youtube_api_exe("APIキー③", "チャンネルID③");
    */

    youtube_api_exe("APIキー①", "UCpGpA7mSYmNJjLiJxKso5QA"); #しもふりチューブ
    youtube_api_exe("APIキー②", "UCIR2mQ77wHrLMreV45nYhgw"); #かまいたちチャンネル
    youtube_api_exe("APIキー③", "UCRYWF2XsVvwvSqMrK4FXz3g"); #よゐこチャンネル
    
}
add_action ('youtube_content_get_cron', 'youtube_content_get');

こんな感じでyoutube_api_exeを増やすとそれぞれのチャンネルから30分に1回、新しい順に6件の動画を取得しデータベースに保存してくれます。

APIキーは指定したいチャンネル毎に別の物を準備した方がユニットの消費を分散させられるのでいいと思います。

動画の取得件数を変えたい場合は、function.phpに「’maxResults’ => 6,」という記述があるんで好きな数字に変えて下さい。デフォルトは6なので動画を6件取得します。

動画の表示

ショートコードを張り付けるだけで好きな場所に自動更新される動画枠に置き換わります。

ショートコードの中にはチャンネルIDと動画表示数を指定するパラメータを設定できるので、必ず入力して下さい。

ショートコードなのでワードプレスブログ内の好きな場所に記述する事ができます、ただデフォルトでは適応されるCSSは共通なので、htmlの出力やCSSをカスタマイズしていい感じに調整しちゃって下さい。

htmlの出力はfuction.phpの164-176行目に記述してますんで、ここら辺を好きな形にカスタムして下さい。

エラーの確認

youtubeAPI実行時に発生したエラーはワードプレスフォルダ直下に「ycl-errors.log」という名前で出力されますんで、動画が取得できない場合などは確認して原因をググってみて下さい。

動画が取得できない場合やエラーが出る原因のほどんどは、youtubeAPIキーのセキュリティ設定が原因です。

キーの制限を一度なしにしてエラー消えるか試してみて下さい、意味不明な場合はお問い合わせか当記事のコメントから連絡して下さい。

まとめ

今回は自分の推しチャンネルの最新動画を表示するコードを書いてみました。分からない事や依頼があれば受け付けてますんでお問い合わせからご連絡下さいm(_ _)m

コメント