29
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?

PokeAPI × Amazon Comprehendでポケモン図鑑説明文を感情分析、ネガティブスコアTOP3

Last updated at Posted at 2025-12-15

はじめに

この記事では、初代151匹のポケモンを対象に、PokeAPI から図鑑説明文を取得し、
Amazon Comprehend を使って感情分析を行いました。
その結果から「図鑑説明文のネガティブスコアが高いポケモン TOP3」を紹介します。

Amazon Comprehend について

Amazon Comprehend は、機械学習 (ML) を用いてテキストからインサイトを引き出す自然言語処理 (NLP) サービスです。Amazon Comprehend は、カスタムエンティティ認識、カスタム分類、キーフレーズ抽出、感情分析、エンティティ認識などの API を提供しており、NLP をアプリケーションに簡単に統合できます。

以下のように与えたテキストからネガティブスコアを返してくれます。

PokeAPIとは

PokeAPIは簡単に言うとポケモンのデータを取得することができるAPIです。
https://pokeapi.co/

全国図鑑No.1であるフシギダネというポケモンの図鑑説明文を取得したい場合、下記のようにリクエストを送ります。

https://pokeapi.co/api/v2/pokemon-species/1

そのレスポンスから「ポケットモンスターX版の日本語テキスト」を取得したい場合は、flavor_text_entries からversion.name == "x" かつ language.name == "ja"flavor_textを抽出します。

flavor_text_entries: [
    {
        flavor_text: "生まれたときから 背中に
        不思議な タネが 植えてあって
        体と ともに 育つという。",
        language: {
            name: "ja",
            url: "https://pokeapi.co/api/v2/language/11/"
        },
        version: {
            name: "x",
            url: "https://pokeapi.co/api/v2/version/23/"
        }
    }
]

処理の流れ

Untitled Diagram (4).jpg
ロールを設定するだけで簡単に Amazon Comprehend と接続できたので、Lambda を使用して分析を行いました!
今回は日本語の図鑑説明文が充実していたため、ポケットモンスターX版の説明文を対象にしています。
Lambda の実行コードは以下となります。

Lambdaの実行コード(Python)
import json
import time
import urllib.request
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Comprehend クライアントを東京リージョンで作成
comprehend = boto3.client('comprehend', region_name='ap-northeast-1')

# PokeAPI のポケモン種データ取得用URL
POKE_URL = "https://pokeapi.co/api/v2/pokemon-species/{}"

# API呼び出し間隔(秒)
MIN_INTERVAL_SEC = 0.25
_last_request = 0.0

def _throttle():
    # API呼び出し間隔を一定以上空けるための処理
    global _last_request
    now = time.time()
    if now - _last_request < MIN_INTERVAL_SEC:
        time.sleep(MIN_INTERVAL_SEC - (now - _last_request))
    _last_request = time.time()

def fetch_species(i):
    _throttle()
    with urllib.request.urlopen(POKE_URL.format(i), timeout=10) as r:
        return json.load(r)

def get_japanese_name(spec):
    # ポケモンの日本語名(ja-Hrkt)を取得
    for n in spec.get("names", []):
        if n.get("language", {}).get("name") == "ja-Hrkt":
            return n.get("name")
    return spec.get("name") or "unknown"

def pick_version_text(spec, version):
    # 指定バージョンの日本語図鑑説明文(ja-Hrkt)を取得
    for e in spec.get("flavor_text_entries", []):
        if e.get("version", {}).get("name") == version and e.get("language", {}).get("name") == "ja-Hrkt":
            return e.get("flavor_text", "").replace("\n", " ")
    return None

def analyze_all(start_id=1, end_id=151, sleep_sec=MIN_INTERVAL_SEC, version="x"):
    #  指定範囲のポケモンを取得し、Comprehendで感情分析を実施
    results = []
    for i in range(start_id, end_id + 1):
        try:
            spec = fetch_species(i)
        except Exception as e:
            logger.debug(f"fetch failed id={i} err={e}")
            continue
        # 図鑑説明文を抽出
        text = pick_version_text(spec, version)
        if not text:
            logger.debug(f"no ja-Hrkt text for ver={version} id={i}")
            continue
        try:
            resp = comprehend.detect_sentiment(Text=text, LanguageCode='ja')
            neg = resp["SentimentScore"]["Negative"]
        except Exception as e:
            logger.debug(f"comprehend failed id={i} err={e}")
            continue
        results.append({
            "name_ja": get_japanese_name(spec),
            "negative": neg,
            "text": text
        })
        time.sleep(sleep_sec)
    # ネガティブスコアの降順でソート
    results.sort(key=lambda x: x["negative"], reverse=True)
    return results

def lambda_handler(event, context):
    params = event.get("queryStringParameters") or {}
    start_id = max(1, int(params.get("start_id", 1)))
    end_id = min(151, int(params.get("end_id", 151)))
    if end_id < start_id:
        return {"statusCode": 400, "body": json.dumps({"error": "invalid id range"})}
    version = params.get("version", "x")
    sleep_sec = float(params.get("sleep_sec", MIN_INTERVAL_SEC))

    results = analyze_all(start_id, end_id, sleep_sec, version)

    # Negative Top3 のみ返す
    top3 = results[:3]

    body = {
        "top3": top3,
        "total_analyzed": len(results),
    }
    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json; charset=utf-8"},
        "body": json.dumps(body, ensure_ascii=False)
    }

実行結果

ポケモン図鑑説明文ネガティブスコアランキング(ポケットモンスターX版)

順位 画像 ポケモン名 ネガティブスコア 図鑑説明文
1位 マルマイン.png マルマイン 0.9951 すこしの しげきに はんのうして ばくはつする。バクダンボールという あだなで こわがられて いる。
2位 ベロリンガ.png ベロリンガ 0.9877 ながい したは ねばねばした だえきで べっとり。どんなものでも くっついて とても べんり。
3位 コダック.png コダック 0.9856 いつも ずつうに なやまされている。 この ずつうが はげしくなると ふしぎな ちからを つかいはじめる。

おわりに

読んでいただきありがとうございました!
ポケモンにも「あだな」があることを知れて勉強になりました!

29
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
29
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?