6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2020-12-31

はじめに

これは、Spreadsheets/Excel Advent Calendar 2020の25日目の記事となります。
6日遅れの大晦日に書いていますけどね。

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

環境

  • Windows 10 Home
  • Google.Apis.Youtube.v3 v1.49.0.2173

準備

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

仕様

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

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

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

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

使用方法

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);
  }
}

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

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

Video IDを入力

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

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

let video_list = ['oeJ_b0iG9lM'];

プログラム

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をやられており、ツイート画像に「おはよー」を隠すという遊びをしています。
郡司りかさんのツイート画像の「おはよー」を見つける

【NiziU】郡司さんMake You Happyにチャレンジ (練習前)」の動画コメントを取得すると、下記のようになります。
GASYoutubeAPI.png

他のプログラム言語と違い

API_KEYの指定が不要

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

返信コメントの取得

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

最後に

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

6
5
19

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?