特定のYouTubeチャンネルのコメント一覧をAPIで取得してホームページに表示してみた

上杉

まずは実装したPHPコードを紹介します

YouTube APIを使用してチャンネルIDからコメント一覧をjsonファイルに保存

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;

$apiKey = '';
$channelId = 'UCv1JdLgLvqF-6B7XJoOCw-Q';

$client = new Client([
  'base_uri' => 'https://youtube.googleapis.com/youtube/v3/',
]);

// トップレベルのコメント取得
$response = $client->request('GET', 'commentThreads', [
  'query' => [
    'part' => 'snippet',
    'allThreadsRelatedToChannelId' => $channelId,
    'maxResults' => 5,
    'key' => $apiKey,
  ]
]);

$body = $response->getBody();
$contents = $body->getContents();

$commentThreads = json_decode($contents, true)['items'];

foreach ($commentThreads as $index => $thread) {
    $videoId = $thread['snippet']['topLevelComment']['snippet']['videoId'];

    // 動画の詳細を取得
    $videoResponse = $client->request('GET', 'videos', [
        'query' => [
            'part' => 'snippet',
            'id' => $videoId,
            'key' => $apiKey,
        ]
    ]);

    $videoBody = $videoResponse->getBody();
    $videoContents = $videoBody->getContents();
    $videoDetails = json_decode($videoContents, true);

    // 動画のタイトルとサムネイルURLを取得
    $videoTitle = $videoDetails['items'][0]['snippet']['title'];
    $thumbnailUrl = $videoDetails['items'][0]['snippet']['thumbnails']['medium']['url']; // 480x360サイズ

    // コメントに動画のタイトルとサムネイルURLを追加
    $commentThreads[$index]['videoTitle'] = $videoTitle;
    $commentThreads[$index]['thumbnailUrl'] = $thumbnailUrl;
}

$file = 'comments.json';

// コメントをJSONファイルに保存
file_put_contents($file, json_encode($commentThreads, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

echo "Comments with video titles and thumbnails saved to $file";
?>

jsonファイルを取得しホームページへ出力

<?php

$url = "comments.json";

// cURLセッションを初期化
$curl = curl_init($url);

// cURLオプションを設定
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

// URLの内容を取得
$response = curl_exec($curl);

// エラーチェック
if (curl_errno($curl)) {
  echo 'cURL Error: ' . curl_error($curl);
} else {
  // JSONデータをデコード
  $comments = json_decode($response, true);

  if (is_array($comments)) {
    // 配列を逆順に処理
    // $comments = array_reverse($comments);

    echo '<ul class="c-cardB">';

    // 各コメントを処理
    foreach ($comments as $comment) {
      // 指定されたIDを持つコメントを除外
      if ($comment['id'] === "UgyzKP7svZFZTSNMFzF4AaABAg") {
        continue;
      }

      $commentSnippet = $comment['snippet']['topLevelComment']['snippet'];
      $textDisplay = $commentSnippet['textDisplay'];
      $publishedAt = $commentSnippet['publishedAt'];
      $authorDisplayName = $commentSnippet['authorDisplayName'];
      $authorProfileImageUrl = $commentSnippet['authorProfileImageUrl'];
      $videoId = $comment['snippet']['videoId'];
      $videoTitle = $comment['videoTitle'];
      $thumbnailUrl = $comment['thumbnailUrl'];

      // 日付をY.m.d形式に変換
      $date = new DateTime($publishedAt);
      $formattedDate = $date->format('Y.m.d');

      // リストアイテムとして表示
      echo "
      <li>
        <a href='https://www.youtube.com/watch?v=$videoId' target='_blank' rel='noopener noreferrer'>
          <div class='c-cardB__flex'>
            <div class='c-cardB__head'>
              <span class='c-cardB__meta'>
                <img class='proficon' src='$authorProfileImageUrl' alt=''>
                <span class='name'>$authorDisplayName</span>
                <span class='date'>$formattedDate</span>
              </span>
              <p class='c-cardB__text'>$textDisplay</p>
            </div>
            <div class='c-cardB__thumb'>
              <img src='$thumbnailUrl' alt=''>
            </div>          
          </div>
          <p class='c-cardB__videotitle'><span>$videoTitle</span> へのコメント</p>
        </a>
      </li>
      ";
    }

    echo '</ul>';
  } else {
    echo "JSONデータのデコードに失敗しました。";
  }
}

// cURLセッションを終了
curl_close($curl);
?>

今回出力されたHTMLコード

<ul class="c-cardB">
  <li>
    <a href="https://www.youtube.com/watch?v=Asvb1ChLU9A" target="_blank" rel="noopener noreferrer">
      <div class="c-cardB__flex">
        <div class="c-cardB__head">
          <span class="c-cardB__meta">
            <img class="proficon" src="https://yt3.ggpht.com/L1PnRS1Q5BzLkDmHbtQxmaSQQGJFLwaLwga4lZ3VPxAnO5n99zdvhbZ-miFJ9f-JOv5sbdYk=s48-c-k-c0x00ffffff-no-rj" alt="">
            <span class="name">@user-xc6tr4wh3h</span>
            <span class="date">2023.11.23</span>
          </span>
          <p class="c-cardB__text">動画と違うコメでスミマセン<br>柔整師の方に伺いたくて<br>交通事故で数ヶ月接骨院に通っていたのですが、示談が終わると<br>『みんなお礼に示談金の10~20%は持ってくるよ』って言われてしまって<br>ご近所の先生なので、お礼をしにお菓子位はお持ちするつもりだったのですが<br>そんなに皆さん現金なんてお渡しするものなんでしょうか?<br>治療費でそこそこ儲かってると思うのですが</p>
        </div>
        <div class="c-cardB__thumb">
          <img src="https://i.ytimg.com/vi/Asvb1ChLU9A/mqdefault.jpg" alt="">
        </div>          
      </div>
      <p class="c-cardB__videotitle"><span>吉田接骨院の治療器具の紹介</span> へのコメント</p>
    </a>
  </li>
  
  <li>
    <a href="https://www.youtube.com/watch?v=4mu42Q2XK1I" target="_blank" rel="noopener noreferrer">
      <div class="c-cardB__flex">
        <div class="c-cardB__head">
          <span class="c-cardB__meta">
            <img class="proficon" src="https://yt3.ggpht.com/ytc/APkrFKaGUthhtXeW2hQX-KhNS4rk9-U-vLIS7ltOTg=s48-c-k-c0x00ffffff-no-rj" alt="">
            <span class="name">@user-xg5tg1nm8g</span>
            <span class="date">2023.09.19</span>
          </span>
          <p class="c-cardB__text">長年バネ指で病院や専門医整骨院整体かかるも改善せず、痛み酷くなると注射過去6回打ちました。ありとあらゆるユーチューブの整体師等見てやるも全て、少しの改善も無く、今回貴殿のを拝見、試したら、効果感じました。バネ指になる前に首が回らなくなり、医者駄目で、自分で治しました。因果関係ありますね‼️他のユーチューブはばね指の仕組み能書きばかりで本体は少しで、貴殿は端的明快で、素晴らしいです。私も勉強して、手首腕のスジを何とかする事はわかるのですが、もんでもなかなか良くはなりませんが、貴殿のを試したら、効くのが即分かるので、試して見ます。本当にありがとうございます。</p>
        </div>
        <div class="c-cardB__thumb">
          <img src="https://i.ytimg.com/vi/4mu42Q2XK1I/mqdefault.jpg" alt="">
        </div>          
      </div>
      <p class="c-cardB__videotitle"><span>ばね指の症状とお家でできる体操を紹介!</span> へのコメント</p>
    </a>
  </li>
  
  <li>
    <a href="https://www.youtube.com/watch?v=cWPuyZVZ0BE" target="_blank" rel="noopener noreferrer">
      <div class="c-cardB__flex">
        <div class="c-cardB__head">
          <span class="c-cardB__meta">
            <img class="proficon" src="https://yt3.ggpht.com/AcBibzp7k2EB2MJtsvh14bGfU7JpjDCfVtBqBBltlnpoVuEe9sFpNBH2xvw-bOsu-aMKARP2=s48-c-k-c0x00ffffff-no-rj" alt="">
            <span class="name">@user-gg6lg4zy3l</span>
            <span class="date">2023.08.23</span>
          </span>
          <p class="c-cardB__text">始まりは左右ともにでしたが、<br>右のみは自然に治りました。<br>筋肉痛なのかなと思い左だけは痛みが始まり1ヶ月以上痛みが続いているままです。</p>
        </div>
        <div class="c-cardB__thumb">
          <img src="https://i.ytimg.com/vi/cWPuyZVZ0BE/mqdefault.jpg" alt="">
        </div>          
      </div>
      <p class="c-cardB__videotitle"><span>四十肩・五十肩の原因と治療法【肩関節周囲炎】</span> へのコメント</p>
    </a>
  </li>
  
  <li>
    <a href="https://www.youtube.com/watch?v=mr97eOYmwA8" target="_blank" rel="noopener noreferrer">
      <div class="c-cardB__flex">
        <div class="c-cardB__head">
          <span class="c-cardB__meta">
            <img class="proficon" src="https://yt3.ggpht.com/ytc/APkrFKZtFAfw0EMDmD02m0tKPEu2H116bU1yVwEnrzZGfcm4DvwbLF_oim4rF4aet9PzxT0rpTU=s48-c-k-c0x00ffffff-no-rj" alt="">
            <span class="name">@user-lo5dj6jj5z</span>
            <span class="date">2023.08.12</span>
          </span>
          <p class="c-cardB__text">以前、腰痛体操を教えていただき、<br>調子がいいので、ずっと続けています。<br>引っ越しをしたので通院はできなくなりましたが、<br>動画を楽しみに<br>体操、参考にします。😊</p>
        </div>
        <div class="c-cardB__thumb">
          <img src="https://i.ytimg.com/vi/mr97eOYmwA8/mqdefault.jpg" alt="">
        </div>          
      </div>
      <p class="c-cardB__videotitle"><span>ネットで話題のあのストレッチは本当に効果がある?柔道整復師に聞いてみました【肩こり・腰痛・猫背・反り腰】</span> へのコメント</p>
    </a>
  </li>
  
  <li>
    <a href="https://www.youtube.com/watch?v=IzcFVu9R014" target="_blank" rel="noopener noreferrer">
      <div class="c-cardB__flex">
        <div class="c-cardB__head">
          <span class="c-cardB__meta">
            <img class="proficon" src="https://yt3.ggpht.com/6S_cR23AZbktA9GOxydcmjs1paRBQCtUsObw6A8w4pOnfOHNpUzg7DCvJL91K1aajcXQM6ID=s48-c-k-c0x00ffffff-no-rj" alt="">
            <span class="name">@user-cm2qh6or3y</span>
            <span class="date">2023.07.01</span>
          </span>
          <p class="c-cardB__text">自分は今50の母をもつ高1です。親には秘密でコメントします。最近母親はリウマチと病院から言われたそうで自分は調べたところ手首のこわばり、体重の減少、貧血などの症状が母に当てはまっててとても心配です。病院の先生からはグレートゾーンと言われたそうで半年後にまたもう一度検査すると言われたそうです。ですがネットを見ると半年が勝負とか書いており母親にもそう言いました。なんかあったらすぐ行くよと言っているのですが今からやれば治るものなんですか?病院からはレベル1にもまだ行ってないと言われたそうなんですが、ネットで見たところ症状が当てはまってるものが多く心配です。すぐに病院に行って欲しいです。もし歩けなくなったら抗がん剤を使うようになったら本当に嫌です。母が好きなので。いますが病院に行くべきですよね?今母は余裕で動けています。ですが昔から腰が痛い全身が痛いなどは何度も聞きます。前の検査ではそんなだったのに今回でひどくなったそうです。コメント長い文章ですみません。語彙力もなくて読みづらいと思いますが</p>
        </div>
        <div class="c-cardB__thumb">
          <img src="https://i.ytimg.com/vi/IzcFVu9R014/mqdefault.jpg" alt="">
        </div>          
      </div>
      <p class="c-cardB__videotitle"><span>女性に多いリウマチってどんな病気?#shorts</span> へのコメント</p>
    </a>
  </li>
</ul>

ざっくり解説

まず前提として

  • GoogleAPIライブラリインストール
  • APIキー取得

が必要です。

参考

サンプルコードではチャンネルに投稿された最新5件のコメントをjsonに保存

  • コメントした人のサムネイル
  • ユーザーネーム
  • 日時
  • 動画ID
  • 動画タイトル
  • 動画サムネイル

などを表示できるようにしています

  • ページ読み込み毎に毎回APIを呼び出していてはAPI利用制限がかかるので、毎日1回だけget_comment.phpをcronで呼び出してコメントをjsonに保存するようにしています
  • ページ読み込み時はphpでjsonを取得しWebサイトへ展開しています
  • チャンネルオーナーのコメントは除外しています

YouTubeコメントをどう活用するか

吉田接骨院のトップページ下部でコメント一覧を表示させています。

コメントを表示させることでシンプルにYouTubeチャンネルに興味を持ってもらうことができます。

さらにYouTubeのコメントを見た新規の患者様が、クリニックの雰囲気をよりよく理解できる手助けになることも期待しています

上杉

記事ごとにYouTubeコメントを表示させたりするのもおもしろそうですね!

おまけコード

以下のコードはテストしていませんが一応おいておきます

コメントの差分だけ取得して増やしていくコード

例ではその日の時点からの最新5件を取得するので古いコメントは消えるため、全てのコメントを保存しておきたい場合は毎回jsonを比較しマージする必要があります

マージした場合のコードです

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;

$apiKey = '';
$channelId = 'UCv1JdLgLvqF-6B7XJoOCw-Q';

$client = new Client([
  'base_uri' => 'https://youtube.googleapis.com/youtube/v3/',
]);

$response = $client->request('GET', 'commentThreads', [
  'query' => [
    'part' => 'snippet',
    'allThreadsRelatedToChannelId' => $channelId,
    'maxResults' => 20,
    'key' => $apiKey,
  ]
]);

$body = $response->getBody();
$contents = $body->getContents();

$newComments = json_decode($contents, true)['items'];

$file = '../comments.json';

// Load existing comments
if (file_exists($file)) {
  $existingComments = json_decode(file_get_contents($file), true);
} else {
  $existingComments = [];
}

// Merge new comments
foreach ($newComments as $newComment) {
  $isNew = true;
  foreach ($existingComments as $existingComment) {
    if ($existingComment['id'] === $newComment['id']) {
      $isNew = false;
      break;
    }
  }
  if ($isNew) {
    array_push($existingComments, $newComment);  // Add new comments at the beginning
  }
}

// Save merged comments
file_put_contents($file, json_encode($existingComments, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

echo "Merged comments saved to $file";

返信も取得するコード

コメントに対する返信も取得する場合はトップレベルのコメントとそのコメントに対する返信を統合する必要があります

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;

$apiKey = '';
$channelId = 'UCv1JdLgLvqF-6B7XJoOCw-Q';

$client = new Client([
  'base_uri' => 'https://youtube.googleapis.com/youtube/v3/',
]);

// トップレベルのコメント取得
$response = $client->request('GET', 'commentThreads', [
  'query' => [
    'part' => 'snippet',
    'allThreadsRelatedToChannelId' => $channelId,
    'maxResults' => 10,  // 最新の10件を取得
    'key' => $apiKey,
  ]
]);

$body = $response->getBody();
$contents = $body->getContents();

$commentThreads = json_decode($contents, true)['items'];

// 返信の取得と統合
foreach ($commentThreads as $index => $thread) {
    $topLevelCommentId = $thread['snippet']['topLevelComment']['id'];
    $totalReplyCount = $thread['snippet']['totalReplyCount'];

    if ($totalReplyCount > 0) {
        // 返信が存在する場合、それらを取得
        $repliesResponse = $client->request('GET', 'comments', [
            'query' => [
                'part' => 'snippet',
                'parentId' => $topLevelCommentId,
                'maxResults' => $totalReplyCount,
                'key' => $apiKey,
            ]
        ]);

        $repliesBody = $repliesResponse->getBody();
        $repliesContents = $repliesBody->getContents();
        $replies = json_decode($repliesContents, true)['items'];

        // トップレベルのコメントに返信を統合
        $commentThreads[$index]['replies'] = $replies;
    }
}

$file = '../comments.json';

// コメントをJSONファイルに保存
file_put_contents($file, json_encode($commentThreads, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

echo "Comments and replies saved to $file";
?>
動画でも話しました!
ブログ一覧へ