Steam データ活用

SteamSpyの使い方【推定オーナー数・タグ集計・CCU指標】Python最小サンプル

この記事でできること:
サードパーティの SteamSpy API から 推定オーナー数(owners)タグ投票(tags) を取得し、規模感の把握ジャンル傾向の可視化分析の優先度付けに活用する方法をまとめます。
併せて、日次スナップショット保存 → 時系列化の実務テンプレも配布します。

全体像は Steamガイド(入口ページ) を、AppIDの取得は Step1 を参照してください。

SteamSpyとは(要点)

  • Steamユーザープロファイルの標本から 統計的に推定 したメトリクスを提供する外部サービス。
  • 代表的なエンドポイント:request=appdetails&appid=<APPID>request=all(全体一覧)など。
  • 推定値である点に注意(厳密値ではない)。指標は規模感の目安として使い、意思決定の“第一段”に。
  • 同じアプリでも時期により推定が揺れるため、日次で保存して推移を持つ運用が実務的。

取得できる主な項目(抜粋)

キー意味メモ
name, developer, publisher基本属性表記揺れあり
owners推定オーナー数(例:200000..500000範囲表記。中央値に寄せて数値化する例を後述
ccu前日ピークCCUの推定瞬間ではなく“日次ピーク”系
positive, negative, userscore, score_rankレビュー集計系指標“傾向”の参考に
average_forever, median_forever累計平均/中央値プレイ時間(分)やり込み度の目安
price, initialprice, discount価格・割引時点のスナップショット
languages, genre言語・ジャンル文字列
tagsタグ名→投票数(辞書)人気度/特徴量として有用

クイックスタート:Python最小サンプル(requests)

AppID一件の詳細(ownersとtagsを抽出)

import requests

def steamspy_appdetails(appid:int):
    url = "https://steamspy.com/api.php"
    r = requests.get(url, params={"request":"appdetails", "appid": appid}, timeout=30)
    r.raise_for_status()
    return r.json()

def owners_to_midpoint(owners_range:str) -> int | None:
    # "200000..500000" → 350000 のように中央値に寄せる
    try:
        lo, hi = owners_range.split("..")
        return (int(lo) + int(hi)) // 2
    except Exception:
        return None

data = steamspy_appdetails(730)  # 例:CS2
owners_mid = owners_to_midpoint(data.get("owners",""))
top_tags = sorted((data.get("tags") or {}).items(), key=lambda x: -x[1])[:5]
print("owners(mid):", owners_mid)
print("top tags:", top_tags)

公式ラッパを使う(steamspypi

# pip install steamspypi
import steamspypi

def steamspy_appdetails_pi(appid:int):
    req = {"request":"appdetails", "appid": appid}
    return steamspypi.download(req)

print(steamspy_appdetails_pi(730)["name"])

複数タイトルを一括取得 → DataFrame化

owners中央値と上位タグを列にまとめる

import time, pandas as pd, requests

def fetch_many(appids:list[int], sleep=0.3):
    rows = []
    for aid in appids:
        try:
            d = requests.get("https://steamspy.com/api.php",
                             params={"request":"appdetails","appid":aid},
                             timeout=30).json()
            owners_mid = owners_to_midpoint(d.get("owners",""))
            tags = sorted((d.get("tags") or {}).items(), key=lambda x: -x[1])
            row = {
                "appid": aid,
                "name": d.get("name"),
                "owners_mid": owners_mid,
                "ccu_peak_yday": d.get("ccu"),
                "userscore": d.get("userscore"),
                "price": d.get("price"),
                "discount": d.get("discount"),
                "tag_top1": tags[0][0] if len(tags)>0 else None,
                "tag_top2": tags[1][0] if len(tags)>1 else None,
                "tag_top3": tags[2][0] if len(tags)>2 else None,
            }
            rows.append(row)
        except Exception:
            pass
        time.sleep(sleep)  # マナー
    return pd.DataFrame(rows)

df = fetch_many([570, 730, 1172470])
print(df)

日次スナップショットで“推移化”する(実務テンプレ)

毎日1回:owners中央値・前日ピークCCUなどをCSVに追記

"""
config:
  APPIDS = 例: [570, 730, 1172470]
  実行頻度 = 1日1回(cron/タスクスケジューラ)
"""
import csv, time, requests, datetime as dt
from pathlib import Path

APPIDS = [570, 730, 1172470]
OUT = Path("steamspy_daily.csv")

def today_utc():
    return dt.datetime.utcnow().date().isoformat()

def fetch_row(appid:int):
    d = requests.get("https://steamspy.com/api.php",
                     params={"request":"appdetails","appid":appid},
                     timeout=30).json()
    return {
        "date_utc": today_utc(),
        "appid": appid,
        "name": d.get("name"),
        "owners_mid": owners_to_midpoint(d.get("owners","")),
        "owners_raw": d.get("owners"),
        "ccu_peak_yday": d.get("ccu"),
        "userscore": d.get("userscore"),
        "price": d.get("price"),
        "discount": d.get("discount")
    }

def append_daily():
    is_new = not OUT.exists()
    with OUT.open("a", newline="", encoding="utf-8") as f:
        fields = ["date_utc","appid","name","owners_mid","owners_raw","ccu_peak_yday","userscore","price","discount"]
        w = csv.DictWriter(f, fieldnames=fields)
        if is_new: w.writeheader()
        for aid in APPIDS:
            row = fetch_row(aid)
            w.writerow(row)
            time.sleep(0.4)
    print("appended:", OUT)

append_daily()

これで owners/ccuの“時系列” が作れます。CCUの“瞬間値”は公式APIで収集するため、Step5 と合わせて設計してください。

タグ人気の読み方と活用

tags は「タグ名 → 投票数」の辞書です。人気の定義は複数あります:

  • 票数ベース:投票数の多いタグほど“強く結びつく特徴”。
  • 規模ウェイトowners_mid と組み合わせ、票数 × owners_mid規模×紐づきの強さを近似。
  • 勢いCCU時系列 と組み合わせ、「最近CCUの高いタイトルに多いタグ」を抽出。

単純な「タグ×規模ウェイト」ランキング例(擬似)

import pandas as pd

# df_apps: fetch_many() の結果に tags 辞書も含めてある前提(適宜改修)
def tag_weight_table(appids:list[int]):
    rows = []
    for aid in appids:
        d = steamspy_appdetails(aid)
        owners_mid = owners_to_midpoint(d.get("owners",""))
        for tag, votes in (d.get("tags") or {}).items():
            rows.append({"appid": aid, "tag": tag, "votes": votes, "owners_mid": owners_mid,
                         "weight": (owners_mid or 0) * votes})
    return (pd.DataFrame(rows)
              .groupby("tag", as_index=False)
              .agg(votes_sum=("votes","sum"), owners_sum=("owners_mid","sum"), weight_sum=("weight","sum"))
              .sort_values("weight_sum", ascending=False))

tbl = tag_weight_table([570, 730, 1172470])
print(tbl.head(10))

まずは 票数ベースの集計から始め、必要に応じて 規模ウェイト直近CCU を掛け合わせると“今強いタグ”が見えてきます。

注意点・ベストプラクティス(重要)

  • 推定である前提:owners/ccuを厳密値として扱わない。順位・規模感・優先度付けの材料に。
  • 時系列の自前保有:APIはスナップショット中心。日次保存で推移を作るのが実務。
  • 欠損・外れ値:推定が飛ぶ日がある。移動中央値/外れ値除外で可視化の安定度を高める。
  • レート&マナー:間隔をあけ、失敗時はリトライ。大規模バッチはスロット分割。
  • 解釈の整合:価格やタグの“表記”は appdetails 側と突き合わせて正規化。

ユースケースと導線

  • 市場規模のあたり付け:ownersでTAM感を掴む → 深掘りは レビュー要約CCU時系列
  • タグ別の勢い:票数×規模×直近CCUで“今熱いタグ”を特定 → 記事企画に。
  • 比較テーブル:複数タイトルで owners/ccu/userscore/価格 を横並びに。

この先に進む(関連How-to)

  1. 【How-to】レビュー本文の取得と要約(appreviews)
  2. 【How-to】同時接続の自前収集と可視化(時系列)
  3. 【How-to】価格・タグ・発売情報を取る(appdetails)

入口に戻る:Steamガイド / これまで:Step1Step2Step3Step4Step5

免責とポリシー:
SteamSpyは第三者サービスの推定値を提供します。挙動や仕様は予告なく変わる可能性があります。利用規約・法令・引用ルールを遵守の上でご利用ください。

-Steam, データ活用
-, , , , , ,