みなさん、こんにちは!
今回からは、僕の最近は待っているゲーム「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キーを取得する。
- PythonとFlaskの環境を構築する。
- APIキーを
.envで安全に扱い、サモナー検索APIを実装する。 - ReactとPythonサーバーを連携させ、画面に基本情報を表示する。
1. 開発の準備:APIキーと環境構築
1.1. Riot Games APIキーの取得
LOLのデータにアクセスするには、Riot Games Developer PortalでAPIキーが必要です。
- Riot Games Developer Portalにアクセスし、ログインします。
- ログイン後、ページ上部の「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.pyのhello()関数の定義の下に、以下の新しい関数を追加してください。
# 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を呼び出す標準的な方法です。headersにX-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.nameやsummonerData.summonerLevelを画面に出力しています。
5. 実行と確認
ターミナルが2つ必要です。
-
ターミナル1(Pythonサーバー):
my-api-serverフォルダで**python app.py**を実行。 -
ターミナル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からサモナーの基本情報を取得できました。 -
認証:
.envとpython-dotenvでAPIキーを安全に管理できました。
次回は、今回取得したサモナーID(PUUID)を使って、さらにディープなデータ、例えばランク情報や特定チャンピオンの熟練度を取得し、表示していくステップに進みます!
次回も頑張っていきましょう!