2018年4月12日木曜日

YouTube コメントの全取得

YouTubeの動画の下の方にコメントがありますね。
長いコメントは詳細を押さないと表示できなかったり、件数が多いと下へのスクロールを繰り返しさないといけなくて煩わしいと思ったことはありませんか?ということでコメントの全取得に挑戦します。
コマンドプロンプトで確認した後はhtmlで取得という二段階にわけて説明します。
使用するAPIは2つ。
APIの情報は親コメントがこちらで子のコメントはこちらです。

今回のAPI使用方法のポイントとしては、コメントの件数が多く一回の読み出しで終わらない場合は、["nextPageToken"]を使い&pageToken=にその値を入れてURLに付与することにより続きを読み出すことができます。
また、コメントは返信による子コメントがあります。
["totalReplyCount"]が1以上であれば子コメントが存在するので、commentThreadsのAPIで読み出します。件数が多い場合もあるのでpageTokenを使い終わるまで読みだしています。 子コメントに関わる変数名は$○○2としています。
またAPIの使用回数を減らすため 親コメントはmaxResults=100、子コメントはmaxResults=50をURLに付与しています。

コマンドプロンプトで行う場合の仕様
・textデータとして表示する
・タブでインデントぽくする
・絵文字はあきらめる
・アイコン画像はあきらめる
・コメントの並びは新しい順
・日時情報は省略
です。コードが以下になります。
①のはAPIキー、②には今回は動画のidを入れます。
https://www.youtube.com/watch?v=xxxxxxxxxxx
のxxxxxxxxxxxの文字列のことです。

comment.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?php
 
$key = "①";
$videoId = "②";
 
$search_api_base = "https://www.googleapis.com/youtube/v3/commentThreads?part=snippet,replies";
$search_api_base2 = "https://www.googleapis.com/youtube/v3/comments?part=snippet";
 
$search_api = $search_api_base . "&videoId=" .$videoId. "&key=" . $key ."&textFormat=plainText&maxResults=100";
$url = $search_api;
$next = "";
while(1)
{
 if($next)
 {
  $url = $search_api . "&pageToken=" . $next;
 }
  
 
 $search_contents = file_get_contents($url);
 $search_json = json_decode($search_contents,true);
 
 foreach($search_json["items"] as $k => $data)
 {
  echo $data["snippet"]["topLevelComment"]["snippet"]["authorDisplayName"];
  echo PHP_EOL;
  echo "\t";
  $str = str_replace("\n","\n\t",$data["snippet"]["topLevelComment"]["snippet"]["textDisplay"]);
  print_r($str);
 
  echo PHP_EOL;
  echo PHP_EOL;
 
  if($data["snippet"]["totalReplyCount"] >= 1)
  {
   $next2 = "";
   $data2array = array();//子コメント配列の初期化
    
   while(1)
   {
    $parentId = $data["snippet"]["topLevelComment"]["id"];
    $search_api2 = $search_api_base2 . "&parentId=" . $parentId . "&key=" . $key ."&textFormat=plainText&maxResults=50";
    $url2 = $search_api2;
 
    if($next2)
    {
     $url2 = $search_api2 . "&pageToken=" . $next2;
    }
 
    $search_contents2 = file_get_contents($url2);
    $search_json2 = json_decode($search_contents2,true);
 
    foreach($search_json2["items"] as $k2 => $data2)
    {
     array_push($data2array,$data2);
    }
 
    if(empty($search_json2["nextPageToken"]) == FALSE)
    {
     $next2 = $search_json2["nextPageToken"];
    }
    else
    {
     //子コメントを古い順にするためひっくり返す
     $data2array = array_reverse($data2array);
     foreach($data2array as $k2 => $data2)
     {
      echo "\t";
      echo $data2["snippet"]["authorDisplayName"];
      echo PHP_EOL;
      echo "\t\t";
      $str = str_replace("\n","\n\t\t",$data2["snippet"]["textDisplay"]);
      print_r($str);
      echo PHP_EOL;
      echo PHP_EOL;
     }
     unset($data2array);
     break;
    }
   }
  }
 }
 
 if(empty($search_json["nextPageToken"]) == FALSE)
 {
  $next = $search_json["nextPageToken"];
 }
 else
 {
  break;
 }
}
 
?>


注釈として
子コメントも親コメントと同様に新しい順で読み出されるため、一度全部配列に保存しから 65行目でひっくり返して古い順にして読みやすくしています。
28と72行目では改行文字を改行文字と水平タブに置換することによって無理やりインデントを作っています。


動作確認がしたらコマンドプロンプトで以下のように[>>test.txt]や[>test.txt]とすればファイルに保存されます。
>は、新規ファイルを作成して実行結果を出力する
>>は、既存ファイルを作成して実行結果を出力する。ファイルなければ>と同じ動作
です。
結果の例はこんな感じです。一応ぼかしてあります。



では、次にhtmlです。
html(ブラウザ)で行う場合の仕様
・htmlデータとして表示する
・アイコン画像を表示する
・コメントの並びは新しい順
・日時情報は省略
です。コードが以下になります。



comment2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<!DOCTYPE html>
<html>
    <head>
        <title>コメント出力</title>
    </head>
    <body>
 
<?php
 
$key = "①";
$videoId = "②";
 
$search_api_base = "https://www.googleapis.com/youtube/v3/commentThreads?part=snippet,replies";
$search_api_base2 = "https://www.googleapis.com/youtube/v3/comments?part=snippet";
 
$search_api = $search_api_base . "&videoId=" .$videoId. "&key=" . $key ."&textFormat=html&maxResults=100";
$url = $search_api;
$next = "";
while(1)
{
    if($next)
    {
        $url = $search_api . "&pageToken=" . $next;
    }
     
 
    $search_contents = file_get_contents($url);
    $search_json = json_decode($search_contents,true);
 
    foreach($search_json["items"] as $k => $data)
    {
        $image = $data["snippet"]["topLevelComment"]["snippet"]["authorProfileImageUrl"];
        echo "<img src=$image>";
        echo $data["snippet"]["topLevelComment"]["snippet"]["authorDisplayName"];
        echo "<br>";
        print_r($data["snippet"]["topLevelComment"]["snippet"]["textDisplay"]);
        echo "<br>";
        echo "<br>";
         
        if($data["snippet"]["totalReplyCount"] >= 1)
        {
            $next2 = "";
            $data2array = array();//子コメント配列の初期化
             
            while(1)
            {
                $parentId = $data["snippet"]["topLevelComment"]["id"];
                $search_api2 = $search_api_base2 . "&parentId=" . $parentId . "&key=" . $key ."&textFormat=html&maxResults=50";
                $url2 = $search_api2;
 
                if($next2)
                {
                    $url2 = $search_api2 . "&pageToken=" . $next2;
                }
 
                $search_contents2 = file_get_contents($url2);
                $search_json2 = json_decode($search_contents2,true);
 
                foreach($search_json2["items"] as $k2 => $data2)
                {
                    array_push($data2array,$data2);
                }
                 
                if(empty($search_json2["nextPageToken"]) == FALSE)
                {
                    $next2 = $search_json2["nextPageToken"];
                }
                else
                {
                    //子コメントを古い順にするためひっくり返す
                    $data2array = array_reverse($data2array);
                    echo "<p style=\"padding-left:2em\">";
                    foreach($data2array as $k2 => $data2)
                    {
                        $image = $data2["snippet"]["authorProfileImageUrl"];
                        echo "<img src=$image>";
                        echo $data2["snippet"]["authorDisplayName"];
                        echo "<br>";
                        print_r($data2["snippet"]["textDisplay"]);
                        echo "<br>";
                        echo "<br>";
                    }
                    echo "</p>";
                    unset($data2array);
                    break;
                }
            }
        }
    }
 
    if(empty($search_json["nextPageToken"]) == FALSE)
    {
        $next = $search_json["nextPageToken"];
    }
    else
    {
        break;
    }
}
 
?>
 
     </body>
</html>
?>


注釈として
16と48行目ではtxetformatをhtmlにしています。
72と83行目でインデントを作ってます。
htmlなので改行の表現が変わっています。
phpのechoで出力していた部分をhtmlの構文で出力すれば対応するのでそうしています。

これを実行する場合はブラウザのURLに
「http://localhost/comment2.php」と入力してみてください。(C:\xampp\htdocs直下にファイルがある場合)
 結果はこんな感じです。アイコン画像も表示されていい感じです。絵文字もでます。


コードの改善点として、
動画のタイトルとか入れたほうが保存した後わかりやすいかなとか、動画のIDを入力してボタンを押して出力できるようとかありますね。
コメントに対する高評価・低評価はスルーしました。
APIは低評価は取得できないのでYouTubeのページと同じにはできないようです。
コメントに対する評価や返信もできません。
日時情報は省略してますががちょっと追加するだけです。
まぁ読む専門の作りなのでこんな感じです。

2 件のコメント:

  1. Youtube Data API・PHPの初心者なので、このページの内容はとても参考になります。
    質問ですが、特定の動画のコメントではなく、チャネル全体のコメントを表示したいです。https://developers.google.com/youtube/v3/docs/commentThreadsを読んで、snippet.videoIdに値を入れなければ特定の動画ではなくチャネルのコメントが表示される、みたいな記述があったので、プログラムの$videoIdを単純に削除して試してみたのですが、エラーとなってしまいました。どうしたらいいか分からず途方に暮れています。どのようにすればいいかご教授いただきたいです。よろしくお願いします。

    返信削除
  2. 自己解決しました。
    allThreadsRelatedToChannelIdを使うんですね。お騒がせしました。

    返信削除