はじめに
この記事では、初代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/"
}
}
]
処理の流れ

ロールを設定するだけで簡単に 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版)
おわりに
読んでいただきありがとうございました!
ポケモンにも「あだな」があることを知れて勉強になりました!


