見出し画像

YouTubeの「緊急で動画を回している」は全動画の何割かPythonで分析したい

はじめに

こんにちは。
破綻国家研究所というYouTubeで近代地歴の動画を投稿しているものです。
実はYouTubeは副業で、本職はデータサイエンティストなんですよね。
動画作成の時、データ集計をJupyter notebookで行ってます。
近代地歴+データってなかなかない組み合わせなんですよね。

さて、今回はYouTubeで「緊急で動画を回しているんですけれども」という枕詞がありますが、
果たしてどれくらいの頻度で緊急で動画を回しているのでしょうか?

気になったのでコードをかいて分析しました。

どのYouTubeを対象とするか

私が個人的に好きな、高須クリニックのお医者様である高須幹弥先生のYouTubeで分析してみよう!
先生って忙しいから結構緊急で動画を回しているイメージがあるもんなぁ


高須幹弥先生のYouTube, 登録者が多い!さすがだなぁ

コードを書いてみよう

コードは下記からダウンロードできます。

pip install yt-dlp pytube youtube-transcript-api tqdm pandas
import yt_dlp
from youtube_transcript_api import YouTubeTranscriptApi, NoTranscriptFound
import pandas as pd
import requests
import re
from tqdm import tqdm

def get_channel_videos(channel_url):
    ydl_opts = {
        'extract_flat': True,
        'skip_download': True,
        'quiet': True
    }
    video_urls = []
    
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(channel_url, download=False)
        # チャンネル内の動画IDを取得
        if 'entries' in info:
            for entry in info['entries']:
                video_id = entry['id']
                video_urls.append(f"https://www.youtube.com/watch?v={video_id}")
    
    return video_urls

def fetch_video_title_and_date(video_url):
    try:
        response = requests.get(video_url)
        if response.status_code == 200:
            # タイトルを取得
            title_match = re.search(r'<title>(.*?)</title>', response.text)
            video_title = title_match.group(1).replace(" - YouTube", "").strip() if title_match else "タイトルを取得できませんでした"
            
            # 投稿日付を正規表現で取得
            date_match = re.search(r'"dateText":{"simpleText":"(.*?)"}', response.text)
            publish_date = date_match.group(1) if date_match else "日付を取得できませんでした"
            
            return video_title, publish_date
        return "タイトルを取得できませんでした", "日付を取得できませんでした"
    except Exception as e:
        return f"エラー: {e}", "日付を取得できませんでした"

def get_video_details(video_url, language_code='ja'):
    try:
        # URLから動画IDを抽出
        video_id_match = re.search(r'v=([^&]+)', video_url)
        if video_id_match:
            video_id = video_id_match.group(1)
        else:
            return None

        # Webページから動画のタイトルと投稿日付を取得
        video_title, video_date = fetch_video_title_and_date(video_url)

        # 字幕を取得
        try:
            transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[language_code])

            # tqdmを使って進捗表示しながら字幕を処理
            filtered_transcript = "".join(
                entry['text'].strip() + " " for entry in tqdm(transcript, desc="字幕処理中") if entry['start'] <= 15
            ).strip()
        except NoTranscriptFound:
            filtered_transcript = "文字おこし不可動画"
        
        # '緊急'が含まれているかチェック
        contains_emergency = 1 if '緊急' in filtered_transcript else 0

        # データをDataFrameにまとめる
        data = {
            "動画タイトル": [video_title],
            "動画投稿日付": [video_date],
            "動画URL": [video_url],
            "抽出した文章": [filtered_transcript],
            "抽出した文章に'緊急'があるか": [contains_emergency]
        }

        df = pd.DataFrame(data)
        return df
    except Exception as e:
        print(f"エラー: {e} - 動画URL: {video_url}")
        return None  # エラーが発生した場合にはNoneを返す

# チャンネルURLを指定して動画のURLリストを取得
channel_url = 'https://www.youtube.com/@takasumikiya/videos'
video_urls = get_channel_videos(channel_url)

# 取得した動画URLを基にデータフレームを作成
video_dfs = []
for url in tqdm(video_urls, desc="動画リストの処理中"):
    video_df = get_video_details(url)
    if video_df is not None:  # Noneを除外
        video_dfs.append(video_df)

# 全ての動画の情報を結合
all_videos_df = pd.concat(video_dfs, ignore_index=True)
all_videos_df

ここまでで、all_videos_dfでデータフレームを出します。
何をやっているのかというと、高須先生のYoutubeにアクセスして、各動画のURL, タイトル、初めの15秒の書き起こしをスクレイピングしています。そして、スクレイピングした文章から"緊急"が含まれているかを探します。
やってることは簡単ですね。

こんな感じで処理されています。

スクレイピング中
def count_emergency_occurrences(df):
    emergency_counts = df["抽出した文章に'緊急'があるか"].value_counts()
    total = len(df)
    emergency_percentage = emergency_counts / total * 100
    result_df = pd.DataFrame({
        'カウント': emergency_counts,
        '割合 (%)': emergency_percentage
    }).reset_index().rename(columns={'index': '緊急の有無'})    
    return result_df

emergency_stats = count_emergency_occurrences(all_videos_df)
emergency_stats

次に上記部分でカウントと割合を出します。やってることは小学生の計算です。

def extract_channel_name(channel_url):
    match = re.search(r'@([^/]+)', channel_url)
    return match.group(1) if match else "チャンネル名未取得"

def save_to_excel(df, channel_url):
    channel_name = extract_channel_name(channel_url)
    file_name = f"{channel_name}.xlsx"
    df.to_excel(file_name, index=False)
    return file_name

file_name = save_to_excel(all_videos_df, channel_url)
print(f"Excelファイル '{file_name}' が正常に出力されました。")

最後に上記部分でエクセル形式に保存します。
Jupyter notebook上で解析やってもいいけど、みんなエクセルが使えるしエクセルで出力しましょ。

エクセルの名前はYoutubeの@XXXXXをファイル名にしてます。

うまく出力できましたね

出力されたエクセルも置いておきます。

解析結果

緊急で動画を回している割合

カウントと割合の結果です。
解析結果、94.5%は通常の動画、5.5%が緊急で挿画を回している結果でした。筆者はもっと多いと思っていましたが、どうなんだろう?少ないのかな?
というか高須幹弥先生、動画数多いな!

うーん、緊急動画は意外と少ないのか?

時系列に見てみる

まずはエクセルの中身を見てみましょう。こんな感じで動画タイトル、動画投稿日付、動画URL、抽出した文章、抽出した文章に"緊急"と含まれているか?というカラムを作りました。

抽出した文章はYouTbeの書き起こし文章なので、変な箇所が所々見受けられますね。改善できればいいのですがね。

ところどころ緊急という文字が見えます

じゃあ時系列のグラフを見てみたらどうなるのっと( ^ω^)・・・

コードの都合上、0が通常動画、1が緊急で回している動画です。

あれ?時系列でみると結構偏ってる感じですね。
なるほど。
緊急で動画を回すことが多いのはここ2年くらいだということですね。

時系列分析はおもしろいね

じゃあ2022/1/1~2024/10/28までに
絞ったらどうなるのっと( ^ω^)・・・

なるほど…
10.5%が緊急で動画を回しているのか。ふむふむ
ということは10回に1回は高須幹弥先生は緊急で動画を回しているということになりますね。

コード書き直すのマンドクセ…だったのでエクセルで簡易的にやった

というか、クリニックを経営かつ執刀もしていて、YouTube運営もされている高須幹弥先生が緊急で動画を回す割合が10回に1回とかすごくね?
むしろそんな少ないのか、という感触でした。

応用方法

このコードって簡単な部類ですが、いろんな応用方法があると思うんですよね。

政治家のYouTube分析

政治家って言ってることがころころ変わるじゃないですか。だから、政治家個人のYouTubeチャンネルの動画でスクレイピングをかけて、この時期に言う単語ランキングとか、動画ジャンル別にどういうことを言っている傾向があるのか、やってみたら面白いかもしれませんね。

LLM(大規模言語モデル)との組み合わせ

生成AIと組み合わせで、再生数の多い動画はどういう単語や話題を言っている傾向があるか調べたりできますね。
私はめんどくさいのでそこまでやりませんが、頭のいい方は試してみてください。

一言ご連絡いただくと、私はとってもうれしいです!

ほなまた

この記事が気に入ったらサポートをしてみませんか?