【GAS】Youtube Data Apiを使ってYouTube 動画コメントを全取得する

# はじめに
これは、[Spreadsheets/Excel Advent Calendar 2020](https://adventar.org/calendars/5097)の25日目の記事となります。
6日遅れの大晦日に書いていますけどね。

* [【Python】Youtube Data Apiを使ってYouTube 動画コメントを全取得する](https://qiita.com/yaju/items/3bec88dbd544502e1343)
* [【VBA】ExcelでYoutube Data Apiを使ってYouTube 動画コメントを全取得する](https://qiita.com/yaju/items/dd0d8359d8f926c32627)
* [【VBA】Excel for MacでYoutube Data Apiを使ってYouTube 動画コメントを全取得する](https://qiita.com/yaju/items/eb47e20446d7e43d8e55)
* [【.NET】Youtube Data Apiを使ってYouTube 動画コメントを全取得する](https://qiita.com/yaju/items/9763d0267ebbe3f3de69)

どんだけ同じネタを使い回すのか、とはいえ、GoogleAppsScrip(GAS)版を作ってなかったんだよね。

# 環境
* Windows 10 Home
* Google.Apis.Youtube.v3 v1.49.0.2173

## 準備
Apps Script画面のサービスにて、YouTube Data APIを追加してください。
サービスの横の「＋」をクリックし、YouTube Data API を選択して追加します。

https://matsukiyoshi.com/gas-youtube/

# 仕様
コメントのフォーマットはhtmlかplain textがあるのですが、plain textになっています。
並び順はrelevanceで評価順が多い順にしています。子コメントについては順不動（並び順を指定しても動画サイトと同じにならない）です。

※APIの使用回数を減らすため 親コメントはmaxResults=100、子コメントはmaxResults=50とする。
※コメントでは低評価(disLike)数は取得できない。

```
(連番)         (親コメント) （グッド数） （投稿者名） （投稿日時) （返信数)
(連番) (子連番) (子コメント) （グッド数） （投稿者名） （投稿日時) 
```

【2021/05/28追記】
並び順を評価順(relevance)にした場合、YouTube Data API v3の制約上2000件を超える親コメントが取得できません。
並び順を新しい順(time)にした場合、上限が不明ですが2000件を超える親コメントが取得できます。

# 使用方法
```javascript
function getYouTubeComment() {
  let video_list = ['(Video IDを入力)'];
  for (let i = 0; i < video_list.length; i++) {
    let video_id = video_list[i];
    getComment(i, video_id);
  }
}
```

シート名には動画のタイトルがセットされます。
今回はリストになっているため、複数指定することができシートごとに動画コメントが出力されます。

```javascript
let video_list = ['(Video IDを入力)', '(Video IDを入力)'];
```

## Video IDを入力
例えば、「[https://www.youtube.com/watch?v=oeJ_b0iG9lM](https://www.youtube.com/watch?v=oeJ_b0iG9lM)」であれば、`oeJ_b0iG9lM`がVideo IDとなりますので、プログラムの(Video IDを入力)を対象動画のVideo IDに書き換えてください。

:::note warn
なお、Video IDに書き換える場合には括弧は不要です。気を付けてください。

```javascript
let video_list = ['oeJ_b0iG9lM'];
```
:::

# プログラム
```javascript
function getYouTubeComment() {
  let video_list = ['(Video IDを入力)'];
  for (let i = 0; i < video_list.length; i++) {
    let video_id = video_list[i];
    getComment(i, video_id);
  }
}

// コメント取得
function getComment(i, video_id) {
  let ss = SpreadsheetApp.getActiveSpreadsheet();
  let pageToken = '';

  let video = YouTube.Videos.list('id,snippet, statistics', {id: video_id,});
  
  let sh;
  if (i == 0) {
    sh = ss.getActiveSheet();
  } else {
    sh = ss.insertSheet();
  }
  sh.setName(video.items[0].snippet.title);
  sh.clear();
  
  let row = { value: 1 };
  let values = new Array('連番', '子連番', 'コメント', 'グッド数', '投稿者名', '投稿日時', '返信数');
  output(row, sh, values);
      
  let no = 1;
  while (true) {
    let comment_list = YouTube.CommentThreads.list('id, replies, snippet', {
      videoId: video_id,
      maxResults: 100,
      order: 'relevance',
      textFormat: 'plaintext',   
      pageToken: pageToken,
    });
  
    for (let j = 0; j < comment_list.items.length; j++) {
      // 総返信数
      let replyCount = comment_list.items[j].snippet.totalReplyCount;
      let item = comment_list.items[j].snippet.topLevelComment.snippet;
      outputItem(no, '', row, sh, item, replyCount);

      if(replyCount > 0) {
        if(replyCount == comment_list.items[j].replies.comments.length) {
          for (let k = 0; k < comment_list.items[j].replies.comments.length; k++) {
            let item2 = comment_list.items[j].replies.comments[k].snippet;
            outputItem(no, k + 1, row, sh, item2, '');
          }
        }
        else{
          // 親ID
          let parentID = comment_list.items[j].snippet.topLevelComment.id;
          getReplyComment(sh, no, row, parentID);
        }
      }
      no++;
    }

    pageToken = comment_list.nextPageToken;
    if (typeof pageToken == 'undefined') {
      break;
    }
  }
}

// 返信コメント取得
function getReplyComment(sh, no, row, parentID) {
  let cno = 1;
  let pageToken = '';
  while (true) {
    let comment_list = YouTube.Comments.list('id, snippet', {
      maxResults: 50,
      parentId : parentID,
      textFormat: 'plaintext',   
      pageToken: pageToken,
    });

    for (let j = 0; j < comment_list.items.length; j++) {
      let item = comment_list.items[j].snippet;
      outputItem(no, cno, row, sh, item, '');
      cno++;
    }

    pageToken = comment_list.nextPageToken;
    if (typeof pageToken == 'undefined') {
      break;
    }
  }    
}

// データ出力
function outputItem(no, cno, row, sh, item, replyCount) {
  // コメント
  let text = item.textDisplay;
  // グッド数
  let likeCount = item.likeCount;
  // ユーザー名
  let authorName = item.authorDisplayName;
  // 投稿日時
  let publishedAt = localDate(item.publishedAt);

  values = Array(no, cno, text, likeCount, authorName, publishedAt, replyCount);
  output(row, sh, values);
}

// Google Spread Sheetに出力
function output(row, sh, values) {
  for(let i = 0; i < values.length; i ++) {
    sh.getRange(row.value, i + 1).setValue(values[i]);
  }
  row.value++;
}

// 日本日時に変換
function localDate(dt) {
  return Utilities.formatDate(new Date(dt), 'JST', 'yyyy-MM-dd HH:mm:ss');
}
```

# 実行結果
郡司りかさんを御存知でしょうか？
マツコ・デラックスさんと村上信五（関ジャニ∞）さんがMCの「月曜から夜更かし」の中で、運動音痴として注目を浴びた女性です。
郡司りかさんはYoutubeとTwitterをやられており、ツイート画像に「おはよー」を隠すという遊びをしています。
[郡司りかさんのツイート画像の「おはよー」を見つける](https://note.com/yaju/n/n80d0dab9b192)

「[【NiziU】郡司さんMake You Happyにチャレンジ (練習前)](https://www.youtube.com/watch?v=swZVrg3H-8E)」の動画コメントを取得すると、下記のようになります。
![GASYoutubeAPI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/336bb280-e212-20c2-89d2-1ecfde18f353.png)

# 他のプログラム言語と違い
## API_KEYの指定が不要
他のプログラム言語ではAPI_KEYを指定していたのですが、Google Apps Scriptでは指定は不要でした。

## 返信コメントの取得
他のプログラム言語と違い返信コメントは5件までだったら、`item.snippet.replies.comments`の配下で取得出来るので、それで負荷を減らしています。ほとんどの返信コメントは数件ですから、それで充分です。

* [Google Apps Script で YouTube 動画コメントを取得する](https://sleepygamersmemo.blogspot.com/2020/08/retrieve-youtube-comments-with-google-apps-scripts.html)
* [Google Apps ScriptとYoutubeのAPIを利用して動画のコメントを取得する](https://www.acrovision.jp/service/gas/?p=568)

# 最後に
初めて本格的に GoogleAppsScript と Googleスプレッドシート を使いましたけど、これは連携させるとすごく楽ですね。
仕事だとExcelメインで Googleスプレッドシートを使う機会がなかったので、さわって来なかったんだけど。
Google Colaboratory と Googleスプレッドシートで連携できそうだし積極的に使っていくことにします。