タイトル画像
琴葉茜

さて、今回から「PokeAPI」を使ってポケモンの情報が表示されるようにしてみようか!

琴葉葵

ところで、PokeAPIはどんな感じのデータが取れるの??

琴葉茜

例えば「ピカチュウ」はIDが「25」だから、「https://pokeapi.co/api/v2/pokemon/25」で中身を見ることができるよ

琴葉葵

うおぅ、文字だらけ...!!

琴葉茜

PokeAPIみたいに「Web API」はすごい量のJSONなことが多いから、分析していくのは苦労しそうでしょ
だから今回は、その中の一部だけをかい摘んで使っていくね

琴葉葵

了解!!まずはスクレイピングの時みたいに「requests」を使うんかな??

琴葉茜

そうだね!
じゃあ次のプログラムを追加して、内容を表示してみよう

import requests # 追加を忘れずに!!

# 途中は省略

if search_btn:
    poke_id = None # 追加
    poke_name = None # 追加

    if user_input in NAME_TO_ID:
        poke_name = user_input # 入力内容を名前にする
        poke_id = NAME_TO_ID[user_input]
    elif user_input in ID_TO_NAME:
        poke_name = ID_TO_NAME[user_input]
        poke_id = user_input # 入力内容をIDにする
    # この条件部分を追加
    if poke_id is not None: # IDを取得しなかった場合にエラーが出ないようにする 
        api_url = f"https://pokeapi.co/api/v2/pokemon/{poke_id}"
        res = requests.get(api_url)
        st.write(res)
    else:
        st.error("その名前やIDのポケモンは見つかりませんでした。")
解説画像1
琴葉葵

あ!なんか「<Response [200]>」って出てきた!
さっきブラウザで見た時みたいに大量に文字が出てくるのかと思った

琴葉茜

これは、指定したURLでwebサイトへリクエストを送った時に情報が正しく返ってきたっていう合図だよ!もしちゃんと返ってこない場合は「404」とか「500」って返ってくるんだ

琴葉葵

そういえば、たまにホームページを開いた時に「404エラー」みたいなのが表示される時があるから、それと一緒かな

琴葉茜

そうだね!だから今回の処理を作る時は、返ってきた情報が「200」だった時に処理をするって流れになるよ

琴葉茜

「requests」で受け取った情報は「json」ライブラリで扱えるから、まずは「たかさ」と「おもさ」を取り出してみよう!

import json # 追加を忘れずに!!

# 途中は省略

if poke_id is not None:
    api_url = f"https://pokeapi.co/api/v2/pokemon/{poke_id}"
    res = requests.get(api_url)
    # ここから追加
    if res.status_code == 200:
        data = res.json()
        height = data["height"] / 10
        weight = data["weight"] / 10
        st.write(f"たかさ: {height} m")
        st.write(f"おもさ: {weight} kg")
解説画像2
琴葉葵

「たかさ」と「おもさ」が表示されてるね!
でも、両方とも10で割らないとけないのは手間だね

琴葉茜

それは海外の人が作ってて、そこの国の基準になってるからだね
web APIを扱う時はよくあることだからデータはしっかり観察しようね

琴葉葵

はーい!

琴葉茜

じゃあ次は「タイプ」と「能力(種族値)」を表示しようか
だけどその前に、得られる情報は英語表記だから日本語に変換出来る辞書型の定数を用意しよう

# タイプと能力値の日本語辞書
TYPES = {
    "normal": "ノーマル", "fire": "ほのお", "water": "みず", "electric": "でんき",
    "grass": "くさ", "ice": "こおり", "fighting": "かくとう", "poison": "どく",
    "ground": "じめん", "flying": "ひこう", "psychic": "エスパー", "bug": "むし",
    "rock": "いわ", "ghost": "ゴースト", "dragon": "ドラゴン", "dark": "あく",
    "steel": "はがね", "fairy": "フェアリー"
}

STATS = {
    "hp": "HP", "attack": "こうげき", "defense": "ぼうぎょ",
    "special-attack": "とくこう", "special-defense": "とくぼう",
    "speed": "すばやさ"
}
琴葉葵

これはimportしているプログラムのすぐ下くらいに配置したら良いよね!

琴葉茜

どこに何を配置したら良いかイメージが出来てきてるね
じゃあ、その調子で表示内容を追加してみようか!

if res.status_code == 200:
    data = res.json()
    height = data["height"] / 10
    weight = data["weight"] / 10

    # タイプを日本語に変換
    types = [TYPES[t["type"]["name"]] for t in data["types"]]

    # 種族値を日本語に変換
    stats = {STATS[s["stat"]["name"]]: s["base_stat"] for s in data["stats"]}


    st.header(f"{poke_name}")
    st.write(f"ID: {poke_id}")
    st.write(f"タイプ: {', '.join(types)}")
    st.write(f"たかさ: {height} m")
    st.write(f"おもさ: {weight} kg")
    st.subheader("種族値")
    for stat_name, stat_val in stats.items():
        st.write(f"- {stat_name}: {stat_val}")
琴葉葵

ポケモンのタイプは「types」、種族値は「stats」で受け取れるんだね!
この2つは複数の情報を持ってるから、内包表記で1つずつ日本語に変換してるってことか

琴葉茜

そういうことだね!プログラムを更新した後に検索して結果を見てみると...

解説画像3
琴葉葵

おおー!バッチリだね!!だけど、文字だけだと図鑑って感じはしないなぁ

琴葉茜

じゃあ次の項目で、より図鑑っぽくするために画像や鳴き声を追加しよう!

この時点での全体のプログラム

import streamlit as st
import json
import requests

# タイプと能力値の日本語辞書
TYPES = {
    "normal": "ノーマル", "fire": "ほのお", "water": "みず", "electric": "でんき",
    "grass": "くさ", "ice": "こおり", "fighting": "かくとう", "poison": "どく",
    "ground": "じめん", "flying": "ひこう", "psychic": "エスパー", "bug": "むし",
    "rock": "いわ", "ghost": "ゴースト", "dragon": "ドラゴン", "dark": "あく",
    "steel": "はがね", "fairy": "フェアリー"
}

STATS = {
    "hp": "HP", "attack": "こうげき", "defense": "ぼうぎょ",
    "special-attack": "とくこう", "special-defense": "とくぼう",
    "speed": "すばやさ"
}

# タイトル・説明
st.title("ポケモン図鑑")
st.write("これから段階的に機能を増やしていこう!")

# JSONファイルを読み込み(日本語名 → ID)
with open("poke_dict.json", "r", encoding="utf-8") as f:
    NAME_TO_ID = json.load(f)

# ID → 日本語名 の逆引き辞書も作成
ID_TO_NAME = {str(v): k for k, v in NAME_TO_ID.items()}

# 入力とボタン
user_input = st.text_input("ポケモンの名前またはIDを入力してください")
search_btn = st.button("検索")

# 検索処理
if search_btn:
    poke_id = None
    poke_name = None

    if user_input in NAME_TO_ID:
        poke_id = NAME_TO_ID[user_input]
        poke_name = user_input
    elif user_input in ID_TO_NAME:
        poke_id = int(user_input)
        poke_name = ID_TO_NAME[user_input]

    if poke_id is not None:
        api_url = f"https://pokeapi.co/api/v2/pokemon/{poke_id}"
        res = requests.get(api_url)
        
        if res.status_code == 200:
            data = res.json()
            height = data["height"] / 10
            weight = data["weight"] / 10

            # タイプを日本語に変換
            types = [TYPES[t["type"]["name"]] for t in data["types"]]

            # 種族値を日本語に変換
            stats = {STATS[s["stat"]["name"]]: s["base_stat"] for s in data["stats"]}

            st.header(f"{poke_name}")
            st.write(f"ID: {poke_id}")
            st.write(f"タイプ: {', '.join(types)}")
            st.write(f"たかさ: {height} m")
            st.write(f"おもさ: {weight} kg")
            st.subheader("種族値")
            for stat_name, stat_val in stats.items():
                st.write(f"- {stat_name}: {stat_val}")
    else:
        st.error("その名前やIDのポケモンは見つかりませんでした。")