たきこみの丸太

暇だった。

Youtubeビッグデータ解析 その2 Completed Live Chat の取得

Youtubeのデータは、YouTube.v3.Dataってのを叩けば簡単に取れる。
が、放送が終了したYoutube Live Chat情報は、YouTube.v3.Dataでは取得できない。
これに気付くまで半日かかった。
ってか、放送中のLive Chatは取れるのに、放送終了すると取れなくなるってどーゆーこっちゃねん。

でも、Liveに付いたコメント数とか、スパチャ金額の集計とかやりたいので、無理やり取ってみる。


先人様の有り難い教え。
PythonでYouTube Liveのアーカイブからチャット(コメント)を取得する - 雑記帳(@watagasi_)
あなた様の情報が無ければ無理でした。


JSONの取得は、先人様のPythonコードをc#に書き換えただけなので省略して、
JSONからLive Chat情報を取得する方法を載せていく。

Live Chat情報は、
jsonObj?.continuationContents?.liveChatContinuation?.actions
内のactionに入っているので、actionを再帰関数で取得する。

/// <summary>
/// コメントリストにコメントを詰める
/// </summary>
/// <param name="url">コメントURL</param>
/// <param name="commentList">コメントリスト</param>
private void CollectCommentByHttpRequest(string url, ref List<LiveCommentCompleted> commentList)
{
    Console.WriteLine("コメントURLの読み込み:" + url);

    //次のコメント一覧を取得するためのパラメータ
    string nextcontinuation = string.Empty;
    dynamic jsonObj = GetJsonObject(url);

    try
    {
        nextcontinuation = jsonObj?.continuationContents?.liveChatContinuation?.continuations?[0]?.liveChatReplayContinuationData?.continuation;
    }
    catch
    {
        //次の情報無し。
    }

    dynamic actions = jsonObj?.continuationContents?.liveChatContinuation?.actions;

    if (actions != null)
    {
        foreach (var action in actions)
        {
            try
            {
                var item = LiveCommentCompleted.CreateLiveCommentCompleted(action, VideoId);
                if (item != null)
                {
                    commentList.Add(item);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }
    }

    if (!string.IsNullOrEmpty(nextcontinuation))
    {
        string nextAddress = "https://www.youtube.com/live_chat_replay?continuation=" + nextcontinuation;
        CollectCommentByHttpRequest(nextAddress, ref commentList);
    }
}


ここで取得したactionは、いくつかのTypeに分類できる。

Type 内容 判別方法
Text 通常のチャット action?.
replayChatItemAction?.
actions?[0]?.
addChatItemAction?.
item?.
liveChatTextMessageRenderer
!= null
Paid スーパーチャット action?.
replayChatItemAction?.
actions?[0]?.
addChatItemAction?.
item?.
liveChatPaidMessageRenderer
!= null
Engagement Yotubeメッセージ action?.
replayChatItemAction?.
actions?[0]?.
addChatItemAction?.
item?.
liveChatViewerEngagementMessageRenderer
!= null
Ticker チャットエリア上部に残るコメント同期アイコン action?.
replayChatItemAction?.
actions?[0]?.
addLiveChatTickerItemAction
!= null
IndependentTicker チャットエリア上部に残るコメント非同期アイコン action?.
replayChatItemAction?.
actions?[0]?.
addChatItemAction?.
item?.
liveChatPaidStickerRenderer
!= null
MembershipItem メンバーシップへの加入メッセージ action?.
replayChatItemAction?.
actions?[0]?.
addChatItemAction?.
item?.
liveChatMembershipItemRenderer
!= null
Placeholder 動画の中間地点マーカー? action?.
replayChatItemAction?.
actions?[0]?.
addChatItemAction?.
item?.
liveChatPlaceholderItemRenderer
!= null

主に画面に出ているアイテムはこんな感じ。
f:id:takikomiprogramming:20200721104538p:plain
f:id:takikomiprogramming:20200721104543p:plain

後はType毎に欲しい物を取得する訳だが、
ここで取得できる項目は、配信中にYouTube.v3.Dataで取得できる項目とは違うので、
配信中に取得した情報と配信後に取得した情報の同期をとる必要があるなら、
ここの内容はもうちょっと精査する必要があるかもしれない。

メインになる情報は以下2つ。

Type:Text
dynamic renderer =
    action?.
    replayChatItemAction?.
    actions?[0]?.
    addChatItemAction?.
    item?.
    liveChatTextMessageRenderer;
    
Text = GetNotNullString(renderer?.message?.runs?[0]?.text);
Author = GetNotNullString(renderer?.authorName?.simpleText);
Type:Paid
dynamic reader = 
    action?.
    replayChatItemAction?.
    actions?[0]?.
    addChatItemAction?.
    item?.
    liveChatPaidMessageRenderer;
    
Text = GetNotNullString(renderer?.message?.runs?[0]?.text);
Author = GetNotNullString(renderer?.authorName?.simpleText);
PurchaseAmountText = reader?.purchaseAmountText.simpleText;
  • Text:チャット内容
  • Author :ユーザー名
  • PurchaseAmountText:表示金額

表示金額は、

  • "¥1,220"
  • "NZ$5.00"
  • "¥12,000"
  • "R$2.00"
  • "$9.99"

みたいな感じで入ってくるので、集計用に分解しておく。

private static readonly Regex REGEX_PURCHASE = new Regex(@"(\D*)(.+)");

if(!string.IsNullOrEmpty(PurchaseAmountText) && REGEX_PURCHASE.IsMatch(PurchaseAmountText))
{
    Match m = REGEX_PURCHASE.Match(PurchaseAmountText);
    PurchasePrifix = m.Groups[1].Value;
    decimal d;
    if(decimal.TryParse(m.Groups[2].Value, out d))
    {
        PurchaseAmount = d;
    }
}


最後に。扱うデータがビッグなので、取ってきたデータを全てDBMSに突っ込む。
f:id:takikomiprogramming:20200721114047p:plain
f:id:takikomiprogramming:20200721114053p:plain

これで一般的に『ビッグデータ』と呼ばれているモノができた気がする。
次からは『解析』に入っていく。