【Python】Flask+YouTube Data APIによる動画データ分析アプリ開発





Python初心者が作れるものとして、Web API連携した簡易的なアプリケーション開発があります。

本記事では、PythonフレームワークであるFlaskとYouTube Data APIを連携させた簡易的な動画データ分析アプリを開発します。

  • Webアプリケーションを開発してみたい人
  • PythonフレームワークFlaskを利用してみたい人
  • YouTube Data APIを活用してみたい人

これらの悩みを解決しながら、動画データ分析アプリを開発します。

動画データ分析アプリケーションの完成画面は以下になります。

以下の章から開発工程を記載していきます。

独学に限界を感じたなら...
とりあえず独学でプログラミング学習を始めたけど、右も左も分からずあなたの時間が無駄に終わるどころか挫折するかもしれません。

あなたが時間を無駄にした分を回収したいなら【Python】2022年最新!おすすめのオンラインプログラミングスクールをご確認ください!

※期間限定で学習ロードマップを記載しています!

FlaskによるWebアプリ開発環境の準備/インストール

本記事で開発するFlask+YouTube Data APIによる動画データ分析アプリは、以下の項目を利用します。

  • PC : Mac
  • テキストエディタ : Atom
  • 使用言語 : HTML/CSS/Python(Flask)
  • Web API : YouTube Data API v3
  • CDN : BootStrap5, chart.js

各種必要となるプログラミング言語・ライブラリ等のインストールを開始します。

Pythonのインストールができていない人は「【Python初心者入門】ダウンロードとインストール方法を解説!」で解説します。

【Python初心者入門】ダウンロードとインストール方法を解説!

2017.07.09

また、YouTube Data APIを利用する際にGoogleアカウントを使用します。

Googleアカウント作成とYouTube Data API利用までの一連の準備工程ができていない人は「【Python】YouTube Data API v3を利用した特定チャンネルの動画情報取得」で解説します。

【Python】YouTube Data API v3を利用した特定チャンネルの動画情報取得

2020.05.12

各種インストールが完了したら、早速任意のディレクトリにFlaskによるアプリのフォルダを作成します。

mkdir top-movies

Macであればターミナルを使用し、任意のディレクトリにて上記コードを実行するとフォルダ作成できます。

もちろん、CUI操作が苦手な人であればデスクトップ上からコマンド入力せず作成できるので、わからない人はGUI操作で作成しましょう。

任意のフォルダ作成ができたら、以下のファイル/フォルダを作成したフォルダ内に作ります。

top-movies
├── app.py 
├── config.py
└── templates
   └── index.html

フォルダ内のapp.pyはFlaskを保存するためのPythonファイルです。(拡張子が.py)

config.pyはYouTubeのAPI KEY等の認証情報を格納するファイルになります。

templatesフォルダの中は、空のHTMLファイルを作成してください。

FlaskにてHTMLファイルを呼び出し、ブラウザ上に表示します。

Flaskによる動画データ分析アプリケーション開発の流れ

以下の4つの工程に分けて、動画データ分析アプリ開発を進めます。

  1. ヘッダーと入力フォーム作成
  2. 取得した特定チャンネル情報表示
  3. チャンネル内の各動画の視聴回数/高評価数グラフ化
  4. 各動画データのテーブル表示

各開発工程で、必要となるライブラリや編集ファイル等は解説の中で記載します。

ヘッダーと入力フォーム作成

ここでは、ヘッダーと入力フォーム作成にあたって以下の項目を実施します。

  • Flask/google-api-python-clientのインストール
  • templatesフォルダ内のindex.html編集
  • アプリフォルダ内のapp.py編集
  • アプリフォルダ内のconfig.py編集

はじめにFlaskを任意で作成したアプリフォルダ内にインストールします。

Flaskのインストール

Macであればターミナル、Windowsであればコマンドプロンプトを利用して任意フォルダまでディレクトリを移動し、Flaskとgoogle-api-python-clientをインストールします。

pip3 install flask
pip3 install --upgrade google-api-python-client

pip3コマンドを利用して任意フォルダにFlaskとgoogle-api-python-clientをインストールします。

今後、アプリ開発の中でpip3コマンドは多用するため、pipライブラリのインストールあるいは使い方を理解しておきましょう。

templatesフォルダ内のindex.html編集

次に、アプリフォルダ内に作成したtemplatesフォルダにあるindex.htmlを開きます。

空のindex.htmlにて、以下のコードを記載します。

<!doctype html>
<html lang="en">
<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
  <title>Top-Movies</title>
</head>
<body>
  <!-- ヘッダーと入力フォーム -->
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <h1><a href="/">Top-Movies</a></h1>
        <p>YouTubeのCHANNEL IDを入れると直近の動画数 / 動画再生数 / 最もGood評価されている動画を一覧できます.</p>
        <form class="form-inline" method="post">
          <div class="form-group">
            <label class="sr-only" for="user_id"></label>
            <div class="input-group">
              <input id="channel_id" name="channel_id" type="text" class="form-control" placeholder="CHANNEL IDを入力してください.">
              <button class="btn btn-outline-primary" type="submit">取得する</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
  <!-- ヘッダーと入力フォーム -->
</body>
</html>

HTMLファイルの<head>タグの中では、以下のコードを追加しています。

  • BootStrap5のCDN実行コード
  • <title>タグによるブラウザタブのタイトル名

<head>タグには、利用するCDN実行コードや<title>タグを含め、アプリに関するメタ情報を組み込めます。

HTMLファイルの<body>タグの中では、以下のコードを追加しています。

  • <h1>, <a href>タグによるタイトル表示
  • <form>, <input>タグによる入力フォーム表示

上記コードをindex.htmlに保存します。

アプリフォルダ内のapp.py編集

次に、アプリフォルダ内に作成したapp.pyを開きます。

空のapp.pyにて、以下のコードを記載します。

from apiclient.discovery import build
from flask import Flask, render_template, request, logging, Response, redirect, flash
from config import CONFIG

#各種YouTube data APIセット
API_KEY = CONFIG["API_KEY"]
YOUTUBE_API_SERVICE_NAME = CONFIG["YOUTUBE_API_SERVICE_NAME"]
YOUTUBE_API_VERSION = CONFIG["YOUTUBE_API_VERSION"]
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=API_KEY)

#入力するYouTubeチャンネルID
CHANNEL_ID = ""

#Flaskの起動
app = Flask(__name__)

#"xxxx.com/"にアクセスする際に実行される関数
@app.route('/', methods = ["GET", "POST"])
def index():
    if request.method == "POST":
    	#formのname="channel_id"を取得
    	CHANNEL_ID = request.form['channel_id']
        #確認用データ出力(ターミナル/コマンドプロンプトにて確認)
        print(CHANNEL_ID)
        return render_template('index.html', channel_id=CHANNEL_ID)
    else:
    	return render_template('index.html')

if __name__ == '__main__':
    app.run(host="localhost")

app.pyの中では、以下のライブラリを追加しています。

  • apiclient.discovery
  • flask
  • config(config.py参照)

開発工程が進む上で、ライブラリを追加していきます。

グローバル定数/変数の定義では、以下のコードを追加しています。

  • 各種YouTube Data APIセット
  • CHANNEL_ID
  • app

こちらも開発工程が進む上で、定数/変数を追加していきます。

アプリフォルダ内のconfig.py編集

次に、アプリフォルダ内に作成したconfig.pyを開きます。

空のconfig.pyにて、以下のコードを記載します。

#YouTube data api設定
CONFIG = {
   "API_KEY":"Googleアカウントにて取得したAPI KEY",
   "YOUTUBE_API_SERVICE_NAME":"youtube",
   "YOUTUBE_API_VERSION":"v3"
}

API KEYは、自身のGoogleアカウントにて取得したAPI KEYをセットしてください。

Macであればターミナル、Windowsであればコマンドプロンプトにて以下のコードを実行します。

python3 app.py

コマンドを実行するとFlaskアプリが起動するため、http://localhost:5000をブラウザ上で表示させます。

以下の画面がブラウザ上で表示されます。

ブラウザタブのタイトル名/起動画面/入力フォームが正しく表示されれば、OKです。

また、任意のYouTubeチャンネルにおけるチャンネルIDをコピペして入力し、『取得する』をクリックするとYouTube Data APIにてデータ取得できます。

ターミナルあるいはコマンドプロンプトにて、CHANNEL_IDが表示されれば問題なく取得できています。

取得した特定チャンネル情報表示

ここでは、取得した特定チャンネル情報表示にあたって以下の項目を実施します。

  • pandasのインストール
  • templatesフォルダ内のindex.html編集
  • アプリフォルダ内のapp.py編集

はじめにpandasを任意で作成したアプリフォルダ内にインストールします。

pandasのインストール

Macであればターミナル、Windowsであればコマンドプロンプトを利用して任意フォルダまでディレクトリを移動し、pandasをインストールします。

pip3 install pandas

pip3コマンドを利用して任意フォルダにpandasをインストールします。

今後、アプリ開発の中でpandasによるデータ整形を多用するため、pandasの各メソッドは理解しておきましょう。

templatesフォルダ内のindex.html編集

次に、アプリフォルダ内に作成したtemplatesフォルダにあるindex.htmlを開きます。

index.htmlにて、以下のコードを記載します。

  <!-- コード追加1 -->
  {% if channel_id %}
  <div class="container">
    <table class="table table-hover">
      <thead>
        <tr>
          {% for i in channel_columns %}<th scope="col">{{ i|e }}</th>{% endfor %}
        </tr>
      </thead>
      <tbody>
        {% for i in channels %}
        <tr class="table-light">
          {% for j in i %}<td>{{ j|e }}</td>{% endfor %}
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
  {% endif %}
  <!-- コード追加1 -->

ブラウザアプリ画面にて取得したチャンネルIDを確認できた場合に、{% if channel_id %}から{% endif %}の中に記載されているHTML要素を出力表示しています。

以下が追記コードを含めた全体のindex.htmlになります。(追加コードは赤色で記載)

<!doctype html>
<html lang="en">
<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
  <title>Top-Movies</title>
</head>
<body>
  <!-- ヘッダーと入力フォーム -->
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <h1><a href="/">Top-Movies</a></h1>
        <p>YouTubeのCHANNEL IDを入れると直近の動画数 / 動画再生数 / 最もGood評価されている動画を一覧できます.</p>
        <form class="form-inline" method="post">
          <div class="form-group">
            <label class="sr-only" for="user_id"></label>
            <div class="input-group">
              <input id="channel_id" name="channel_id" type="text" class="form-control" placeholder="CHANNEL IDを入力してください.">
              <button class="btn btn-outline-primary" type="submit">取得する</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
  <!-- ヘッダーと入力フォーム -->
  <!-- コード追加1 -->
  {% if channel_id %}
  <div class="container">
    <table class="table table-hover">
      <thead>
        <tr>
          {% for i in channel_columns %}<th scope="col">{{ i|e }}</th>{% endfor %}
        </tr>
      </thead>
      <tbody>
        {% for i in channels %}
        <tr class="table-light">
          {% for j in i %}<td>{{ j|e }}</td>{% endfor %}
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
  {% endif %}
  <!-- コード追加1 -->
</body>
</html>

テーブル内の値は、app.pyにて作成するchannel_columnsとリスト型でreturnしたchannels変数をfor文にてループ処理出力しています。

アプリフォルダ内のapp.py編集

次に、アプリフォルダ内に作成したapp.pyを開きます。

app.pyの中では、以下のライブラリを追加しています。

import pandas as pd #追加①

app.pyの中では、以下のライブラリを追加しています。

  • pandas

ライブラリの追加は、pandasで最後になります。

グローバル定数/変数の定義では、以下のコードを追加しています。

#入力するYouTubeチャンネルID 追加②
CHANNEL_ID = ""

#YouTube data APIで取得するchannelの値 追加③
channel_columns = [
    "チャンネル名",
    "登録者数",
    "投稿本数",
    "登録日"
    ]

index.htmlにて入力されて取得したチャンネルIDを変数CHANNEL_IDに格納します。

channel_columnsでは、チャンネル名/登録者数/投稿本数/登録日を設定しています。

  • CHANNEL_ID
  • channel_columns

こちらも開発工程が進む上で、定数/変数を追加していきます。

次に、YouTube Data APIにてチャンネル情報を取得するget_channel_df関数を作成します。

#特定チャンネルの各種データ取得関数 追加④
def get_channel_df(CHANNEL_ID):
    channels = pd.DataFrame(columns=channel_columns)
    channel_response = youtube.channels().list(
        part = 'snippet,statistics',
        id = CHANNEL_ID
        ).execute()
    for channel_result in channel_response.get("items", []):
        if channel_result["kind"] == "youtube#channel":
            se = pd.Series([
                       channel_result["snippet"]["title"],
                       channel_result["statistics"]["subscriberCount"],
                       channel_result["statistics"]["videoCount"],
                       channel_result["snippet"]["publishedAt"]
                   ]
                   ,channel_columns
                   )
            channels = channels.append(se, ignore_index=True)
    return channels.values.tolist()

get_channel_df関数では引数をCHANNEL_IDとし、プライベート変数channelsとchannel_responseを用意しています。

YouTube Data APIから取得したデータをfor文によって回し、pandasのSeries()メソッドで定義した変数seに格納して変数channelsへappendメソッドを利用して追加しています。

最後に、channels.value.tolist()にてリスト型データを返します。

メインとなるindex関数では、以下のコードを追加しています。

CHANNEL_ID = request.form['channel_id'] #追加⑤
channels = get_channel_df(CHANNEL_ID) #追加⑥
return render_template('index.html', channel_id=CHANNEL_ID, channels=channels, channel_columns=channel_columns) #追加⑦

変数CHANNEL_IDでは、index.htmlにて入力フォームで取得した値を格納しています。

変数channelsでは、get_channel_df関数を呼び出し、リスト型のチャンネルデータを格納しています。

render_template()では、引数としてchannel_id, channels, channel_columnsを持たせてindex.htmlへ受け渡しています。

追加後の全体コードが以下になります。(追加コードは赤色で記載)

from apiclient.discovery import build
import pandas as pd #追加①
from flask import Flask, render_template, request, logging, Response, redirect, flash
from config import CONFIG

#各種YouTube data APIセット
API_KEY = CONFIG["API_KEY"]
YOUTUBE_API_SERVICE_NAME = CONFIG["YOUTUBE_API_SERVICE_NAME"]
YOUTUBE_API_VERSION = CONFIG["YOUTUBE_API_VERSION"]
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=API_KEY)

#入力するYouTubeチャンネルID 追加②
CHANNEL_ID = ""
#YouTube data APIで取得するchannelの値 追加③
channel_columns = [
    "チャンネル名",
    "登録者数",
    "投稿本数",
    "登録日"
    ]

#Flaskの起動
app = Flask(__name__)

#"xxxx.com/"にアクセスする際に実行される関数
@app.route('/', methods = ["GET", "POST"])
def index():
    if request.method == "POST":
        #formのname="channel_id"を取得
        CHANNEL_ID = request.form['channel_id'] #追加⑤
        channels = get_channel_df(CHANNEL_ID) #追加⑥
        return render_template('index.html', channel_id=CHANNEL_ID, channels=channels, channel_columns=channel_columns) #追加⑦
    else:
        return render_template('index.html')

#特定チャンネルの各種データ取得関数 追加④
def get_channel_df(CHANNEL_ID):
    channels = pd.DataFrame(columns=channel_columns)
    channel_response = youtube.channels().list(
        part = 'snippet,statistics',
        id = CHANNEL_ID
        ).execute()
    for channel_result in channel_response.get("items", []):
        if channel_result["kind"] == "youtube#channel":
            se = pd.Series([
                       channel_result["snippet"]["title"],
                       channel_result["statistics"]["subscriberCount"],
                       channel_result["statistics"]["videoCount"],
                       channel_result["snippet"]["publishedAt"]
                   ]
                   ,channel_columns
                   )
            channels = channels.append(se, ignore_index=True)
    return channels.values.tolist()

if __name__ == '__main__':
    app.run(host="localhost")

改めてapp.pyを実行してみると、以下の画面が表示されます。

次に、任意のYouTubeチャンネルのCHANNEL_IDを入力して『取得する』をクリックすると、以下の実行結果が表示されます。

筆者は個人的に書店関連のYouTubeチャンネルにハマっているので、本記事では”有隣堂しか知らない世界”チャンネルのチャンネルIDを入力して実行しました。

あなたの好みでチャンネルIDをコピペしてみてください。

チャンネル内の各動画の視聴回数/高評価数グラフ化

ここでは、チャンネル内の各動画の視聴回数/高評価数グラフ化にあたって以下の項目を実施します。

  • templatesフォルダ内のindex.html編集
  • アプリフォルダ内のapp.py編集

はじめにtemplatesフォルダ内のindex.htmlを編集します。

templatesフォルダ内のindex.html編集

アプリフォルダ内に作成したtemplatesフォルダにあるindex.htmlを開きます。

index.htmlにて、以下のコードを記載します。

<!-- chart.jsのCDNを挿入 -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<head>タグ内に上記のchart.jsに関するCDNコードを挿入します。

  <!-- 混合グラフチャート -->
  {% if channel_id %}
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <canvas id="chart"></canvas>
      </div>
    </div>
  </div>
  {% endif %}
  <!-- 混合グラフチャート -->
  <!-- 混合グラフチャートのスクリプト -->
  <script>
  {% if channel_id %}
  var barData = {
    labels : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
    "{{row['publishedAt']}}",
    {% endfor %}],
    datasets : [
      {
        label: "高評価数",
        backgroundColor: '#d4114f',
        borderColor: '#d4114f',
        borderWidth:5,
        bezierCurve : false,
        data : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
        {{row["likeCount"]}},
        {% endfor %}]
      },{
        label: "視聴回数",
        backgroundColor: '#0dcaf0',
        borderColor: '#0dcaf0',
        type: 'line',
        data : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
        {{row["viewCount"]}},
        {% endfor %}],
      }
    ]
  }
  // draw bar chart
  var mychart = document.getElementById("chart");
  var chart = new Chart(mychart, {
    type:'bar',
    data:barData,
    options: {
      scales: {
        yAxes: [
          {
            ticks: {
              beginAtZero: true,
              min: 0,
              max: 10000
            }
          }
        ]
      }
    }
  });
  {% endif %}
  </script>
  <!-- 混合グラフチャートのスクリプト -->

<body>タグ内に上記の混合グラフチャート用のHTML要素を追加します。

また、<body>タグ内に混合グラフチャート用の<script>タグを追加しています。

ここまでの全体のinde.htmlのコードは以下になります。(追加コードは赤色で記載)

<!doctype html>
<html lang="en">
<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
  <!-- chart.jsのCDNを挿入 -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <title>Top-Movies</title>
</head>
<body>
  <!-- ヘッダーと入力フォーム -->
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <h1><a href="/">Top-Movies</a></h1>
        <p>YouTubeのCHANNEL IDを入れると直近の動画数 / 動画再生数 / 最もGood評価されている動画を一覧できます.</p>
        <form class="form-inline" method="post">
          <div class="form-group">
            <label class="sr-only" for="user_id"></label>
            <div class="input-group">
              <input id="channel_id" name="channel_id" type="text" class="form-control" placeholder="CHANNEL IDを入力してください.">
              <button class="btn btn-outline-primary" type="submit">取得する</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
  <!-- ヘッダーと入力フォーム -->
  <!-- コード追加1 -->
  {% if channel_id %}
  <div class="container">
    <table class="table table-hover">
      <thead>
        <tr>
          {% for i in channel_columns %}<th scope="col">{{ i|e }}</th>{% endfor %}
        </tr>
      </thead>
      <tbody>
        {% for i in channels %}
        <tr class="table-light">
          {% for j in i %}<td>{{ j|e }}</td>{% endfor %}
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
  {% endif %}
  <!-- コード追加1 -->
  <!-- 混合グラフチャート -->
  {% if channel_id %}
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <canvas id="chart"></canvas>
      </div>
    </div>
  </div>
  {% endif %}
  <!-- 混合グラフチャート -->
  <!-- 混合グラフチャートのスクリプト -->
  <script>
  {% if channel_id %}
  var barData = {
    labels : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
    "{{row['publishedAt']}}",
    {% endfor %}],
    datasets : [
      {
        label: "高評価数",
        backgroundColor: '#d4114f',
        borderColor: '#d4114f',
        borderWidth:5,
        bezierCurve : false,
        data : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
        {{row["likeCount"]}},
        {% endfor %}]
      },{
        label: "視聴回数",
        backgroundColor: '#0dcaf0',
        borderColor: '#0dcaf0',
        type: 'line',
        data : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
        {{row["viewCount"]}},
        {% endfor %}],
      }
    ]
  }
  // draw bar chart
  var mychart = document.getElementById("chart");
  var chart = new Chart(mychart, {
    type:'bar',
    data:barData,
    options: {
      scales: {
        yAxes: [
          {
            ticks: {
              beginAtZero: true,
              min: 0,
              max: 10000
            }
          }
        ]
      }
    }
  });
  {% endif %}
  </script>
  <!-- 混合グラフチャートのスクリプト -->
</body>
</html>

アプリフォルダ内のapp.py編集

次に、アプリフォルダ内に作成したapp.pyを開きます。

グローバル定数/変数の定義では、以下のコードを追加しています。

#再帰的処理に利用する変数 追加⑧
nextPagetoken = None
nextpagetoken = None

#YouTube data APIで取得するvideoの値 追加⑨
video_columns = [
    "title",
    "viewCount",
    "likeCount",
    "dislikeCount",
    "commentCount",
    "publishedAt"
    ]

後述するget_videos_df関数で利用する再帰的処理用変数を定義しています。

video_columnsでは、title/viewCount/likeCount/dislikeCount/commentCount/publishedAtを設定しています。(キャメルケースによる命名方式を採用)

  • nextPagetoken
  • nextpagetoken
  • video_columns

定数/変数の追加は最後になります。

次に、YouTube Data APIにて各種動画データを取得するget_videos_df関数を作成します。

#特定チャンネルの各種ビデオデータ取得関数 追加⑩
def get_videos_df(CHANNEL_ID, nextPagetoken, nextpagetoken):
    searches = []
    videos = pd.DataFrame(columns=video_columns)
    while True:
        if nextPagetoken != None:
            nextpagetoken = nextPagetoken
        search_response = youtube.search().list(
            part = "snippet",
            channelId = CHANNEL_ID,
            maxResults = 50,
            order = "date", #日付順にソート
            pageToken = nextpagetoken #再帰的に指定
            ).execute()
        for search_result in search_response.get("items", []):
            if search_result["id"]["kind"] == "youtube#video":
                searches.append(search_result["id"]["videoId"])

        try:
            nextPagetoken = search_response["nextPageToken"]
        except:
            break

    for result in searches:
        video_response = youtube.videos().list(
            part = 'snippet,statistics',
            id = result
            ).execute()
        for video_result in video_response.get("items", []):
            if video_result["kind"] == "youtube#video":
                se = pd.Series([
                    video_result["snippet"]["title"],
                    video_result["statistics"]["viewCount"],
                    video_result["statistics"]["likeCount"],
                    video_result["statistics"]["dislikeCount"],
                    video_result["statistics"]["commentCount"],
                    video_result["snippet"]["publishedAt"]
                ]
                ,video_columns
                )
            videos = videos.append(se, ignore_index=True)
    return videos

get_videos_df関数では引数をCHANNEL_ID, nextPagetoken, nextpagetokenとし、プライベート変数searchesとvideosを用意しています。

YouTube Data APIから取得したデータをfor文によって回し、pandasのSeries()メソッドで定義した変数seに格納して変数videosへappendメソッドを利用して追加しています。

最後に、各種動画データを格納したvideosにてDataFrame型データを返します。

メインとなるindex関数では、以下のコードを追加しています。

videos = get_videos_df(CHANNEL_ID, nextPagetoken, nextpagetoken) #追加⑪
return render_template('index.html', channel_id=CHANNEL_ID, channels=channels, channel_columns=channel_columns, videos=videos, video_columns=video_columns) #追加⑫

変数videosでは、get_videos_df関数を呼び出し、DataFrame型の各種動画データを格納しています。

render_template()では、引数としてchannel_id, channels, channel_columns, videos, video_columnsを持たせてindex.htmlへ受け渡しています。

追加後の全体コードが以下になります。(追加コードは赤色で記載)

from apiclient.discovery import build
import pandas as pd #追加①
from flask import Flask, render_template, request, logging, Response, redirect, flash
from config import CONFIG

#各種YouTube data APIセット
API_KEY = CONFIG["API_KEY"]
YOUTUBE_API_SERVICE_NAME = CONFIG["YOUTUBE_API_SERVICE_NAME"]
YOUTUBE_API_VERSION = CONFIG["YOUTUBE_API_VERSION"]
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=API_KEY)

#入力するYouTubeチャンネルID 追加②
CHANNEL_ID = ""

#再帰的処理に利用する変数 追加⑧
nextPagetoken = None
nextpagetoken = None

#YouTube data APIで取得するchannelの値 追加③
channel_columns = [
    "チャンネル名",
    "登録者数",
    "投稿本数",
    "登録日"
    ]

#YouTube data APIで取得するvideoの値 追加⑨
video_columns = [
   "title",
   "viewCount",
   "likeCount",
   "dislikeCount",
   "commentCount",
   "publishedAt"
   ]

#Flaskの起動
app = Flask(__name__)

#"xxxx.com/"にアクセスする際に実行される関数
@app.route('/', methods = ["GET", "POST"])
def index():
    if request.method == "POST":
    	#formのname="channel_id"を取得
    	CHANNEL_ID = request.form['channel_id'] #追加④
    	channels = get_channel_df(CHANNEL_ID) #追加⑤
    	videos = get_videos_df(CHANNEL_ID, nextPagetoken, nextpagetoken) #追加⑪
    	return render_template('index.html', channel_id=CHANNEL_ID, channels=channels, channel_columns=channel_columns, videos=videos, video_columns=video_columns) #追加⑫
    else:
    	return render_template('index.html')

#特定チャンネルの各種データ取得関数 追加④
def get_channel_df(CHANNEL_ID):
    channels = pd.DataFrame(columns=channel_columns)
    channel_response = youtube.channels().list(
        part = 'snippet,statistics',
        id = CHANNEL_ID
        ).execute()
    for channel_result in channel_response.get("items", []):
    	if channel_result["kind"] == "youtube#channel":
    	    se = pd.Series([
                       channel_result["snippet"]["title"],
                       channel_result["statistics"]["subscriberCount"],
                       channel_result["statistics"]["videoCount"],
                       channel_result["snippet"]["publishedAt"]
                   ]
                   ,channel_columns
                   )
            channels = channels.append(se, ignore_index=True)
    return channels.values.tolist()

#特定チャンネルの各種ビデオデータ取得関数 追加⑩
def get_videos_df(CHANNEL_ID, nextPagetoken, nextpagetoken):
    searches = []
    videos = pd.DataFrame(columns=video_columns)
    #再帰的処理に利用する変数
    nextPagetoken = None
    nextpagetoken = None
    while True:
        if nextPagetoken != None:
    	    nextpagetoken = nextPagetoken
    	search_response = youtube.search().list(
    	    part = "snippet",
    	    channelId = CHANNEL_ID,
    	    maxResults = 50,
    	    order = "date", #日付順にソート
    	    pageToken = nextpagetoken #再帰的に指定
    	    ).execute()
    	for search_result in search_response.get("items", []):
    	    if search_result["id"]["kind"] == "youtube#video":
    	        searches.append(search_result["id"]["videoId"])

    	try:
    	    nextPagetoken = search_response["nextPageToken"]
    	except:
    	    break

    for result in searches:
        video_response = youtube.videos().list(
    	    part = 'snippet,statistics',
    	    id = result
    	    ).execute()
    	for video_result in video_response.get("items", []):
    	    if video_result["kind"] == "youtube#video":
    	        se = pd.Series([
                    video_result["snippet"]["title"],
                    video_result["statistics"]["viewCount"],
                    video_result["statistics"]["likeCount"],
                    video_result["statistics"]["dislikeCount"],
                    video_result["statistics"]["commentCount"],
                    video_result["snippet"]["publishedAt"]
                ]
                ,video_columns
                )
    	    videos = videos.append(se, ignore_index=True)
    return videos

if __name__ == '__main__':
    app.run(host="localhost")

改めてapp.pyを実行してみると、以下の画面が表示されます。

次に、任意のYouTubeチャンネルのCHANNEL_IDを入力して『取得する』をクリックすると、以下の実行結果が表示されます。

表示されたグラフ上部の高評価数/視聴回数をクリックすると、単体でグラフを確認することができて左側の数字変動やグラフサイズを調整してくれます。

chart.jsは非常に有効的なグラフチャートを示します。

各動画データのテーブル表示

ここでは、各動画データのテーブル表示にあたって以下の項目を実施します。

  • templatesフォルダ内のindex.html編集

各動画データのテーブル表示を実行するためにtemplatesフォルダ内のindex.html編集します。

templatesフォルダ内のindex.html編集

アプリフォルダ内に作成したtemplatesフォルダにあるindex.htmlを開きます。

index.htmlにて、以下のコードを記載します。

  <!-- コード追加2 -->
  {% if channel_id %}
  <div class="container">
    <div class="table-frame">
      <table class="table table-hover">
        <thead>
          <tr>
            {% for i in video_columns %}<th scope="col">{{ i|e }}</th>{% endfor %}
          </tr>
        </thead>
        <tbody>
          {% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
          <tr class="table-light">
            {% for i in row %}<td>{{ i|e }}</td>{% endfor %}
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
  {% endif %}
  <!-- コード追加2 -->

<body>タグ内に上記のHTML要素を挿入します。

また、このままテーブル表示を実行してしまうと、ブラウザ画面表示後にテーブルが長くなってしまい、画面全体のスクロール量が膨大になります。

そのため、テーブルの高さを定めてコンパクトにスクロールできるように整形します。

<style>
  .table-frame{
    width: 100%;
    height: 600px;
    overflow: auto;
  }
</style>

ここまでの全体のinde.htmlのコードは以下になります。(追加コードは赤色で記載)

<!doctype html>
<html lang="en">
<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
  <!-- chart.jsのCDNを挿入 -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <title>Top-Movies</title>
<style>
  .table-frame{
    width: 100%;
    height: 600px;
    overflow: auto;
  }
</style>
</head>
<body>
  <!-- ヘッダーと入力フォーム -->
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <h1><a href="/">Top-Movies</a></h1>
        <p>YouTubeのCHANNEL IDを入れると直近の動画数 / 動画再生数 / 最もGood評価されている動画を一覧できます.</p>
        <form class="form-inline" method="post">
          <div class="form-group">
            <label class="sr-only" for="user_id"></label>
            <div class="input-group">
              <input id="channel_id" name="channel_id" type="text" class="form-control" placeholder="CHANNEL IDを入力してください.">
              <button class="btn btn-outline-primary" type="submit">取得する</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
  <!-- ヘッダーと入力フォーム -->
  <!-- コード追加1 -->
  {% if channel_id %}
  <div class="container">
    <table class="table table-hover">
      <thead>
        <tr>
          {% for i in channel_columns %}<th scope="col">{{ i|e }}</th>{% endfor %}
        </tr>
      </thead>
      <tbody>
        {% for i in channels %}
        <tr class="table-light">
          {% for j in i %}<td>{{ j|e }}</td>{% endfor %}
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
  {% endif %}
  <!-- コード追加1 -->
  <!-- 混合グラフチャート -->
  {% if channel_id %}
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <canvas id="chart"></canvas>
      </div>
    </div>
  </div>
  {% endif %}
  <!-- 混合グラフチャート -->
  <!-- 混合グラフチャートのスクリプト -->
  <script>
  {% if channel_id %}
  var barData = {
    labels : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
    "{{row['publishedAt']}}",
    {% endfor %}],
    datasets : [
      {
        label: "高評価数",
        backgroundColor: '#d4114f',
        borderColor: '#d4114f',
        borderWidth:5,
        bezierCurve : false,
        data : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
        {{row["likeCount"]}},
        {% endfor %}]
      },{
        label: "視聴回数",
        backgroundColor: '#0dcaf0',
        borderColor: '#0dcaf0',
        type: 'line',
        data : [{% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
        {{row["viewCount"]}},
        {% endfor %}],
      }
    ]
  }
  // draw bar chart
  var mychart = document.getElementById("chart");
  var chart = new Chart(mychart, {
    type:'bar',
    data:barData,
    options: {
      scales: {
        yAxes: [
          {
            ticks: {
              beginAtZero: true,
              min: 0,
              max: 10000
            }
          }
        ]
      }
    }
  });
  {% endif %}
  </script>
  <!-- 混合グラフチャートのスクリプト -->
  <!-- コード追加2 -->
  {% if channel_id %}
  <div class="container">
    <div class="table-frame">
      <table class="table table-hover">
        <thead>
          <tr>
            {% for i in video_columns %}<th scope="col">{{ i|e }}</th>{% endfor %}
          </tr>
        </thead>
        <tbody>
          {% for index, row in videos.sort_values(by="publishedAt", ascending=True).iterrows() %}
          <tr class="table-light">
            {% for i in row %}<td>{{ i|e }}</td>{% endfor %}
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
  {% endif %}
  <!-- コード追加2 -->
</body>
</html>

改めてapp.pyを実行してみると、以下の画面が表示されます。

次に、任意のYouTubeチャンネルのCHANNEL_IDを入力して『取得する』をクリックすると、以下の実行結果が表示されます。

画面下部に表示されるテーブルにて、heght: 600px以上となればスクロール機能が実装されます。

動画本数が比較的多いチャンネルで動画データを取得して確認してみてください。

Flaskによる動画データ分析アプリのデプロイ

せっかくなので、ここまで開発したアプリケーションをネット上で公開するためにデプロイ方法をまとめます。

先にお伝えしておきますが、本記事で開発したアプリは残念ながらAPI KEYを指定した状態で開発しているため、利用者が一定の制限を超えてテストする可能性を考慮し、URL公開は控えておきます。

そのため、ぜひ自身で開発したアプリを自分用としてネット公開し、利用してみてください。

あくまで、アプリのデプロイ方法として参考にして頂けると幸いです。

  • Herokuアカウント作成
  • ローカルPCのセットアップ(デプロイ準備)
  • Herokuにアプリをデプロイ

手順に沿って解説していきます。

Herokuアカウント作成

公式サイトのHerokuにアクセスします。

『新規登録』をクリックしてアカウントを作成します。

手順に沿って登録すれば、すぐにアカウント作成を完了できます。

ローカルPCのセットアップ(デプロイ準備)

Herokuにアプリをデプロイするために以下のファイルを作成します。

  • requirements.txt
  • runtime.txt
  • Procfile

requirements.txtは、任意のディレクトリにて以下のコマンドを実行します。

pip3 freeze > requirements.txt

コマンド実行すると、任意のディレクトリにrequirements.txtが作成されます。

herokuに何のライブラリをインストールすれば良いのか記載しています。

requirements.txtは、Pythonのライブラリで何がインストールされているのかをリストにしてるテキストデータになります。

また、runtime.txtも以下のコマンドで作成できます。

pip3 freeze > runtime.txt

次に、runtime.txtの設定を行います。

echo python-3.8.5 > runtime.txt

上記のコードにて、runtime.txtにpythonのバージョンを指定させます。

最後に、Procfileの指定を行います。

echo web: gunicorn app:app --log-file=- > Procfile

任意のディレクトリ内のファイル構成が以下になります。

top-movies
├── app.py
├── requirements.txt
├── runtime.txt
├── Procfile
├── config.py
└── templates
    └── index.html

任意のフォルダを開いて確認してみてください。

Herokuにアプリをデプロイ

最後に、Herokuへアプリをデプロイします。

CUIコマンドを実行するため、開発フォルダがあるディレクトリにてターミナルあるいはコマンドプロンプトで実施してください。

Homebrewのインストールがまだできていない人は、以下のコードを実行します。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

次に、Herokuをインストールします。

brew tap heroku/brew
brew install heroku

上記のコードを実行すると、herokuのインストールが完了します。

次に、Herokuへログインを行います。

ログインする際は、現在アプリ開発しているフォルダのディレクトリから実行しましょう。

heroku login

heroku loginのコマンドを実行すると、ブラウザ画面にログイン状態を確認されます。

次にHerokuにて適当なアプリ名を作成して、python起動を明示的に示すコマンドを実行します。

heroku create <app-name>
heroku buildpacks:set heroku/python
heroku ps:scale web=1

<app-name>の箇所に、任意のアプリケーション名を入力して実行してください。

最後に、gitコマンドにてアプリケーションをpushすれば完了です。

git init
git add .
git commit -m "first commit"
git push heroku master

gitを初期化したのち、git add .にて現在のディレクトリ内のファイル/フォルダをコミット対象にします。

git commitにてファイル/フォルダを変更/追加できるように保存しています。

最後に、git push heroku masterにてファイル/フォルダをpushしてあげれば、作成したアプリケーション名でheroku用のURLが発行されます。

Flask + YouTube Data APIによる動画データ分析アプリ → https://top-movies-up.herokuapp.com/

まとめ

Flask + YouTube Data APIによる動画データ分析アプリ → https://top-movies-up.herokuapp.com/

本記事では、YouTube Data APIのAPI KEYがデプロイ後変更することができないため、APIを何度も利用すると制限がかかり、アプリが使用できなくなってしまいます。

そのため、デプロイしたアプリに若干修正を加え、API KEYとCHANNEL IDを入力フォームから送信できるよう変更しました。

動画データ分析を試してみたい人は、YouTube Data APIのKEYを取得し、お好みのYouTubeチャンネルのURLバーに記載されているチャンネルIDをコピーして実行してみてください。



ABOUTこの記事をかいた人

sugi

大学卒業後、IT企業に就職を果たす。システム開発・人工知能に触れながら大手企業と業務をこなす。2年半後脱サラし、フリーランス活動経験を経て 2019年2月から起業し、今に至る。 自社サービス及び製品を開発、ブログ収入、クラウドソーシングなど、多方面で売り上げを立てている。