0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【LOLアプリ開発 #1】Python(Flask)とRiot APIでサモナー検索APIを構築!

Last updated at Posted at 2025-11-18

みなさん、こんにちは!

今回からは、僕の最近は待っているゲーム「LoL」のデータを活用したWebアプリを、React(フロントエンド)とPython(バックエンド)で構築していきます。

🎮 そもそもLeague of Legends (LoL) とは?

League of Legends(リーグ・オブ・レジェンズ)、略してLoLは、世界中で最も人気のあるPCゲームの一つです。
5人対5人のチームで戦うMOBA(Multiplayer Online Battle Arena)ジャンルのゲームで、勝敗を決めるのは、個人のスキルだけでなく、チームの戦略、そして大量のデータです。

なぜ、LoLを開発題材にするのか?

それは、Riot Gamesが公式に膨大で詳細なデータ(API)を公開しているからです。
プレイヤーのランク、試合ごとのキル・デス・アシスト(KDA)、チャンピオンの熟練度など、開発者にとって「料理しがいのあるデータ」が満載です。

あと、好きなものに関してなら楽しくかけるかなと思ったので…

この連載を通じて、あなたはプログラミングのスキルを磨きながら、データ駆動型の分析に基づいた自分だけのLoLツールを開発できるようになります!

連載ロードマップ:あなただけのLOLアプリを作ろう!

フェーズ 記事のテーマ(予定) 目的
I. 基盤構築 #1. Riot APIとFlaskでサモナー検索APIを構築 (←今ここ) APIキーの安全管理と、基本情報の取得を実現する。
II. データ強化 #2. ランク情報とアイコン表示の実装 サモナーのTierとアイコンを画面に表示し、見た目を華やかにする。
III. 試合データ解析 #3. 直近の試合履歴と勝率データの取得 最新の戦績データ(KDA、ビルドなど)を解析し、表示する。
IV. 応用機能開発 #4. DDragonでチャンピオン情報を統合 チャンピオン画像を動的に表示し、Webアプリとしての完成度を高める。

今回のゴールは、上の表の通り、基盤構築を完了させることです!
Python初心者でもわかるように丁寧に解説するのでご安心ください。

記事のゴール

  • Riot Games Developer PortalでAPIキーを取得する。
  • PythonFlaskの環境を構築する。
  • APIキーを.envで安全に扱い、サモナー検索APIを実装する。
  • ReactとPythonサーバーを連携させ、画面に基本情報を表示する。

1. 開発の準備:APIキーと環境構築

1.1. Riot Games APIキーの取得

LOLのデータにアクセスするには、Riot Games Developer PortalでAPIキーが必要です。

  1. Riot Games Developer Portalにアクセスし、ログインします。
  2. ログイン後、ページ上部の「Generate Personal Key」をクリックし、APIキー(RGAPI-xxxxx...から始まる文字列)をコピーしておいてください。

⚠️ 注意!: 開発用キーは24時間で失効します。失効したら再発行して.envを更新すればOKです。

1.2. Pythonの環境構築

サーバーサイドのプロジェクトフォルダ(例: my-api-server)内で、Pythonの仮想環境を作成し、必要なライブラリをインストールします。

# サーバーフォルダに移動
cd my-api-server 

# 仮想環境を作成 (venv)
python -m venv venv

# 仮想環境を起動
# Windowsの場合: .\venv\Scripts\activate
# Mac/Linuxの場合: source venv/bin/activate

# 必要なパッケージをインストール
# Flask: Webフレームワーク本体
# requests: HTTPリクエストライブラリ
# python-dotenv: .envファイル読み込み用
# flask-cors: CORS設定用(Reactとの連携に必須)
pip install flask python-dotenv requests flask-cors

2. Python (Flask) サーバーの構築

2.1. APIキーの安全な管理(.envの準備)

APIキーをコード内に直接書かず、安全に管理するために.envファイルを作成します。

my-api-serverフォルダの直下に、新しく.envファイルを作成し、以下の内容を記述します。

# my-api-server/.env
# ここにコピーしたAPIキーを貼り付けます
RIOT_API_KEY="ここにAPIキーを貼り付ける"

2.2. Flaskアプリケーションの基本定義

my-api-serverフォルダ直下に、app.pyファイルを作成し、以下の基本コードを記述します。

# my-api-server/app.py - 基本設定

import os
from flask import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv

# .envファイルをロードし、APIキーを取得
load_dotenv() 
RIOT_API_KEY = os.getenv('RIOT_API_KEY') 

app = Flask(__name__)
CORS(app) # Reactからのアクセスを許可

# Riot APIのベースURL(日本サーバー)
RIOT_BASE_URL = 'https://jp1.api.riotgames.com/lol/summoner/v4/summoners/by-name/'

# サーバー起動確認用の簡単なルート
@app.route('/')
def hello():
    return 'Flask Server is running!'

if __name__ == '__main__':
    # 開発サーバーをポート5000で実行
    app.run(port=5000, debug=True)

【コード解説:基本設定】

  • load_dotenv(): .envファイルを読み込み、RIOT_API_KEYを環境変数として利用可能にします。
  • CORS(app): React(通常ポート3000)からFlask(通常ポート5000)へアクセスできるようにするための必須設定です。
  • app.run(port=5000, debug=True): Flaskのデフォルトポートは5000なので、これに合わせてサーバーを起動します。

3. サモナー情報取得APIの実装

Riot APIを叩き、サモナー情報を取得するロジックをapp.pyに追加します。
今回はrequestsライブラリを使います。

app.pyhello()関数の定義の下に、以下の新しい関数を追加してください。

# my-api-server/app.py (app.route('/')の下に追加)

import requests # requestsライブラリをインポート

# ... 中略(既存コード) ...

# サモナー情報を取得するAPI (GET /api/lol/summoner)
@app.route('/api/lol/summoner', methods=['GET'])
def get_summoner_data():
    # クライアントからのクエリパラメータ(?name=〇〇)を取得
    summoner_name = request.args.get('name') 
    
    if not summoner_name:
        return jsonify({"message": "サモナー名を入力してください。"}), 400

    try:
        url = f"{RIOT_BASE_URL}{summoner_name}"
        
        # Riot APIにリクエストを送信
        api_response = requests.get(
            url,
            headers={
                'X-Riot-Token': RIOT_API_KEY # ヘッダーにAPIキーを設定
            }
        )

        # Riot APIからの応答をチェック
        if api_response.status_code == 200:
            return jsonify(api_response.json())
        else:
            # Riot APIがエラーを返した場合、エラー情報とステータスコードをそのまま返す
            return jsonify(api_response.json()), api_response.status_code

    except Exception as e:
        print(f"Riot API呼び出しエラー: {e}")
        return jsonify({"message": "サーバー内部エラーが発生しました。"}), 500

【コード解説:APIの実装】

  • @app.route(...): Flaskでエンドポイントを定義するデコレータです。
  • request.args.get('name'): Reactから送られてくるURLクエリパラメータ(?name=...)から、サモナー名を取得します。
  • requests.get(...): Pythonで外部APIを呼び出す標準的な方法です。headersX-Riot-Tokenを設定して認証しています。
  • api_response.status_code: 応答ステータスコードを確認し、エラーの場合はそのエラー情報とステータスコードをクライアントに返します。

4. React(クライアント)の新規実装と修正

Reactクライアント側(my-react-app/src/App.jsx)に、サモナー名を入力し、Pythonサーバーにリクエストを送信するためのロジックとUIを新規で追加します。

ステップ 4.1:API URLの変更と新しいステートの定義

まず、ReactがPythonサーバー(ポート5000)と通信するように設定を変更し、検索に必要な新しいuseStateを追加します。

my-react-app/src/App.jsxを開き、以下のコードを追加・修正してください。

// src/App.jsx (最上部)

// PythonサーバーのベースURL (5000番ポート)
const PYTHON_API_BASE_URL = 'http://localhost:5000'; 

function App() {
  // LOL検索機能のためのステートを定義
  const [searchSummonerName, setSearchSummonerName] = useState(''); // フォーム入力値
  const [summonerData, setSummonerData] = useState(null); // 検索結果データ
  const [searchError, setSearchError] = useState(null); // 検索エラーメッセージ
  
  // ...

【コード解説:新規ステート】

  • searchSummonerName: ユーザーがフォームに入力したサモナー名を保持します。
  • summonerData: 検索成功後、Pythonサーバーから返されたサモナーのIDやレベルなどの情報を保持します。
  • searchError: 検索失敗時(例: サモナーが存在しない)に表示するエラーメッセージを保持します。

ステップ 4.2:サモナー検索ロジックの追加

サモナー名を使ってPythonサーバーのエンドポイントを呼び出すhandleSearchSummoner関数を、CRUD関数群の下に新規で追加します。

// src/App.jsx (App関数内に新規追加)

// LOL検索ロジック
const handleSearchSummoner = async (e) => {
  e.preventDefault(); // フォーム送信によるページリロードを防ぐ
  if (!searchSummonerName.trim()) return;

  setSearchError(null);
  setSummonerData(null);
  
  try {
    // PythonサーバーのLOLエンドポイントを呼び出し
    const response = await fetch(
      `${PYTHON_API_BASE_URL}/api/lol/summoner?name=${searchSummonerName}`
    );

    if (!response.ok) {
      // サーバーからのエラー(404など)を処理
      const errorDetail = await response.json();
      setSearchError(`検索失敗: ${errorDetail.status?.message || errorDetail.message || '不明なエラー'}`);
      return;
    }

    const data = await response.json();
    setSummonerData(data); // データをステートに保存
    
  } catch (err) {
    setSearchError('API通信中にエラーが発生しました。');
    console.error(err);
  }
};

【コード解説:検索ロジック】

  • fetchのURL: PYTHON_API_BASE_URLを使ってhttp://localhost:5000/api/lol/summoner?name=...というURLを構築しています。
  • response.ok: HTTPステータスが200番台でなければエラーと判断し、Pythonサーバーから返されたエラー情報を取得してユーザーに表示します。

ステップ 4.3:検索フォームと結果表示のUIを追加

最後に、return文内の、既存のタスクフォームの下に、以下のUIを新規で追加します。

// src/App.jsx (return文内)

return (
    <div className="App">
      <h1>LoL Data App Builder</h1>
      
      <hr style={{ marginTop: '40px', borderColor: '#4a90e2' }} />

      <h2>🎮 LoL サモナー検索</h2>

      {/* 検索フォーム - onSubmitでhandleSearchSummonerを呼び出す */}
      <form className="task-form" onSubmit={handleSearchSummoner}>
          <input
            type="text"
            value={searchSummonerName}
            onChange={(e) => setSearchSummonerName(e.target.value)}
            placeholder="サモナー名を入力..."
            aria-label="サモナー名"
            style={{ flexGrow: 1 }}
          />
          <button type="submit">検索開始!</button>
      </form>

      {/* 検索結果の表示 */}
      {searchError && <p className="error">{searchError}</p>}

      {summonerData && (
          <div style={{ padding: '15px', border: '1px solid #ccc', borderRadius: '8px', marginTop: '20px' }}>
              <h3>サモナー情報が見つかりました!</h3>
              <p><strong>名前:</strong> {summonerData.name}</p>
              <p><strong>レベル:</strong> {summonerData.summonerLevel} Lv.</p>
              <p><strong>ID (PUUID):</strong> {summonerData.puuid.substring(0, 30)}...</p>
              <p style={{ fontSize: '0.8em', color: '#888' }}>
                  *この情報(特にPUUID)を基に、次回以降もっとディープなデータを取得します。
              </p>
          </div>
      )}
    </div>
  );

【コード解説:UIの追加】

  • <form onSubmit>: EnterキーまたはボタンのクリックでhandleSearchSummonerが実行されます。
  • summonerData && (...): 検索結果のデータ(summonerData)が存在する場合のみ、その情報を画面に表示する条件付きレンダリングを行っています。
  • データ表示: 取得したsummonerData.namesummonerData.summonerLevelを画面に出力しています。

5. 実行と確認

ターミナルが2つ必要です。

  1. ターミナル1(Pythonサーバー): my-api-serverフォルダで**python app.py**を実行。
  2. ターミナル2(Reactクライアント): my-react-appフォルダでnpm run devを実行。

ブラウザで自分のサモナー名を入力して「検索開始!」を押してみましょう!

🎉 Pythonサーバーを経由してサモナー情報が表示されれば大成功です!


6. 完成版コード全体

ここまでの修正をすべて適用した、サーバー側のコード全体です。

my-api-server/app.py 全体コード

完成版 app.py 全体コード(クリックで展開)
# my-api-server/app.py

import os
import requests
from flask import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv

# .envファイルをロードし、APIキーを取得
load_dotenv() 
RIOT_API_KEY = os.getenv('RIOT_API_KEY') 

app = Flask(__name__)
CORS(app) # Reactからのアクセスを許可

# Riot APIのベースURL(日本サーバー)
RIOT_BASE_URL = 'https://jp1.api.riotgames.com/lol/summoner/v4/summoners/by-name/'

# サーバー起動確認用の簡単なルート
@app.route('/')
def hello():
    return 'Flask Server is running!'

# サモナー情報を取得するAPI (GET /api/lol/summoner)
@app.route('/api/lol/summoner', methods=['GET'])
def get_summoner_data():
    summoner_name = request.args.get('name') 
    
    if not summoner_name:
        return jsonify({"message": "サモナー名を入力してください。"}), 400

    try:
        url = f"{RIOT_BASE_URL}{summoner_name}"
        
        # Riot APIにリクエストを送信
        api_response = requests.get(
            url,
            headers={
                'X-Riot-Token': RIOT_API_KEY # ヘッダーにAPIキーを設定
            }
        )

        # Riot APIからの応答をチェック
        if api_response.status_code == 200:
            return jsonify(api_response.json())
        else:
            # Riot APIがエラーを返した場合
            return jsonify(api_response.json()), api_response.status_code

    except Exception as e:
        print(f"Riot API呼び出しエラー: {e}")
        return jsonify({"message": "サーバー内部エラーが発生しました。"}), 500


if __name__ == '__main__':
    # 開発サーバーをポート5000で実行
    app.run(port=5000, debug=True)

my-react-app/src/App.jsx 全体コード

完成版 App.jsx 全体コード(クリックで展開)
// src/App.jsx
import React, { useState } from 'react';
import './App.css'; 

// PythonサーバーのベースURL (5000番ポート)
const PYTHON_API_BASE_URL = 'http://localhost:5000'; 

function App() {
  // LOL検索機能のためのステート
  const [searchSummonerName, setSearchSummonerName] = useState(''); // フォーム入力値
  const [summonerData, setSummonerData] = useState(null); // 検索結果データ
  const [searchError, setSearchError] = useState(null); // 検索エラーメッセージ
  
  // LOL検索ロジック
  const handleSearchSummoner = async (e) => {
    e.preventDefault(); 
    if (!searchSummonerName.trim()) return;

    setSearchError(null);
    setSummonerData(null);
    
    try {
      // PythonサーバーのLOLエンドポイントを呼び出し
      const response = await fetch(
        `${PYTHON_API_BASE_URL}/api/lol/summoner?name=${searchSummonerName}`
      );

      if (!response.ok) {
        // サーバーからのエラー(404など)を処理
        const errorDetail = await response.json();
        setSearchError(`検索失敗: ${errorDetail.status?.message || errorDetail.message || '不明なエラー'}`);
        return;
      }

      const data = await response.json();
      setSummonerData(data); // データをステートに保存
      
    } catch (err) {
      setSearchError('API通信中にエラーが発生しました。');
      console.error(err);
    }
  };


  return (
    <div className="App">
      <h1>LoL Data App Builder</h1>
      
      <hr style={{ marginTop: '40px', borderColor: '#4a90e2' }} />

      <h2>🎮 LoL サモナー検索</h2>

      {/* 検索フォーム - onSubmitでhandleSearchSummonerを呼び出す */}
      <form className="task-form" onSubmit={handleSearchSummoner}>
          <input
            type="text"
            value={searchSummonerName}
            onChange={(e) => setSearchSummonerName(e.target.value)}
            placeholder="サモナー名を入力..."
            aria-label="サモナー名"
            style={{ flexGrow: 1 }}
          />
          <button type="submit">検索開始!</button>
      </form>

      {/* 検索結果の表示 */}
      {searchError && <p className="error">{searchError}</p>}

      {summonerData && (
          <div style={{ padding: '15px', border: '1px solid #ccc', borderRadius: '8px', marginTop: '20px' }}>
              <h3>サモナー情報が見つかりました!</h3>
              <p><strong>名前:</strong> {summonerData.name}</p>
              <p><strong>レベル:</strong> {summonerData.summonerLevel} Lv.</p>
              <p><strong>ID (PUUID):</strong> {summonerData.puuid.substring(0, 30)}...</p>
              <p style={{ fontSize: '0.8em', color: '#888' }}>
                  *この情報(特にPUUID)を基に、次回以降もっとディープなデータを取得します。
              </p>
          </div>
      )}
    </div>
  );
}

export default App;


7. まとめと次のステップ

今回は、Python(Flask)への移行を成功させ、LoLアプリ開発の基盤を磐石にしました。

  • 言語移行: Node.jsからPython/Flaskへサーバーを切り替えました。
  • API連携: requestsを使ってRiot APIからサモナーの基本情報を取得できました。
  • 認証: .envpython-dotenvでAPIキーを安全に管理できました。

次回は、今回取得したサモナーID(PUUID)を使って、さらにディープなデータ、例えばランク情報特定チャンピオンの熟練度を取得し、表示していくステップに進みます!

次回も頑張っていきましょう!

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?