LoginSignup
0
0

DjangoでMOBAのランキングを作る③

Posted at

はじめに

前回の記事に引き続き、Django✕Herokuでモバイルゲーム「Mobile Legends: Bang Bang」のヒーローランキングを作成していきます。今回は特にスクレイピングとランキング表示の実装に注目します。

前回の記事はこちらです:

スクレイピングでランキング作成

モデルを作成

スクレイピングで取得したデータを保存するために、main/models.pyにHeroMetaDataモデルを定義します。このモデルには、各ヒーローの名前、勝率、ピック率、BAN率、参照日、ランクレベル、作成日時を保存します。

main/models.py
class HeroMetaData(models.Model):
    name = models.TextField()
    win_rate = models.FloatField()
    pick_rate = models.FloatField() 
    ban_rate = models.FloatField()
    reference_date = models.DateField()
    rank_level = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = (('name', 'rank_level', 'reference_date'),)

モデルを定義した後、マイグレーションを実行してデータベースにテーブルを作成します。ローカル環境だけでなく、Heroku環境でもマイグレーションを実行することを忘れないでください。

python manage.py makemigrations
python manage.py migrate
heroku run python manage.py migrate

スクレイピングするファイルを作る

次に、スクレイピングを行うためのファイルを作成します。今回は、下記サイトからデータを取得します。必要なデータは各ヒーローの勝率、BAN率、ピック率です。

まず、必要なライブラリをインストールします。

pip install pandas selenium webdriver_manager beautifulsoup4

続いて、main/scraper.pyにスクレイピングのロジックを記述します。スクレイピングしたデータはHeroMetaDataモデルを使ってデータベースに保存するようにしてください。

scraper.py
import time

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from datetime import datetime
from .models import HeroMetaData


def scrape_mlbb_meta_data():
    # スクレイピングロジックを記述
    # ...

管理コマンドの作成

main/management/commands/run_scraper.pyに、scrape_mlbb_meta_data関数を実行するための管理コマンドを作成します。

run_scraper.py
from django.core.management.base import BaseCommand
from main.scraper import scrape_mlbb_meta_data

class Command(BaseCommand):
    def handle(self, *args, **options):
        scrape_mlbb_meta_data()

管理コマンドを実行し、データが正しくデータベースに保存されているか確認します。

python manage.py run_scraper

スクリーンショット 2024-03-29 1.49.54.png

データが保存されていることが確認できました。

ランキングの表示

ビューの作成

main/views.pyに、スクレイピングしたデータを使ってランキングを作成するロジックを記述します。

  1. ヒーローのデータをz-scoreで評価し、各ヒーローの相対的な強さを計算
  2. z-scoreに基づいてヒーローにランクを割り当て
  3. ランクごとにヒーローの情報をグループ化
  4. ビューにデータを渡す

ランキング表示には、各ヒーローの画像を使用します。画像はWordPress上で保存されており、画像URLがヒーローのデータと一緒にデータベースに保存されています。

views.py
from django.shortcuts import render
from .models import Hero
from django.db.models import Max
from .models import HeroMetaData  
import pandas as pd

# 定数の定義
RANK_THRESHOLDS = [1.0, 0.5, 0, -0.5, -1.0]  # ランクの閾値
RANK_LABELS = ['S+', 'S', 'A+', 'A', 'B', 'C']  # ランクのラベル

def get_rank_from_z_score(score):
    """
    z-scoreからランクを取得する関数 
    """
    for threshold, label in zip(RANK_THRESHOLDS, RANK_LABELS):
        if score >= threshold:
            return label
    return 'C'

def hero_ranking(request):
    # Heroモデルから必要なデータを取得
    heroes = Hero.objects.all().values_list('name_jp', 'name_en', 'image_url')
    # ロジックを記述
    # ... 

テンプレートの作成

ビューから渡されたデータをmain/templates/main/hero_ranking.htmlで表示します。各ランクごとにヒーローの画像を表示するようにします。

hero_ranking.html
<!DOCTYPE html>
<html>
    <head>
        <title>Hero List</title>
    </head>
    <body>
        <h1>ヒーローTier</h1>
        <p>Latest Update: {{ latest_date }}</p>
        <h2>S+ Heroes</h2>
        {% for hero in s_plus_heroes %}
        <img src="{{ hero.2 }}" alt="{{ hero.0 }}" width="100">
        {% endfor %}
        
        <h2>S Heroes</h2> 
        {% for hero in s_heroes %}
        <img src="{{ hero.2 }}" alt="{{ hero.0 }}" width="100">
        {% endfor %}
        
        ...
</html>

これでローカル環境ではランキングが表示できるようになりました。

127.0.0.1_8000_ (1).png

Herokuでの実行

次に、Heroku上でもスクレイピングとランキング表示ができるように設定します。

ビルドパックの設定

requirements.txtに必要なライブラリを追加します。

...
pandas==2.2.1
selenium==4.19.0
webdriver_manager==4.0.1
beautifulsoup4==4.12.3

当初、以下の記事を参考にビルドパックを設定していましたが、この方法は非推奨になっていました。

代わりに、以下のビルドパックが推奨されていました。

このビルドパックは、ChromeとChromeDriverのバージョンを自動で合わせてくれるため、バージョン違いによるエラーを防ぐことができます。

以下のコマンドでビルドパックを追加します。

heroku create --buildpack https://github.com/heroku/heroku-buildpack-google-chrome.git

その後、アプリを再デプロイします。

git commit --allow-empty -m "Update buildpacks" 
git push heroku main

上手くいかなかった

これでスクレイピングができるようになったので、まずは下記コマンドで取ってみることにしました。

heroku run python manage.py run_scraper

ただ、残念ながら、Heroku上でスクレイピングを実行することができませんでした。スクレイピング自体はできたのですが、対象のサイトを表示した際に表示されるプライバシーポリシーを上手く閉じることができず、データを取得できませんでした。

m.mobilelegends.com_en_rank.png

ローカル環境では問題なくプライバシーポリシーを閉じられるのですが、Heroku上では何故か閉じることができません。様々な方法を試しましたが、上手くいかなかったため、Heroku上でのスクレイピングは諦めることにしました。

データ同期の自動化

なので、ローカルでスクレイピングしたデータをJSON形式で保存し、そのデータをHerokuに同期するようにします。

scraper.pyにJSONファイルを作成するロジックを追加します。

def create_mlbb_meta_data():
    # ロジックを記載

main/management/commands/run_create_hero_meta.pyを作成し、データ作成コマンドを実装します。

run_create_hero_meta.py
from django.core.management.base import BaseCommand
from main.scraper import create_mlbb_meta_data

class Command(BaseCommand):
    def handle(self, *args, **options):
        create_mlbb_meta_data()

下記コマンドを実行するとJSONファイルを作成できます。

 python manage.py run_create_hero_meta

作成したJSONをデータベースに保存するロジックをscraper.pyに追加します。

def save_latest_meta_data_to_db():
    # ロジックを記載  

実行すると下記のようになります。作成しているか更新しているかを確認できるようにしました。

% python manage.py run_save_latest_meta_data_to_db
Updated: 124 records
Created: 0 records

このコマンドはHeroku側で問題なく実行できました。

% heroku run python manage.py run_save_latest_meta_data_to_db
Updated: 0 records
Created: 124 records

GitHubアクションによる自動化

データを最新の状態に保つ工程をGitHubアクションで自動化します。

.github/workflows/update_meta_data.ymlを作成し、自動化の手順を記述します。

update_meta_data.yml
name: Update Meta Data  # ワークフローの名前
# ロジックを記載
...

GitHubのリポジトリ設定ページで、HEROKU_API_KEYHEROKU_APP_NAMEのシークレットを設定します。

また、テストとして手動でワークフローを実行できるようにするため、workflow_dispatchトリガーを追加します。

しかし、GitHub上でワークフローを実行したところ、Heroku上で実行した場合と同様に、プライバシーポリシーが上手く閉じられずデータを取得できませんでした。

そのため、現時点ではコマンドの実行などは手動で行うことにしました。完全自動化については、今後も検討を続けていきます。

まとめ

とりあえず最低限としてヒーローをランキングで表示できるようになりました。
次はBootstrapを入れて見た目を整えていきます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0