😡

YouTubeの良いレスバトルを自動抽出するプログラムを作ってみた

に公開

はじめに

新進気鋭のYouTuberたむらかえ2さんの動画を見て感銘を受けました。
「YouTuberであるたむらかえさんの投稿動画に頻繁にアンチコメントをするユーザがいて、そのユーザとYouTubeのコメント上でレスバトルする」という趣旨の動画でとても面白かったです。
https://www.youtube.com/watch?v=CEGfibKuZMA

この動画の中でたむらさんが良いレスバトルを定義しているので、これを利用して自動で良いレスバトルを抽出するプログラムを作ってみました。
基本無料で動作するので、皆さんも好きなYouTubeチャンネルで試してみてください。

良いレスバトルの定義

まず、動画内でたむらさんが語られている「良いレスバトル」は「最初のコメントに対する1,2個目のreplyのいいね数が多い」であると語っています。盛り上がっていて第三者から見ても面白いものが良いものであるということです。

これらを踏まえて、コメントスレッドを以下のフィルタにかけることで良いレスバトルの抽出を試みました

  1. replyが5件以上存在すること
  2. スレッド内の5割以上のコメントがポジティブでない感情を持つこと
  3. 最初のコメントの投稿者以外のreplyのいいね数が、最初のコメントのいいね数を上回ること

セットアップ

YouTube API キーの取得

  1. Google Cloud Consoleにアクセス
  2. 新しいプロジェクトを作成
  3. YouTube Data API v3を有効化
  4. APIキーを作成
  5. .envファイルに保存
YouTube_API_KEY=your_api_key_here

実装

1. YouTube APIでコメントを取得

まず、YouTube Data API v3を使って動画のコメントを取得する関数を実装します。

import os
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from dotenv import load_dotenv

load_dotenv()
API_KEY = os.getenv('YouTube_API_KEY')

def get_videos(YouTube, channel_id, page_token=None):
    """チャンネルの動画一覧を取得"""
    request = YouTube.search().list(
        part='snippet',
        type='video',
        channelId=channel_id,
        maxResults=100,
        pageToken=page_token,
    )
    response = request.execute()
    return response.get('items', []), response.get('nextPageToken')

def get_comment_threads(YouTube, video_id, page_token=None):
    """動画のコメントスレッドを取得"""
    request = YouTube.commentThreads().list(
        part='snippet,replies',
        videoId=video_id,
        maxResults=100,
        pageToken=page_token,
        textFormat='plainText',
        order='relevance'  # 関連性の高い順
    )
    response = request.execute()
    return response.get('items', []), response.get('nextPageToken')

def get_replies(YouTube, parent_id, page_token=None):
    """コメントへの返信を取得"""
    request = YouTube.comments().list(
        part='snippet',
        parentId=parent_id,
        maxResults=100,
        pageToken=page_token,
        textFormat='plainText',
    )
    response = request.execute()
    return response.get('items', []), response.get('nextPageToken')

2. 感情分析モデルの準備

コメントの雰囲気を判定するため、日本語感情分析モデルを使用します。
今回はchristian-phu/bert-finetuned-japanese-sentimentを使いました。
https://huggingface.co/christian-phu/bert-finetuned-japanese-sentiment

from transformers import pipeline, BertJapaneseTokenizer, AutoModelForSequenceClassification

# トークナイザーの初期化
tokenizer = BertJapaneseTokenizer.from_pretrained(
    "christian-phu/bert-finetuned-japanese-sentiment",
    mecab_kwargs={"mecab_dic": "unidic"},  # unidic辞書を使用
    model_max_length=512  # BERTの最大長
)

# モデルとパイプラインの初期化
model = AutoModelForSequenceClassification.from_pretrained(
    "christian-phu/bert-finetuned-japanese-sentiment"
)
classifier = pipeline(
    "sentiment-analysis",
    model=model,
    tokenizer=tokenizer,
    truncation=True,
    max_length=512
)

3. 感情分析関数

このモデルはポジティブ・ネガティブ・ニュートラルの3分類を返します。

def is_negative(comment):
    """コメントがネガティブかどうかを判定"""
    results = classifier(comment, top_k=None, truncation=True, max_length=512)
    for result in results:
        # positiveスコアが0.5未満ならネガティブと判定
        if result['label'] == 'positive' and result['score'] < 0.5:
            return True
    return False

# 使用例
print(is_negative("素晴らしい動画ですね!"))  # False
print(is_negative("最低の動画ですね"))  # True
print(is_negative("逆にここまで外せるの、天才しかできませんよ。"))  # True (微妙なやつでもそこそこnegative判定)
感情分析モデルの限界について

東北大のBERTベースモデルを使用していますが、純粋な悪口だけでなく皮肉や煽りも検出したい場合、精度が不十分な可能性があります
(特にYouTubeコメントはそういうのが多いようです...)

例えば、以下のような皮肉コメントの検出が難しい場合があります:

  • コメント欄見に来たらこっちの方が面白かった🤣
  • こんな作品を世に出せる勇気に拍手👏
  • 自動販売機の補充動画のほうが見応えあった😃

より高い精度が必要な場合は、GPT-5などのLLMを使った感情分析も検討できますが、今回はコストを考慮してBERTモデルを使用しました。

4. レスバトル判定ロジック

良いレスバトルの条件を満たすかを判定する関数を実装します。

reply_count_threshold = 5  # 最低返信数

def is_negative_threads(reply_items):
    """スレッド内の5割以上がネガティブかどうか判定"""
    if len(reply_items) == 0:
        return False

    negative_cnt = 0
    for reply in reply_items:
        reply_text = reply['snippet']['textDisplay']
        if is_negative(reply_text):
            negative_cnt += 1

    return negative_cnt / len(reply_items) > 0.5

def is_better_res_battle(top_comment_snippet, reply_items):
    """返信のいいね数が元コメントを上回っているか判定"""
    top_comment_like_count = top_comment_snippet['likeCount']
    top_comment_author = top_comment_snippet['authorChannelId']['value']

    for reply in reply_items:
        reply_snippet = reply['snippet']
        reply_like_count = reply_snippet['likeCount']
        reply_author = reply_snippet['authorChannelId']['value']

        # 別の投稿者の返信が元コメントより多くいいねされている
        if reply_like_count > top_comment_like_count and reply_author != top_comment_author:
            return True

    return False

実際のコード
https://github.com/ponyo877/res-battle-picker/blob/main/main.py

実行結果の例

実際にたむらかえ2さんのチャンネルに対して動作させてみると、以下のような良いレスバトルと思われるものが23件、抽出されました:
抽出されたレスバトルの例

抽出されたレスバトル一覧

Video ID: OcyplnP3bzk
Top Comment: これは割と典型じゃない?12個の数字の羅列から月か干支って予測できるから後は簡単でしょ(典型じゃなければ頭悪い俺が今井より早く解けた理由にならない)
Top Comment Like Count: 13
Reply Count: 23

Video ID: oyD_rKmOcuM
Top Comment: Xのリプとか引用見ても考察とかどっちも良さあるよね見たいな意見ばかりでお姉ちゃん叩いてる意見ほとんど無かったけどな😅
結局は「Xのどっちが性格良く見えるか陣取り合戦が起こってますよね?」っていう一個上の視点に立ってますよアピールしたいだけ
「お姉ちゃんの方も褒めといてください」これマジで一ミリも思ってないのが伝わってきてなんとも言えない気持ち悪さが込み上げてきました
おかげで朝早起きできたんで感謝です!
Top Comment Like Count: 4
Reply Count: 5

Video ID: lTj8o_FTYAo
Top Comment: 趣味とかがちゃんとあってボケようとする意思がある女なんてモテるに決まってんだろ
基本女なんてなんの趣味もないんだから
Top Comment Like Count: 14
Reply Count: 11

Video ID: 78WkXlUskYU
Top Comment: YouTubeという賭博はいいのか?
Top Comment Like Count: 6
Reply Count: 5

Video ID: 8SUfq9WRuEM
Top Comment: ???無能な自分の修論を提出した事が無能ハラスメントって事?
ごめん😅ちょっと分かんない。
Top Comment Like Count: 1
Reply Count: 5

Video ID: TkmJYYOUNjs
Top Comment: 姉妹のよくあるやつかも。

姉を勝手に自分より上に見ているから、
自分より下(と勝手に定義した)人間を見つけて、優越感にひたるってやつかな。
Top Comment Like Count: 13
Reply Count: 14

Video ID: 3KCIHm5TqWA
Top Comment: ナンパもよいじゃん
Top Comment Like Count: 0
Reply Count: 8

Video ID: USJ_0u_xphY
Top Comment: 私も元塾講だから分かるわ。前の授業で1時間かけて教えてわからないとこある?って聞いても完璧って言うくせに次の授業で何も覚えてなかったらマジでクソイラつく。 
 子供だからどうとかじゃなくて分からないなら素直に言えよ、何で隠そうとするのかマジで意味不明だった。言葉にしないと分かるわけないじゃん、私は神じゃねえんだぞって心の中で思いながら教えてた
Top Comment Like Count: 3
Reply Count: 6

Video ID: vgO_9Cqsnks
Top Comment: 院の2年生のはずだが、卒業後の進路は決まったのですか?卒業直前に髪を染めるなんて…。
Top Comment Like Count: 2
Reply Count: 5

Video ID: 40NL-52XdfI
Top Comment: 上から目線なのなんで?
Top Comment Like Count: 8
Reply Count: 7

Video ID: 9FrpTRG0AGg
Top Comment: 理物ではないが理一でお化けと信じてる人おるけどな
Top Comment Like Count: 11
Reply Count: 5

Video ID: 5yTDU5Dz0jw
Top Comment: 成り立つ。成り立たないと断言することはない。めっちゃ仲良い女友達いるけど俺はその人の事を女性とは見てない、ペットみたいな感じで見てる。顔はかわいいかと言われたらそうじゃないから、恋愛感情とか性欲とかが湧かないというのも一つの理由。
Top Comment Like Count: 4
Reply Count: 5

Video ID: 8m8j55TbQvY
Top Comment: 東大なのに、怠惰で、髪も奇抜で、尖ってるわたしキモチエエ〜〜みたいなタイプもういらん。
真面目なやつが1番かっこいい
Top Comment Like Count: 23
Reply Count: 7

Video ID: 8m8j55TbQvY
Top Comment: エンタメといえば修士号の価値を貶めること言っても許されるとか思うなよ
Top Comment Like Count: 15
Reply Count: 5

Video ID: epdFuSgISX4
Top Comment: 八王子はぎり埼玉ですもんね笑
Top Comment Like Count: 3
Reply Count: 5

Video ID: 7Uax9qTxV4c
Top Comment: TAってなんだろうか
Top Comment Like Count: 24
Reply Count: 5

Video ID: YQHIPL_mNeI
Top Comment: 母親が正しい。母親は、ゆるくないか?門限は20時だろ
Top Comment Like Count: 0
Reply Count: 5

Video ID: dZbLYzmU-m4
Top Comment: みんな言わないけど普通にブサイク
35点
Top Comment Like Count: 4
Reply Count: 5

Video ID: tEhPNKNbIro
Top Comment: もう少しゆっくり話してほしいなー、テンポ早い😢
Top Comment Like Count: 3
Reply Count: 8

Video ID: zt0kJ05s61M
Top Comment: 絶対かわいいし将来は港区女子になるんだろうなぁ
Top Comment Like Count: 1
Reply Count: 5

Video ID: CEGfibKuZMA
Top Comment: たむらかえブスだとおもう
って何回かコメントしてんのに返事こなかった😂
本当のことだからかなあ😢
Top Comment Like Count: 5
Reply Count: 19

Video ID: CEGfibKuZMA
Top Comment: そんなことしてないで早く大学院へ戻って博士取れな
Top Comment Like Count: 2
Reply Count: 5

Video ID: CEGfibKuZMA
Top Comment: なんか色々と考えさせられるな…
実家極太で生まれたときから全て持ってる人間が明らかな社会的弱者を晒し物にしてインプレッション稼ぐのグロい。
Top Comment Like Count: 5
Reply Count: 8

Video ID: ue7hxROVssg
Top Comment: 占いはー、まったく信じないかなー😅漢方は、効くよ。本当だよー❤
Top Comment Like Count: 0
Reply Count: 9

Video ID: nKFItMZQotM
Top Comment: 少しは周りの人達のために動いたらどうなんですか?そんな事して何の得になると思っているんですか?普通に考えたら分かる事なのになぜあなたは続けるんですか?
Top Comment Like Count: 0
Reply Count: 6

Video ID: iTNG4yGgPHo
Top Comment: だからと言って男子同士で言うような下ネタを言ってもいけない。相手が女子なのだと意識して自分なりに配慮した会話をしても良い。その結果大恥をかいても良い。大学生活は人生で痛々しい恥をかけられる最期のチャンスだと思って頑張るしかない。
Top Comment Like Count: 23
Reply Count: 6

Video ID: dNrQj3nv1-E
Top Comment: 東大と千葉医どっちがむずかしいの
Top Comment Like Count: 0
Reply Count: 7

Video ID: q8PtzU16FUE
Top Comment: ワンピースはつまらないし、それを面白いって言ってるやつの薦める漫画もみないことにしている。
Top Comment Like Count: 6
Reply Count: 9

みていただくとわかるように動画で紹介されているような盛り上がっている高品質なレづバトルは見つからず...
おそらくネガポジ判定の精度が原因だと思われます、改善の余地がありそうです。

まとめ

精度に改善の余地がありますが、YouTube APIと感情分析モデルを組み合わせることで、面白いレスバトルを自動抽出できました。

参考

皆さんも好きなYouTubeチャンネルで試してみてください!

Discussion