はじめに
これは、Visual Basic Advent Calendar 2020の22日目の記事となります。
- 【Python】Youtube Data Apiを使ってYouTube 動画コメントを全取得する
- 【VBA】ExcelでYoutube Data Apiを使ってYouTube 動画コメントを全取得する
- 【VBA】Excel for MacでYoutube Data Apiを使ってYouTube 動画コメントを全取得する
- 【GAS】Youtube Data Apiを使ってYouTube 動画コメントを全取得する
どんだけ同じネタを使い回すのか、とはいえ、.NET版を作ってなかったんだよね。
環境
- Windows 10 Home
- .NET 5
- Visual Studio 2019
- Google.Apis.Youtube.v3 v1.49.0.2173
準備
今回のプログラムではYoutube Data Apiを使用するので、Apiキーの有効化を行っていきます。
Apiキーの有効化などは下記サイトを参考にしてください。
Youtube Data APIを使用して、Youtube Liveのコメントを取得する
ライブラリーの追加
.NETでYoutube Data Apiを使用するにはGoogle.Apis.Youtube.v3が必要なので、NuGetパッケージマネージャーから追加してください。
- NuGetパッケージマネージャーを起動します。
- 参照タブで、Google.Apis.Youtube.v3を検索します。
- Google.Apis.Youtube.v3をインストールします。
仕様
連番を付与しユーザー名と子コメントが取得できるようになっています。
タブ区切りでコメントの改行は半角空白に置換しています。
コメントのフォーマットはhtmlかplain textがあるのですが、plain textになっています。
並び順はrelevanceで評価順が多い順にしています。子コメントについては順不動(並び順を指定しても動画サイトと同じにならない)です。
親連番は4桁、子の連番は3桁にしているので桁数を超えるコメントを取得したい場合は表示する桁数を変更増やすといいでしょう。
※APIの使用回数を減らすため 親コメントはmaxResults=100、子コメントはmaxResults=50とする。
※コメントでは低評価(disLike)数は取得できない。
000X (コメント) (Like数) (ユーザー名) (投稿日時) (返信数)
000X-00X (子コメント) (グッド数) (ユーザー名) (投稿日時)
【2021/05/28追記】
並び順を評価順(relevance)にした場合、YouTube Data API v3の制約上2000件を超える親コメントが取得できません。
並び順を新しい順(time)にした場合、上限が不明ですが2000件を超える親コメントが取得できます。
使用方法
' ここにAPI KEYを入力
Private Const API_KEY As String = "(API KEYを入力)"
' ここにVideo IDを入力
Private Const VIDEO_ID As String = "(Video IDを入力)"
// ここにAPI KEYを入力
private const string API_KEY = "(API KEYを入力)"
// ここにVideo IDを入力
private const string VIDEO_ID = "(Video IDを入力)"
本来は、Video IDは外部から変更できるようにしておくといいと思います。
API_KEYを入力
Youtube Data APIのAPI有効化の認証情報でAPIキーが発行されるので、プログラムのAPI_KEYを入力で発行されたAPIキーに書き換えてください。
Video IDを入力
例えば、「https://www.youtube.com/watch?v=oeJ_b0iG9lM」であれば、oeJ_b0iG9lM
がVideo IDとなりますので、プログラムのVideo IDを入力で対象動画のVideo IDに書き換えてください。
プログラム
VisualBasic
Imports Google.Apis.Services
Imports Google.Apis.YouTube.v3
' ここにAPI KEYを入力
Private Const API_KEY As String = "(API KEYを入力)"
' ここにVideo IDを入力
Private Const VIDEO_ID As String = "(Video IDを入力)"
Public Class CommentInfo
Public ParentNo As Integer
Public ChildNo As Integer
Public Text As String
Public LikeCount As Long
Public AuthorName As String
Public PublishedAt As DateTime
Public ReplyCount As Long
End Class
Public Class Form1
'ここにAPI KEYを入力
Private Const API_KEY As String = "(API KEYを入力)"
'ここにVideo IDを入力
Private Const VIDEO_ID As String = "(Video IDを入力)"
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim youtubeService = New YouTubeService(New BaseClientService.Initializer() With {
.ApiKey = API_KEY
})
Dim videoId = VIDEO_ID
Dim commentList As List(Of CommentInfo) = New List(Of CommentInfo)()
Await GetComment(commentList, videoId, youtubeService, 1, Nothing)
For Each info In commentList
Dim line As String
If info.ChildNo = 0 Then
line = String.Format("{0:0000}\t{1}\t{2}\t{3}\t{4}\t{5}", info.ParentNo, info.Text.Replace(vbLf, ""), info.LikeCount, info.AuthorName, info.PublishedAt, info.ReplyCount)
Else
line = String.Format("{0:0000}-{1:000}\t{2}\t{3}\t{4}\t{5}", info.ParentNo, info.ChildNo, info.Text.Replace(vbLf, ""), info.LikeCount, info.AuthorName, info.PublishedAt)
End If
line = line.Replace("\t", vbTab)
System.Diagnostics.Debug.WriteLine(line)
Next
MessageBox.Show("出力完了しました。")
End Sub
' コメント取得
Public Shared Async Function GetComment(ByVal commentList As List(Of CommentInfo), ByVal videoId As String, ByVal youtubeService As YouTubeService, ByVal no As Integer, ByVal nextPageToken As String) As Task
Dim request = youtubeService.CommentThreads.List("snippet")
request.VideoId = videoId
request.Order = CommentThreadsResource.ListRequest.OrderEnum.Relevance
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText
request.MaxResults = 100
request.PageToken = nextPageToken
Dim response = Await request.ExecuteAsync()
For Each item In response.Items
Try
Dim info As CommentInfo = New CommentInfo()
info.ParentNo = no
info.ChildNo = 0
info.Text = item.Snippet.TopLevelComment.Snippet.TextDisplay
info.LikeCount = CLng(item.Snippet.TopLevelComment.Snippet.LikeCount)
info.AuthorName = item.Snippet.TopLevelComment.Snippet.AuthorDisplayName
info.PublishedAt = JsonDateToDate(item.Snippet.TopLevelComment.Snippet.PublishedAt)
info.ReplyCount = CLng(item.Snippet.TotalReplyCount)
Dim parentId As String = item.Snippet.TopLevelComment.Id
commentList.Add(info)
If item.Snippet.TotalReplyCount > 0 Then
Await GetReplyComment(commentList, youtubeService, parentId, no, 1, Nothing)
End If
no += 1
Catch
End Try
Next
If response.NextPageToken IsNot Nothing Then
Await GetComment(commentList, videoId, youtubeService, no, response.NextPageToken)
End If
End Function
' 返信コメント取得
Public Shared Async Function GetReplyComment(ByVal commentList As List(Of CommentInfo), ByVal youtubeService As YouTubeService, ByVal parentId As String, ByVal no As Integer, ByVal cno As Integer, ByVal nextPageToken As String) As Task
Dim request = youtubeService.Comments.List("snippet")
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText
request.MaxResults = 50
request.ParentId = parentId
request.PageToken = nextPageToken
Dim response = Await request.ExecuteAsync()
For Each item In response.Items
Try
Dim info As CommentInfo = New CommentInfo()
info.ParentNo = no
info.ChildNo = cno
info.Text = item.Snippet.TextDisplay
info.LikeCount = CLng(item.Snippet.LikeCount)
info.AuthorName = item.Snippet.AuthorDisplayName
info.PublishedAt = JsonDateToDate(item.Snippet.PublishedAt)
commentList.Add(info)
cno += 1
Catch
End Try
Next
If response.NextPageToken IsNot Nothing Then Await GetReplyComment(commentList, youtubeService, parentId, no, cno, response.NextPageToken)
End Function
' JSON日付変換
Private Shared Function JsonDateToDate(ByVal dt As String) As DateTime
Dim result As DateTime
DateTime.TryParse(dt, result)
Return result
End Function
End Class
CSharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
public class CommentInfo
{
public int ParentNo;
public int ChildNo;
public string Text;
public long LikeCount;
public string AuthorName;
public DateTime PublishedAt;
public long ReplyCount;
}
namespace WindowsFormsApp
{
public partial class Form1 : Form
{
// ここにAPI KEYを入力
private const string API_KEY = "(API KEYを入力)"
// ここにVideo IDを入力
private const string VIDEO_ID = "(Video IDを入力)"
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = API_KEY
});
var videoId = VIDEO_ID;
List<CommentInfo> commentList = new List<CommentInfo>();
await GetComment(commentList, videoId, youtubeService, 1, null);
foreach(var info in commentList)
{
string line;
if (info.ChildNo == 0)
line = string.Format("{0:0000}\t{1}\t{2}\t{3}\t{4}\t{5}", info.ParentNo, info.Text.Replace("\n", ""), info.LikeCount, info.AuthorName, info.PublishedAt, info.ReplyCount);
else
line = string.Format("{0:0000}-{1:000}\t{2}\t{3}\t{4}\t{5}", info.ParentNo, info.ChildNo, info.Text.Replace("\n", ""), info.LikeCount, info.AuthorName, info.PublishedAt);
System.Diagnostics.Debug.WriteLine(line);
}
MessageBox.Show("出力が完了しました。");
}
// コメント取得
static public async Task GetComment(List<CommentInfo> commentList, string videoId, YouTubeService youtubeService, int no, string nextPageToken)
{
var request = youtubeService.CommentThreads.List("snippet");
request.VideoId = videoId;
request.Order = CommentThreadsResource.ListRequest.OrderEnum.Relevance;
request.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
request.MaxResults = 100;
request.PageToken = nextPageToken;
var response = await request.ExecuteAsync();
foreach (var item in response.Items)
{
try
{
CommentInfo info = new CommentInfo();
info.ParentNo = no;
info.ChildNo = 0;
info.Text = item.Snippet.TopLevelComment.Snippet.TextDisplay;
info.LikeCount = (long)item.Snippet.TopLevelComment.Snippet.LikeCount;
info.AuthorName = item.Snippet.TopLevelComment.Snippet.AuthorDisplayName;
info.PublishedAt = JsonDateToDate(item.Snippet.TopLevelComment.Snippet.PublishedAt);
info.ReplyCount = (long)item.Snippet.TotalReplyCount;
string parentId = item.Snippet.TopLevelComment.Id;
commentList.Add(info);
if (item.Snippet.TotalReplyCount > 0)
await GetReplyComment(commentList, youtubeService, parentId, no, 1, null);
no++;
}
catch { }
}
if (response.NextPageToken != null)
await GetComment(commentList, videoId, youtubeService, no, response.NextPageToken);
}
// 返信コメント取得
static public async Task GetReplyComment(List<CommentInfo> commentList, YouTubeService youtubeService, string parentId, int no, int cno, string nextPageToken)
{
var request = youtubeService.Comments.List("snippet");
request.TextFormat = CommentsResource.ListRequest.TextFormatEnum.PlainText;
request.MaxResults = 50;
request.ParentId = parentId;
request.PageToken = nextPageToken;
var response = await request.ExecuteAsync();
foreach (var item in response.Items)
{
try
{
CommentInfo info = new CommentInfo();
info.ParentNo = no;
info.ChildNo = cno;
info.Text = item.Snippet.TextDisplay;
info.LikeCount = (long)item.Snippet.LikeCount;
info.AuthorName = item.Snippet.AuthorDisplayName;
info.PublishedAt = JsonDateToDate(item.Snippet.PublishedAt);
commentList.Add(info);
cno++;
}
catch { }
}
if (response.NextPageToken != null)
await GetReplyComment(commentList, youtubeService, parentId, no, cno, response.NextPageToken);
}
// JSON日付変換
static private DateTime JsonDateToDate(string dt)
{
DateTime result;
DateTime.TryParse(dt, out result);
return result;
}
}
}
実行結果
郡司りかさんを御存知でしょうか?
マツコ・デラックスさんと村上信五(関ジャニ∞)さんがMCの「月曜から夜更かし」の中で、運動音痴として注目を浴びた女性です。
郡司りかさんはYoutubeとTwitterをやられており、ツイート画像に「おはよー」を隠すという遊びをしています。
郡司りかさんのツイート画像の「おはよー」を見つける
「【NiziU】郡司さんMake You Happyにチャレンジ (練習前)」の動画コメントを取得すると、下記のようになります。
悩んだところ
出力画面に表示されない
Console.WriteLine
を使用したが出力画面に何も表示されなかったので、Debug.WriteLine
にしました。
WinFormだと出ないんでしたっけ。
返信コメントの取得
返信コメントは5件までだったら、item.snippet.replies.comments
の配下で取得出来るはずなんですが、駄目でした。取得できるならもう少し高速化できたんですけどね。
Google App Scriptでは取得出来るので、.NET版で取れないのかも知れません。
Google Apps ScriptとYoutubeのAPIを利用して動画のコメントを取得する
最後に
最初に C# で作成しておき、「Telerik Code Converter」のサイトでVisual Basicに変換しました。
Visual BasicからC#への変換について