この記事でできること:SteamのStorefront appreviews(事実上のAPI)を使って、レビュー本文とクエリサマリーを取得する方法をまとめます。カーソル(cursor)でのページング、language
/ filter
/ purchase_type
/ day_range
などのパラメータ活用、重複除去・CSV保存までをPython最小コードで解説します。
全体像は Steamガイド(入口ページ)、前提のAppIDやストア情報は Step1 / Step3 を参照ください。
appreviewsとは(要点)
- エンドポイント例:
https://store.steampowered.com/appreviews/<APPID>?json=1&language=japanese&filter=recent&num_per_page=100&cursor=*
- 本文レビュー(
review
)、評価(voted_up
)、作成/更新時刻、購入区分などが取得可能。 - クエリサマリー(
query_summary
)に件数・ポジ/ネガ比などの要約が入る。num_per_page=0
でサマリーだけ取得も可能。 - ページングはカーソル方式。初期値は
*
を渡し、返ってきたcursor
を繋いで次ページへ。 - 非公開仕様(ストア側の実装)です。将来の変更に備え、堅牢化と監視が前提。
主なパラメータ早見表
パラメータ | 主な値 | 既定 | 説明 |
---|---|---|---|
json | 1 | — | JSONで返す指定。必須。 |
language | japanese / english / all | english | 対象言語を絞る。all は全言語。 |
filter | recent / updated / all | recent | ソート傾向(最近投稿/最近更新/全体)。 |
review_type | all / positive / negative | all | レビューの極性で絞り込み。 |
purchase_type | all / steam / non_steam_purchase | all | 購入区分(Steam内/外)で絞る。 |
day_range | 例:30 , 90 | — | 直近N日分に限定(recent分析に便利)。 |
num_per_page | 1–100 | 20 前後 | 1ページあたりの件数。最大100。 |
cursor | * (初回)、以降レスポンスの値 | — | ページング用。毎回レスポンスのcursor を次リクエストへ。 |
注意:cursor
はURLエンコードが必要な場合がありますが、requests
のparams
に渡せば多くは自動対応されます(多重エンコードに注意)。
クイックスタート(Python最小サンプル)
ページングして最大N件を取得(日本語・最近・Steam購入に限定)
import time, requests
BASE = "https://store.steampowered.com/appreviews/{appid}"
def fetch_reviews(appid:int, max_reviews=300, language="japanese",
filter_="recent", purchase_type="steam", review_type="all",
num_per_page=100, sleep=0.6, day_range=None):
cursor = "*"
reviews, total = [], 0
while True:
params = {
"json": 1,
"language": language,
"filter": filter_,
"review_type": review_type,
"purchase_type": purchase_type,
"num_per_page": num_per_page,
"cursor": cursor
}
if day_range:
params["day_range"] = day_range
r = requests.get(BASE.format(appid=appid), params=params, timeout=30).json()
batch = r.get("reviews", [])
if not batch:
break
reviews.extend(batch)
total += len(batch)
cursor = r.get("cursor")
if not cursor or total >= max_reviews:
break
time.sleep(sleep) # マナー
return reviews
# 例:直近90日の日本語レビューを最大300件
rows = fetch_reviews(2420510, max_reviews=300, day_range=90)
print(len(rows), "reviews collected")
print(rows[0].keys())
サマリーだけ欲しい(件数・スコア等)
import requests
def fetch_summary(appid:int, language="all", day_range=None):
params = {"json": 1, "language": language, "num_per_page": 0}
if day_range: params["day_range"] = day_range
r = requests.get(f"https://store.steampowered.com/appreviews/{appid}", params=params, timeout=30).json()
return r.get("query_summary", {})
summary = fetch_summary(2420510, language="japanese", day_range=90)
print(summary) # total_positive / total_negative / review_score_desc など
CSV保存+重複除去(recommendationid)
import csv
def to_rows(reviews:list[dict]):
out = []
for r in reviews:
a = r.get("author", {}) or {}
out.append({
"recommendationid": r.get("recommendationid"),
"language": r.get("language"),
"review": (r.get("review") or "").replace("\\n"," ").strip(),
"voted_up": r.get("voted_up"),
"votes_up": r.get("votes_up"),
"votes_funny": r.get("votes_funny"),
"weighted_vote_score": r.get("weighted_vote_score"),
"comment_count": r.get("comment_count"),
"steam_purchase": r.get("steam_purchase"),
"received_for_free": r.get("received_for_free"),
"written_during_early_access": r.get("written_during_early_access"),
"timestamp_created": r.get("timestamp_created"),
"timestamp_updated": r.get("timestamp_updated"),
"author_steamid": a.get("steamid"),
"author_num_games_owned": a.get("num_games_owned"),
"author_num_reviews": a.get("num_reviews"),
"author_playtime_forever": a.get("playtime_forever"),
"author_playtime_last_two_weeks": a.get("playtime_last_two_weeks"),
})
# recommendationid で重複除去
uniq = {row["recommendationid"]: row for row in out if row.get("recommendationid")}
return list(uniq.values())
def save_csv(rows, path="reviews.csv"):
fields = list(rows[0].keys()) if rows else []
with open(path, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=fields)
w.writeheader()
for row in rows:
w.writerow(row)
rows = to_rows(fetch_reviews(2420510, max_reviews=300, day_range=90))
save_csv(rows, "reviews.csv")
print("saved reviews.csv:", len(rows))
レビュー項目の読み方(スキーマ抜粋)
キー | 意味 |
---|---|
review | 本文(改行含む)。 |
voted_up | ポジティブ(True)/ネガティブ(False)。 |
timestamp_created / timestamp_updated | 投稿/更新のUNIX時刻。 |
steam_purchase | Steamでの購入か。 |
received_for_free | 無料配布/提供の可能性。 |
written_during_early_access | EA期間の投稿か。 |
votes_up / votes_funny | 「参考になった」「おもしろい」投票数。 |
weighted_vote_score | 重み付きスコア(文字列)。解析時はfloat化。 |
developer_response | 開発元の返信(あれば)。 |
author.* | 投稿者のゲーム所持数/プレイ時間など。 |
recommendationid | レビュー固有ID。重複判定に利用。 |
query_summary.* | 返却トップにある集計(total_positive/negative 等)。 |
実務Tips:要約/NLPの前処理
- 期間固定:例)
day_range=90
で最近の声に限定。時期ブレを避ける。 - 言語固定:
language=japanese
で日本語のみ抽出。多言語混在は別記事で比較。 - 短文/定型の除外:本文長が極端に短いものを除外(例:10文字未満)。
- 重複除去:
recommendationid
でユニーク化。 - スコア別サンプリング:
voted_up
で正/負を分け、要約の偏りを低減。 - プレイ時間による重み:
author.playtime_forever
を重み付けに使う設計も有効。
可視化やCCUとの突き合わせは CCUの自前収集 と組み合わせると洞察が深まります。
堅牢化のコツ(非公開仕様ゆえ)
- スリープ&リトライ:短時間連打は避け、0.5–1.0秒を目安に間隔を確保。
- スキーマチェック:キー存在確認(
get
多用)で欠損に強く。 - フェイルセーフ:ページが空のときや
cursor
欠落時は打ち切り。 - ログ/監視:仕様変更を検知するため、件数やキー有無を定期ログに。
- 規約順守:本文の再配布・商用利用・スクレイピング方針に留意(引用は最小限に)。
次のステップ(関連How-to)
入口に戻る:Steamガイド / 前の記事:appdetailsの使い方 / さらに前:公式Web APIの使い方 / 最初:AppIDの見つけ方
免責とポリシー:appreviews
は公式ドキュメント外のストアエンドポイントです。挙動は予告なく変わる可能性があります。利用規約・法令・引用ルールを遵守の上でご利用ください。