この記事では、Steam Storefrontの appreviews を使って、Steamレビュー本文と評価サマリーをPythonで取得する方法を解説します。
Steamレビュー分析では、レビュー本文、好評・不評、投稿日時、プレイ時間、購入区分などを取得できると、評価傾向や不満点、アップデート後の反応を分析しやすくなります。
本記事では、レビューサマリーの取得、本文レビューの取得、cursor を使ったページング、重複除去、CSV保存までを扱います。
この記事でできること
- Steamの
appreviewsで取得できる情報を理解する - レビュー件数や好評・不評のサマリーを取得する
- レビュー本文、投稿日時、プレイ時間、購入区分を取得する
cursorを使って複数ページのレビューを取得する- 日本語レビューや直近レビューに絞って取得する
- レビューをDataFrame化してCSV保存する
- レビュー分析で使うときの注意点を理解する
想定読者
- SteamレビューをPythonで取得したい方
- Steamレビュー分析記事の元データを作りたい方
- 好評・不評レビューを分けてテキスト分析したい方
- レビュー件数や評価傾向を時系列で見たい方
- Steamのレビュー本文をCSVとして保存したい方
appreviewsとは
appreviews は、Steamストア上のユーザーレビューをJSON形式で取得できるエンドポイントです。AppIDを指定すると、対象タイトルのレビュー本文や評価情報を取得できます。
https://store.steampowered.com/appreviews/2246340?json=1主に以下の情報を取得できます。
| 情報 | 主なフィールド | 内容 |
|---|---|---|
| レビュー本文 | review | ユーザーが投稿したレビュー本文 |
| 好評・不評 | voted_up | True が好評、False が不評 |
| 投稿日時 | timestamp_created | レビュー投稿日時のUNIX時刻 |
| 更新日時 | timestamp_updated | レビュー更新日時のUNIX時刻 |
| 購入区分 | steam_purchase | Steamで購入したかどうか |
| 無料提供 | received_for_free | 無料提供を受けたかどうか |
| 早期アクセス | written_during_early_access | 早期アクセス期間中の投稿かどうか |
| 投稿者情報 | author | プレイ時間、所持ゲーム数、投稿レビュー数など |
| レビューID | recommendationid | レビュー固有ID。重複除去に使える |
| 評価サマリー | query_summary | レビュー件数や好評・不評件数など |
ただし、appreviews はSteam公式Web APIとして細かく保証されたエンドポイントではなく、Steamストア側のJSONレスポンスを利用する形式です。将来的に仕様が変わる可能性があるため、例外処理や欠損処理を入れて使うのがおすすめです。
事前準備
この記事では、SteamのAppIDが分かっている前提で進めます。AppIDの確認方法は以下の記事で解説しています。
SteamのAppIDを確認する方法|URL・検索・API・Python一括取得
また、価格や発売日などのストア情報を取得したい場合は、以下の記事も参考になります。
Steam appdetailsの使い方|価格・発売日・対応OS・ジャンルをPythonで取得
使用ライブラリ
HTTPリクエストには requests、表形式の整形には pandas を使います。
pip install -U requests pandas主なパラメータ
appreviews では、言語、並び順、レビュー種別、購入区分、取得件数などをパラメータで指定できます。
| パラメータ | 主な値 | 説明 |
|---|---|---|
json | 1 | JSON形式で返す指定 |
language | japanese、english、all | レビュー言語 |
filter | recent、updated、all | 取得するレビューの並び・範囲 |
review_type | all、positive、negative | 好評・不評の絞り込み |
purchase_type | all、steam、non_steam_purchase | 購入区分の絞り込み |
num_per_page | 1〜100 | 1回のリクエストで取得する件数 |
cursor | *、またはレスポンスの cursor | ページング用カーソル |
day_range | 30、90など | 直近N日分に絞り込む場合に使う |
cursor はページングに使います。初回は * を指定し、2ページ目以降はレスポンスに含まれる cursor を次のリクエストに渡します。
レビューサマリーだけを取得する
まず、レビュー本文ではなく、件数や評価サマリーだけを取得します。num_per_page=0 にすると、本文レビューを取得せずに query_summary を確認できます。
import requests
def fetch_review_summary(
appid: int,
language: str = "all",
review_type: str = "all",
purchase_type: str = "all",
day_range: int | None = None,
) -> dict:
"""
Steam appreviews からレビューサマリーを取得する。
"""
url = f"https://store.steampowered.com/appreviews/{appid}"
params = {
"json": 1,
"language": language,
"review_type": review_type,
"purchase_type": purchase_type,
"num_per_page": 0,
}
if day_range is not None:
params["day_range"] = day_range
response = requests.get(
url,
params=params,
timeout=30,
)
response.raise_for_status()
data = response.json()
return data.get("query_summary", {})
appid = 2246340
summary = fetch_review_summary(
appid=appid,
language="japanese",
day_range=90,
)
print(summary)出力には、レビュー件数、好評数、不評数、レビュー評価の説明などが含まれます。まず対象タイトルにレビューが十分あるか確認したい場合に便利です。
1ページ分のレビュー本文を取得する
次に、レビュー本文を1ページ分だけ取得します。
def fetch_review_page(
appid: int,
cursor: str = "*",
language: str = "japanese",
filter_: str = "recent",
review_type: str = "all",
purchase_type: str = "all",
num_per_page: int = 100,
day_range: int | None = None,
) -> dict:
"""
Steam appreviews から1ページ分のレビューを取得する。
"""
url = f"https://store.steampowered.com/appreviews/{appid}"
params = {
"json": 1,
"cursor": cursor,
"language": language,
"filter": filter_,
"review_type": review_type,
"purchase_type": purchase_type,
"num_per_page": num_per_page,
}
if day_range is not None:
params["day_range"] = day_range
response = requests.get(
url,
params=params,
timeout=30,
)
response.raise_for_status()
return response.json()
appid = 2246340
data = fetch_review_page(
appid=appid,
language="japanese",
num_per_page=10,
)
reviews = data.get("reviews", [])
print("取得件数:", len(reviews))
print("次のcursor:", data.get("cursor"))
if reviews:
print(reviews[0].keys())
print(reviews[0].get("review")[:100])レスポンスには、レビュー一覧と次ページ取得用の cursor が含まれます。
cursorで複数ページを取得する
大量のレビューを取得する場合は、cursor を使ってページングします。初回は *、次回以降はレスポンスの cursor を指定します。
import time
def fetch_reviews(
appid: int,
max_reviews: int = 500,
language: str = "japanese",
filter_: str = "recent",
review_type: str = "all",
purchase_type: str = "all",
num_per_page: int = 100,
day_range: int | None = None,
sleep_sec: float = 0.7,
) -> list[dict]:
"""
cursorを使って複数ページのレビューを取得する。
"""
cursor = "*"
all_reviews = []
seen_ids = set()
while len(all_reviews) < max_reviews:
data = fetch_review_page(
appid=appid,
cursor=cursor,
language=language,
filter_=filter_,
review_type=review_type,
purchase_type=purchase_type,
num_per_page=num_per_page,
day_range=day_range,
)
reviews = data.get("reviews", [])
if not reviews:
break
for review in reviews:
recommendationid = review.get("recommendationid")
if recommendationid in seen_ids:
continue
seen_ids.add(recommendationid)
all_reviews.append(review)
if len(all_reviews) >= max_reviews:
break
next_cursor = data.get("cursor")
if not next_cursor or next_cursor == cursor:
break
cursor = next_cursor
time.sleep(sleep_sec)
return all_reviews
appid = 2246340
reviews = fetch_reviews(
appid=appid,
max_reviews=300,
language="japanese",
day_range=90,
)
print("取得レビュー数:", len(reviews))この関数では、recommendationid を使って重複除去しています。また、短時間に連続アクセスしないように sleep_sec で待機時間を入れています。
レビューをDataFrameに変換する
取得したレビューはネストしたJSONになっているため、分析しやすいようにDataFrameへ変換します。
import pandas as pd
def reviews_to_dataframe(reviews: list[dict]) -> pd.DataFrame:
"""
appreviewsのレビュー一覧をDataFrameに変換する。
"""
rows = []
for review in reviews:
author = review.get("author", {}) or {}
rows.append({
"recommendationid": review.get("recommendationid"),
"language": review.get("language"),
"review": (review.get("review") or "").replace("\n", " ").strip(),
"voted_up": review.get("voted_up"),
"votes_up": review.get("votes_up"),
"votes_funny": review.get("votes_funny"),
"weighted_vote_score": review.get("weighted_vote_score"),
"comment_count": review.get("comment_count"),
"steam_purchase": review.get("steam_purchase"),
"received_for_free": review.get("received_for_free"),
"written_during_early_access": review.get("written_during_early_access"),
"timestamp_created": review.get("timestamp_created"),
"timestamp_updated": review.get("timestamp_updated"),
"author_steamid": author.get("steamid"),
"author_num_games_owned": author.get("num_games_owned"),
"author_num_reviews": author.get("num_reviews"),
"author_playtime_forever": author.get("playtime_forever"),
"author_playtime_last_two_weeks": author.get("playtime_last_two_weeks"),
"author_playtime_at_review": author.get("playtime_at_review"),
})
df = pd.DataFrame(rows)
if not df.empty:
df["created_at"] = pd.to_datetime(
df["timestamp_created"],
unit="s",
utc=True,
errors="coerce",
)
df["updated_at"] = pd.to_datetime(
df["timestamp_updated"],
unit="s",
utc=True,
errors="coerce",
)
for col in [
"author_playtime_forever",
"author_playtime_last_two_weeks",
"author_playtime_at_review",
]:
if col in df.columns:
df[col.replace("author_", "") + "_hours"] = df[col] / 60
return df
df_reviews = reviews_to_dataframe(reviews)
print(df_reviews.head())
print(df_reviews.shape)プレイ時間は分単位で返るため、分析では時間単位に変換しておくと見やすくなります。
CSVに保存する
DataFrameに変換したレビューはCSVとして保存できます。
appid = 2246340
output_path = f"steam_reviews_{appid}.csv"
df_reviews.to_csv(
output_path,
index=False,
encoding="utf-8-sig",
)
print("保存しました:", output_path)
print("件数:", len(df_reviews))utf-8-sig で保存すると、Excelで開いたときに日本語が文字化けしにくくなります。
好評・不評を分けて取得する
review_type を指定すると、好評レビューと不評レビューを分けて取得できます。
positive_reviews = fetch_reviews(
appid=2246340,
max_reviews=200,
language="japanese",
review_type="positive",
day_range=365,
)
negative_reviews = fetch_reviews(
appid=2246340,
max_reviews=200,
language="japanese",
review_type="negative",
day_range=365,
)
df_positive = reviews_to_dataframe(positive_reviews)
df_negative = reviews_to_dataframe(negative_reviews)
print("好評:", len(df_positive))
print("不評:", len(df_negative))好評・不評を分けて取得すると、「魅力として語られている要素」と「不満点として語られている要素」を比較しやすくなります。
期間を指定して取得する
day_range を指定すると、直近N日分のレビューに絞り込めます。アップデート後の反応や直近の不満点を見たい場合に便利です。
recent_reviews = fetch_reviews(
appid=2246340,
max_reviews=500,
language="japanese",
filter_="recent",
day_range=90,
)
df_recent = reviews_to_dataframe(recent_reviews)
print("直近90日のレビュー数:", len(df_recent))レビュー分析記事では、全期間の傾向と直近期間の傾向を分けて見ると、評価変化を読み取りやすくなります。
購入区分を指定する
purchase_type を指定すると、Steamで購入したユーザーのレビューや、Steam外購入のレビューを分けて取得できます。
steam_purchase_reviews = fetch_reviews(
appid=2246340,
max_reviews=300,
language="japanese",
purchase_type="steam",
)
df_steam_purchase = reviews_to_dataframe(steam_purchase_reviews)
print("Steam購入レビュー:", len(df_steam_purchase))通常のレビュー分析では purchase_type="all" で全体を見ても問題ありませんが、購入経路による違いを見たい場合は絞り込みが有効です。
取得したレビューの基本集計
取得したレビューを簡単に集計してみます。
summary_table = df_reviews.agg(
review_count=("recommendationid", "count"),
)
positive_rate = df_reviews["voted_up"].mean()
print("レビュー件数:", len(df_reviews))
print("好評率:", round(positive_rate * 100, 1), "%")
if "playtime_at_review_hours" in df_reviews.columns:
print("レビュー時点のプレイ時間中央値:", df_reviews["playtime_at_review_hours"].median())voted_up の平均を取ると、取得した範囲内の好評率を計算できます。ただし、これは取得条件に依存するため、Steamストア上の総合評価と完全に一致するとは限りません。
レビュー項目の読み方
分析でよく使う項目は以下です。
| 列名 | 意味 | 分析での使い方 |
|---|---|---|
review | レビュー本文 | テキスト分析、要約、トピック抽出 |
voted_up | 好評・不評 | 高評価/低評価レビューの分類 |
created_at | 投稿日時 | 月次・日次のレビュー件数推移 |
steam_purchase | Steam購入か | 購入経路による傾向比較 |
received_for_free | 無料提供か | レビュー解釈時の補助情報 |
written_during_early_access | 早期アクセス中の投稿か | 正式リリース前後の比較 |
votes_up | 参考になった票数 | 代表レビュー抽出の補助 |
weighted_vote_score | 重み付き投票スコア | レビューの注目度の参考 |
playtime_at_review_hours | レビュー時点のプレイ時間 | 短時間レビュー・長時間レビューの比較 |
playtime_forever_hours | 累計プレイ時間 | やり込み層のレビュー傾向を見る |
レビュー取得時の注意点
appreviews を使うときは、以下の点に注意してください。
- 公式Web APIとして細かく保証された仕様ではない
将来的にレスポンス形式や取得仕様が変わる可能性があります。 - 取得条件で結果が変わる
language、filter、review_type、purchase_type、day_rangeによって件数や内容が変わります。 - cursorはそのままparamsに渡す
手動で多重エンコードすると取得に失敗する場合があります。 - 短時間に大量リクエストしない
ページング取得ではsleepを入れて間隔を空けます。 - レビュー本文の扱いに注意する
本文の長文転載や過度な再配布は避け、分析・引用は必要最小限にします。 - 取得した好評率は取得条件に依存する
Steamストア上の表示評価と完全に一致するとは限りません。 - 個人ユーザーの特定につながる扱いは避ける
分析では個別ユーザーではなく、レビュー全体の傾向を見ることを基本にします。
レビュー分析での活用例
取得したSteamレビューは、以下のような分析に活用できます。
- 月次・日次のレビュー件数推移を見る
- 高評価レビューで多い魅力要素を抽出する
- 低評価レビューで多い不満点を抽出する
- アップデート後にレビュー傾向が変わったか確認する
- プレイ時間別にレビュー傾向を比較する
- 日本語レビューと日本語以外レビューを比較する
- レビュー本文からトピック抽出や感情分析を行う
たとえば、低評価レビューだけを抽出して頻出語やトピックを確認すると、ユーザーが不満に感じている要素を整理しやすくなります。
次に読む記事
- Steam同時接続数を自前収集して可視化する方法|CCUをPythonで定期取得
- SteamSpyの使い方|推定オーナー数・タグ・CCU指標をPythonで取得
- Steam appdetailsの使い方|価格・発売日・対応OS・ジャンルをPythonで取得
- Steam公式Web APIの使い方|アプリ一覧・同時接続・実績・ニュースをPythonで取得
- SteamのAppIDを確認する方法|URL・検索・API・Python一括取得
- Steamの情報を取得する方法まとめ|公式API・Storefront・SteamSpyの使い分け
まとめ
この記事では、Steam Storefrontの appreviews を使って、レビュー本文と評価サマリーをPythonで取得する方法を紹介しました。
appreviews では、レビュー本文、好評・不評、投稿日時、プレイ時間、購入区分などを取得できます。cursor を使ってページングすることで、複数ページのレビューを取得し、CSVとして保存できます。
Steamレビュー分析では、取得条件を明確にしたうえで、レビュー件数、好評率、本文、プレイ時間、時期を組み合わせて見ることが重要です。次は、同時接続数を自前で継続収集し、レビュー傾向と合わせて確認する方法を扱います。