この記事では、第三者サービスである SteamSpy のAPIを使って、Steamゲームの推定オーナー数、タグ、CCU指標などをPythonで取得する方法を解説します。
Steamレビュー分析では、レビュー本文やストア情報だけでなく、「そのゲームがどの程度の規模なのか」「どのようなタグで見られているのか」を補助的に把握したい場面があります。SteamSpyを使うと、推定オーナー数やタグ投票数などを取得できる場合があります。
ただし、SteamSpyはSteam公式APIではありません。環境や時期によって取得できない場合があるため、本記事では 任意の補助データ として扱い、取得できない場合は appdetails や appreviews の情報で代替する方針にしています。
この記事でできること
- SteamSpyで取得できる主な情報を理解する
- 推定オーナー数を取得する
- タグ情報を取得して上位タグを確認する
- SteamSpyのCCU指標を取得する
- 複数タイトルのSteamSpy情報をDataFrame化する
- 取得できない場合に落ちない実装を作る
appdetailsやappreviewsを使った代替方法を理解する
想定読者
- Steamゲームの規模感を補助指標として見たい方
- 推定オーナー数やタグ情報をPythonで取得したい方
- レビュー分析対象の優先度を決めたい方
- Steamレビュー分析にストア情報以外の補助データを加えたい方
- 取得失敗を前提に安全なコードを書きたい方
SteamSpyとは
SteamSpyは、Steamゲームに関する推定データを提供している第三者サービスです。API経由で、推定オーナー数、タグ、レビュー集計、CCU指標などを取得できる場合があります。
代表的なAPI URLは以下です。
https://steamspy.com/api.php?request=appdetails&appid=730ただし、SteamSpyはSteam公式のWeb APIではありません。アクセス制限、仕様変更、Cloudflare等によるブロック、対象タイトルの未収録などにより、取得できない場合があります。
そのため、本記事ではSteamSpyを必須データ源ではなく、取得できた場合に使う補助データとして扱います。
事前準備
この記事では、SteamのAppIDが分かっている前提で進めます。AppIDの確認方法は以下の記事で解説しています。
SteamのAppIDを確認する方法|URL・検索・API・Python一括取得
また、SteamSpyが取得できない場合の代替情報として、以下の記事で扱う appdetails と appreviews を使います。
SteamSpyで取得できる主な項目
SteamSpyの appdetails で取得できる主な項目は以下です。
| 項目 | 意味 | 見方 |
|---|---|---|
name | タイトル名 | 表記ゆれがある場合があります |
developer | 開発元 | ストア情報と異なる場合があります |
publisher | 販売元 | 表記ゆれに注意します |
owners | 推定オーナー数 | 200000 .. 500000 のような範囲で返る場合があります |
ccu | CCU系指標 | 公式APIの現在値とは意味が異なる可能性があります |
positive | 好評数 | レビュー規模の参考になります |
negative | 不評数 | レビュー規模の参考になります |
userscore | ユーザースコア | 補助的な評価指標として扱います |
average_forever | 平均プレイ時間 | 分単位で返る場合があります |
median_forever | 中央値プレイ時間 | やり込み度の参考になります |
tags | タグと投票数 | ゲーム特徴の補助指標として使えます |
これらは推定値や第三者サービス上の集計値です。厳密な実数として扱うのではなく、規模感や比較の補助として使うのが安全です。
使用ライブラリ
HTTPリクエストには requests、表形式の整形には pandas を使います。
pip install -U requests pandasまず取得できるか確認する
SteamSpyは環境によって取得できない場合があります。まず、ブラウザで以下のURLを開き、JSONが表示されるか確認します。
https://steamspy.com/api.php?request=appdetails&appid=730JSONが表示されれば取得できる可能性があります。一方で、HTMLページやエラー画面が表示される場合は、Pythonからも取得できない可能性があります。
取得できない場合でも、Steamレビュー分析自体は可能です。価格・ジャンルは appdetails、レビュー件数や好評率は appreviews、現在の同時接続数は公式Web APIで代替できます。
SteamSpyから1タイトルの情報を取得する
まず、AppIDを1つ指定してSteamSpyの appdetails を取得します。取得できない場合は空の辞書を返すようにします。
import time
import requests
def fetch_steamspy_appdetails(
appid: int,
sleep_sec: float = 1.0,
) -> dict:
"""
SteamSpy APIから1タイトルの詳細情報を取得する。
取得できない場合は空dictを返す。
"""
url = "https://steamspy.com/api.php"
headers = {
"User-Agent": "Mozilla/5.0",
"Referer": "https://steamspy.com/",
}
params = {
"request": "appdetails",
"appid": appid,
}
try:
response = requests.get(
url,
params=params,
headers=headers,
timeout=30,
)
content_type = response.headers.get("Content-Type", "").lower()
if not response.ok:
return {}
if "json" not in content_type:
return {}
data = response.json()
if not data or not isinstance(data, dict):
return {}
return data
except Exception:
return {}
finally:
time.sleep(sleep_sec)
appid = 730
data = fetch_steamspy_appdetails(appid)
print(data.keys() if data else "SteamSpyから取得できませんでした。")このコードでは、取得に失敗してもエラーで停止しないようにしています。SteamSpyは任意のデータ源として扱うため、取得できない場合はスキップする方針が安全です。
推定オーナー数を扱いやすく変換する
SteamSpyの owners は、200000 .. 500000 のような範囲文字列で返る場合があります。分析では、下限・上限・中央値に分解しておくと扱いやすくなります。
def parse_owners_range(owners: str) -> dict:
"""
SteamSpyのowners範囲文字列を下限・上限・中央値に変換する。
例: "200000 .. 500000"
"""
if not owners or not isinstance(owners, str):
return {
"owners_min": None,
"owners_max": None,
"owners_mid": None,
}
text = owners.replace(",", "").replace(" ", "")
try:
low, high = text.split("..")
low = int(low)
high = int(high)
mid = (low + high) // 2
except Exception:
return {
"owners_min": None,
"owners_max": None,
"owners_mid": None,
}
return {
"owners_min": low,
"owners_max": high,
"owners_mid": mid,
}
owners_info = parse_owners_range(data.get("owners") if data else None)
print(owners_info)owners_mid はあくまで範囲の中央値です。実際の所有者数そのものではないため、厳密な数値としてではなく、規模感の比較に使います。
必要な項目だけを取り出す
SteamSpyのレスポンスから、分析で使いやすい項目だけを取り出します。
def extract_steamspy_fields(
appid: int,
data: dict,
) -> dict:
"""
SteamSpyのレスポンスから主要項目を取り出す。
"""
if not data:
return {
"appid": appid,
"steamspy_available": False,
}
owners_info = parse_owners_range(data.get("owners"))
tags = data.get("tags") or {}
sorted_tags = sorted(
tags.items(),
key=lambda item: item[1],
reverse=True,
)
return {
"appid": appid,
"steamspy_available": True,
"name": data.get("name"),
"developer": data.get("developer"),
"publisher": data.get("publisher"),
"owners_raw": data.get("owners"),
"owners_min": owners_info["owners_min"],
"owners_max": owners_info["owners_max"],
"owners_mid": owners_info["owners_mid"],
"ccu": data.get("ccu"),
"positive": data.get("positive"),
"negative": data.get("negative"),
"userscore": data.get("userscore"),
"average_forever": data.get("average_forever"),
"median_forever": data.get("median_forever"),
"price": data.get("price"),
"initialprice": data.get("initialprice"),
"discount": data.get("discount"),
"tag_top1": sorted_tags[0][0] if len(sorted_tags) > 0 else None,
"tag_top2": sorted_tags[1][0] if len(sorted_tags) > 1 else None,
"tag_top3": sorted_tags[2][0] if len(sorted_tags) > 2 else None,
"tags": tags,
}
appid = 730
data = fetch_steamspy_appdetails(appid)
row = extract_steamspy_fields(appid, data)
print(row)上位タグや推定オーナー数を取り出しておくと、複数タイトルを比較するときに使いやすくなります。
複数タイトルを一括取得する
複数のAppIDについて、SteamSpyから取得できる分だけ取得します。取得できないタイトルは steamspy_available=False として残します。
import pandas as pd
def fetch_steamspy_many(
appids: list[int],
sleep_sec: float = 1.0,
) -> pd.DataFrame:
"""
複数AppIDのSteamSpy情報を取得する。
取得失敗しても処理を止めない。
"""
rows = []
for appid in appids:
data = fetch_steamspy_appdetails(
appid=appid,
sleep_sec=sleep_sec,
)
row = extract_steamspy_fields(
appid=appid,
data=data,
)
rows.append(row)
return pd.DataFrame(rows)
appids = [
570,
730,
1172470,
2246340,
]
df_steamspy = fetch_steamspy_many(appids)
print(df_steamspy[[
"appid",
"steamspy_available",
"name",
"owners_raw",
"owners_mid",
"ccu",
"tag_top1",
"tag_top2",
"tag_top3",
]])取得結果はCSVとして保存できます。
df_steamspy.to_csv(
"steamspy_appdetails.csv",
index=False,
encoding="utf-8-sig",
)SteamSpyは取得できない場合があるため、CSVには取得可否を示す steamspy_available を残しておくと後で確認しやすくなります。
タグ情報を縦持ちテーブルに変換する
SteamSpyの tags は辞書形式で返ります。タグ分析を行う場合は、縦持ちのテーブルに変換すると扱いやすくなります。
def steamspy_tags_to_dataframe(
appid: int,
data: dict,
) -> pd.DataFrame:
"""
SteamSpyのtags辞書を縦持ちDataFrameに変換する。
"""
if not data:
return pd.DataFrame()
tags = data.get("tags") or {}
rows = []
for tag, votes in tags.items():
rows.append({
"appid": appid,
"tag": tag,
"votes": votes,
})
return pd.DataFrame(rows)
tag_frames = []
for appid in appids:
data = fetch_steamspy_appdetails(appid)
df_tags = steamspy_tags_to_dataframe(appid, data)
if not df_tags.empty:
tag_frames.append(df_tags)
df_tags_all = (
pd.concat(tag_frames, ignore_index=True)
if tag_frames
else pd.DataFrame()
)
print(df_tags_all.head())縦持ちにしておくと、タグ別の投票数ランキングや、タイトル間のタグ比較がしやすくなります。
タグ投票数を集計する
複数タイトルのタグをまとめて、投票数の多いタグを確認します。
if not df_tags_all.empty:
tag_summary = (
df_tags_all
.groupby("tag", as_index=False)
.agg(
title_count=("appid", "nunique"),
votes_sum=("votes", "sum"),
votes_mean=("votes", "mean"),
)
.sort_values("votes_sum", ascending=False)
)
print(tag_summary.head(20))votes_sum が大きいタグは、対象タイトル群で多く付与されているタグです。ただし、対象にしたタイトル数やタイトルの知名度に大きく影響されるため、結果は比較対象の範囲内で解釈します。
取得できない場合のフォールバック
SteamSpyが取得できない場合でも、Steamレビュー分析に必要な情報の多くは他の方法で補えます。
| 見たい情報 | SteamSpy | 代替手段 |
|---|---|---|
| タイトル名・発売日・価格 | name、price | appdetails |
| ジャンル・カテゴリ | tags | appdetails の genres / categories |
| レビュー件数・好評率 | positive、negative | appreviews の query_summary |
| 現在の同時接続数 | ccu | 公式Web APIの GetNumberOfCurrentPlayers |
| 同時接続数の時系列 | 取得できる場合でも限定的 | Steam同時接続数を自前収集して可視化する方法 |
特に、レビュー分析に必要な最低限の情報は appdetails、appreviews、公式Web APIでかなり補えます。SteamSpyは「取得できたら使う補助情報」として扱うのがおすすめです。
フォールバック付きで基本情報を取得する
ここでは、SteamSpyが取得できた場合はSteamSpyの推定オーナー数やタグを使い、取得できない場合は appdetails と appreviews の情報で最低限の表を作る例を紹介します。
def fetch_store_appdetails(
appid: int,
cc: str = "jp",
lang: str = "japanese",
) -> dict:
"""
Steam Storefront appdetails から基本情報を取得する。
"""
url = "https://store.steampowered.com/api/appdetails"
params = {
"appids": appid,
"cc": cc,
"l": lang,
}
try:
response = requests.get(
url,
params=params,
timeout=30,
)
response.raise_for_status()
data = response.json()
node = data.get(str(appid), {})
if not node.get("success"):
return {}
return node.get("data", {})
except Exception:
return {}
def fetch_appreviews_summary(
appid: int,
) -> dict:
"""
Steam appreviews からレビューサマリーを取得する。
"""
url = f"https://store.steampowered.com/appreviews/{appid}"
params = {
"json": 1,
"language": "all",
"purchase_type": "all",
"num_per_page": 0,
}
try:
response = requests.get(
url,
params=params,
timeout=30,
)
response.raise_for_status()
data = response.json()
return data.get("query_summary", {})
except Exception:
return {}次に、SteamSpy、appdetails、appreviews をまとめて取得します。
def fetch_game_overview_with_fallback(
appid: int,
) -> dict:
"""
SteamSpyを優先し、取得できない場合もappdetails/appreviewsで基本情報を補う。
"""
spy = fetch_steamspy_appdetails(appid)
store = fetch_store_appdetails(appid)
reviews = fetch_appreviews_summary(appid)
spy_row = extract_steamspy_fields(appid, spy)
genres = [
item.get("description")
for item in store.get("genres", [])
if item.get("description")
]
categories = [
item.get("description")
for item in store.get("categories", [])
if item.get("description")
]
return {
"appid": appid,
"name": spy_row.get("name") or store.get("name"),
"steamspy_available": spy_row.get("steamspy_available", False),
"owners_raw": spy_row.get("owners_raw"),
"owners_mid": spy_row.get("owners_mid"),
"steamspy_ccu": spy_row.get("ccu"),
"tag_top1": spy_row.get("tag_top1"),
"tag_top2": spy_row.get("tag_top2"),
"tag_top3": spy_row.get("tag_top3"),
"type": store.get("type"),
"release_date": (store.get("release_date") or {}).get("date"),
"is_free": store.get("is_free"),
"genres": " / ".join(genres[:3]),
"categories": " / ".join(categories[:3]),
"total_reviews": reviews.get("total_reviews"),
"total_positive": reviews.get("total_positive"),
"total_negative": reviews.get("total_negative"),
"review_score_desc": reviews.get("review_score_desc"),
}
overview = fetch_game_overview_with_fallback(730)
print(overview)このようにしておくと、SteamSpyが取れなかった場合でも、記事や比較表に必要な最低限の情報を作れます。
複数タイトルをフォールバック付きで取得する
複数タイトルの比較表を作る場合は、以下のようにループします。
def fetch_many_game_overviews(
appids: list[int],
sleep_sec: float = 1.0,
) -> pd.DataFrame:
"""
複数タイトルの概要情報をフォールバック付きで取得する。
"""
rows = []
for appid in appids:
row = fetch_game_overview_with_fallback(appid)
rows.append(row)
time.sleep(sleep_sec)
return pd.DataFrame(rows)
df_overview = fetch_many_game_overviews(appids)
print(df_overview.head())
df_overview.to_csv(
"steam_game_overview_with_fallback.csv",
index=False,
encoding="utf-8-sig",
)steamspy_available を確認すれば、SteamSpy由来の情報が入っているタイトルと、フォールバック情報だけで構成されたタイトルを区別できます。
SteamSpy情報を見るときの注意点
SteamSpyの情報を使うときは、以下の点に注意してください。
- Steam公式データではありません
第三者サービスの推定値として扱います。 - 取得できない場合があります
アクセス制限、Cloudflare、未収録、仕様変更などにより取得できないことがあります。 - 推定オーナー数は範囲で返る場合があります
中央値は便宜的な近似であり、実数ではありません。 - タグ投票数は対象タイトルの知名度に影響されます
タグの強さを比較するときは、タイトル規模の違いを考慮します。 - CCU指標は公式APIの現在値とは意味が異なる可能性があります
現在の同時接続数を見たい場合は、公式Web APIのGetNumberOfCurrentPlayersを使います。 - 短時間に大量アクセスしない
取得できる場合でも、間隔を空けて必要な範囲だけ取得します。 - 分析の主軸にしすぎない
レビュー本文、ストア情報、公式APIの同時接続数などと組み合わせて補助的に使います。
レビュー分析での活用例
SteamSpyの情報は、レビュー分析の補助指標として使えます。
| SteamSpy情報 | 活用例 |
|---|---|
| 推定オーナー数 | レビュー分析対象の規模感を把握する |
| タグ | ゲーム特徴やジャンル傾向を補助的に確認する |
| positive / negative | レビュー規模や評価傾向の参考にする |
| average_forever / median_forever | プレイ時間の長さから、やり込み度を推測する |
| ccu | 人気の補助指標として見る。ただし公式APIの現在値とは分けて扱う |
たとえば、レビュー数が少なくても推定オーナー数が多いタイトルでは、レビュー投稿率が低い可能性があります。逆に、タグ投票数が特定ジャンルに偏っているタイトルでは、そのジャンル要素がレビュー本文にも多く出ているか確認できます。
SteamSpyを使わない場合の代替方針
SteamSpyが取得できない場合でも、Steamレビュー分析は問題なく進められます。代替方針は以下です。
- ゲーム基本情報は Steam appdetails から取得する
- レビュー件数・好評率は Steam appreviews の
query_summaryを使う - 現在の同時接続数は Steam公式Web API の
GetNumberOfCurrentPlayersを使う - 同時接続数の推移は Steam同時接続数を自前収集して可視化する方法 で記録する
- タグの代替として
appdetailsのgenresとcategoriesを使う
取得できない外部サービスに依存しすぎないように、SteamSpyが取得できなくても分析が成立する構成にしておくことが重要です。SteamSpyは便利ですが、レビュー本文やストア情報、公式Web APIの情報を主軸にし、補助データとして使うのがおすすめです。
次に読む記事
- Steam appreviewsの使い方|レビュー本文・評価サマリーをPythonで取得
- Steam同時接続数を自前収集して可視化する方法|CCUをPythonで定期取得
- Steam appdetailsの使い方|価格・発売日・対応OS・ジャンルをPythonで取得
- Steam公式Web APIの使い方|アプリ一覧・同時接続・実績・ニュースをPythonで取得
- SteamのAppIDを確認する方法|URL・検索・API・Python一括取得
- Steamの情報を取得する方法まとめ|公式API・Storefront・SteamSpyの使い分け
まとめ
この記事では、SteamSpy APIを使って、推定オーナー数、タグ、CCU指標などをPythonで取得する方法を紹介しました。
SteamSpyは、ゲームの規模感やタグ傾向を補助的に見るうえで便利ですが、Steam公式データではなく、取得できない場合もあります。そのため、取得失敗を前提にした実装にし、appdetails、appreviews、公式Web APIへフォールバックできるようにしておくことが重要です。
Steamレビュー分析では、SteamSpyを主軸にするのではなく、レビュー本文、ストア情報、公式APIの同時接続数と組み合わせて、補助指標として活用するのがおすすめです。