11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Steam Web APIでデータ収集

Last updated at Posted at 2021-03-21

PCゲームのダウンロード兼販売プラットフォームであるSteamでは
Web APIが公開されており、ユーザー情報やサーバー情報など様々なデータを取得することができる。

Web APIの使用方法を学ぶついでに、データ分析の練習に使用するデータを集めることにした。
Steam Web APIの機能は、PyPIにてpython用のラッパーがいくつか公開されているが、今回は自分で機能を実装してみる。

準備するもの

  • steamアカウント(必須)

https://store.steampowered.com/login/

  • apiキー(必須)

steamアカウントを使って発行。Web API利用時に必要。

http://steamcommunity.com/dev/apikey

api_key = "XXXXX"
  • steamID(任意)

自分のでも良いし他人のでも構わない。
ただしプロフィール公開度が「公開」になっているアカウントでないとデータが取得できない。

steamidfinderで適当にアカウントを検索するのも手。

uid = "XXXXX"

ユーザー情報取得関数の作成

IPlayerService インターフェースを実装する。

ドキュメントを参考に、型となるurlを作成し、json形式で受け取るよう指定。
受け取ったデータをデコードして、"response"キーに対応した値を取得。

import json
import requests

def GetOwnedGames(api_key, uid):
    url = "http://api.steampowered.com/IPlayerService/GetOwnedGames/v1/?key={}&steamid={}&format=json".format(
        api_key, uid
    )
    r = requests.get(url)
    data = json.loads(r.text)
    return data["response"]
ownd_games = GetOwnedGames(api_key, uid)
print(ownd_games["game_count"]) #所持ゲーム数
print(ownd_games["games"]) # 所持ゲームの詳細(appid, プレイ時間等)

その他の機能もurlと引数を変えるだけで関数化できる。

数が多いので他の関数の実装方法は省略(jupyter notebook)。

使用例

steamレベルが非常に高いことで有名な、St4ck氏のデータを取得してみた(2021/03/21)

Steamにて、大金をかけアカウントを「レベル4000」に到達させたユーザー現る。カタールの王族であるとの噂
(補足)Steamレベルについて

Steamレベルとは、Steamプロフィールの右上に表示されている数字である。レベルを上げる手順を、まずざっくりと説明しよう。Steamにてトレーディングカード対応のゲームを一定時間プレイすることで、カードをドロップ。ドロップだけではカードはコンプリートできないので、カードを購入してタイトルごとのカードを揃え、バッジが完成する。バッジを完成させれば経験値がたまっていき、指定の経験値をためるとレベルがアップするという流れだ。(https://automaton-media.com/articles/newsjp/20190118-83483/)

uid = 76561198023414915

所有しているゲーム

ownd_games = GetOwnedGames(api_key, uid)
ownd_games["game_count"]

4728

所有ゲーム数 4728

ownd_games["games"][:5]

[{'appid': 10,
  'playtime_forever': 0,
  'playtime_windows_forever': 0,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0},
 {'appid': 80,
  'playtime_forever': 0,
  'playtime_windows_forever': 0,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0},
 {'appid': 100,
  'playtime_forever': 0,
  'playtime_windows_forever': 0,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0},
 {'appid': 300,
  'playtime_forever': 0,
  'playtime_windows_forever': 0,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0},
 {'appid': 20,
  'playtime_forever': 0,
  'playtime_windows_forever': 0,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0}]

所有しているゲームのIDとプレイ時間を取得

# 最近遊んだゲーム数を表示
recently_played_games["total_count"]
3

最近遊んだゲーム

# 最近遊んだゲーム
recently_played_games["games"][:3]

[{'appid': 433850,
  'name': 'Z1 Battle Royale',
  'playtime_2weeks': 4871,
  'playtime_forever': 131291,
  'img_icon_url': 'aee7491abfd812e2fbb4ec3326ad5f4b85c8137a',
  'img_logo_url': 'b83cffe1839f3ecaec18754844b21bda9b397659',
  'playtime_windows_forever': 4965,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0},
 {'appid': 730,
  'name': 'Counter-Strike: Global Offensive',
  'playtime_2weeks': 49,
  'playtime_forever': 414870,
  'img_icon_url': '69f7ebe2735c366c65c0b33dae00e12dc40edbe4',
  'img_logo_url': 'd0595ff02f5c79fd19b06f4d6165c3fda2372820',
  'playtime_windows_forever': 1200,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0},
 {'appid': 1238860,
  'name': 'Battlefield 4™ ',
  'playtime_2weeks': 31,
  'playtime_forever': 13969,
  'img_icon_url': 'fe0bd4bd2adde978ca411a4b2725c1b6d72717ef',
  'img_logo_url': '6f4f3bf49342fcf38f9e7c63b9d9148b42c53b0b',
  'playtime_windows_forever': 13969,
  'playtime_mac_forever': 0,
  'playtime_linux_forever': 0}]

最近(おそらく2週間以内)遊んだゲームが表示される。こちらはゲーム名も明記されるらしい

Steamレベル

# レベルの表示
steam_level = GetSteamLevel(api_key, uid)
steam_level
5000

ニュース記事では4000超だったのが5000に増えていた。

バッジ

# バッジ情報
badges["badges"][:3]

[{'badgeid': 48,
  'level': 1070,
  'completion_time': 1607333397,
  'xp': 2070,
  'scarcity': 4795},
 {'badgeid': 49,
  'level': 1,
  'completion_time': 1616276919,
  'xp': 10,
  'scarcity': 3201589},
 {'badgeid': 13,
  'level': 4608,
  'completion_time': 1615953180,
  'xp': 4858,
  'scarcity': 17877}]
# 総経験値
badges["player_xp"]
125265813

基本的にバッジ1つ作成で100 xpであることを考えると恐ろしい経験値。

コミュニティバッジ進捗

# コミュニティバッジ進捗
community_Badge_Progress
[{'questid': 115, 'completed': True},
 {'questid': 128, 'completed': True},
 {'questid': 134, 'completed': True},
 {'questid': 133, 'completed': True},
 {'questid': 132, 'completed': True},
 {'questid': 108, 'completed': True},
 {'questid': 113, 'completed': True},
 {'questid': 112, 'completed': True},
 {'questid': 104, 'completed': True},
 {'questid': 105, 'completed': True},
 {'questid': 106, 'completed': False},
 {'questid': 114, 'completed': True},
 {'questid': 119, 'completed': True},
 {'questid': 121, 'completed': True},
 {'questid': 110, 'completed': True},
 {'questid': 111, 'completed': True},
 {'questid': 126, 'completed': True},
 {'questid': 101, 'completed': True},
 {'questid': 103, 'completed': True},
 {'questid': 118, 'completed': True},
 {'questid': 117, 'completed': True},
 {'questid': 109, 'completed': True},
 {'questid': 124, 'completed': True},
 {'questid': 127, 'completed': True},
 {'questid': 125, 'completed': True},
 {'questid': 123, 'completed': True},
 {'questid': 120, 'completed': True},
 {'questid': 122, 'completed': True}]

コミュニティバッジ入手のために課されたクエストの達成状況

上から順に

    "Steamガードを有効化",
    "携帯番号の追加",
    "Steamモバイルアプリを利用",
    "ブロードキャストを表示",
    "ストアディスカバリーキューを使用",
    "ウィッシュリストにゲームを追加",
    "フレンドリストにフレンドを追加",
    "ゲームをプレイ",
    "ゲームのレビューを書く",
    "スクリーンショットを投稿",
    "ムービーを投稿",
    "ワークショップのアイテムを評価",
    "ワークショップのアイテムをサブスクライブ",
    "Steamオーバーレイからガイドを表示",
    "コミュニティプロフィールにアバターを設定",
    "コミュニティプロフィールに本名を設定",
    "プロフィールの背景を設定",
    "グループに参加",
    "フレンドのプロフィールにコメント",
    "アクティビティフィード内のコンテンツを評価",
    "フレンドに向けてコメントを投稿",
    "フレンドのスクリーンショットにコメント",
    "ゲームバッジを作成",
    "プロフィール上にバッジを張り付け",
    "チャット内で絵文字を使用",
    "掲示板を検索",
    "コミュニテイマーケットを利用",
    "トレードを利用",

11番目のクエストである動画投稿は、まだやったことがないらしい。

上記のユーザー情報は、基本的にSteamのプロフィールページからも閲覧可能なものばかりだが、Web APIを使用すれば高速かつ大量のデータを得ることができる。
(細かな違いとしては、OS別でのゲームプレイ時間の閲覧や、携帯番号を追加しているかの確認はプロフィールページ上では不可能だった。APIを利用した方がより多くの情報を得られる様子)

データ収集

steamidは17桁。いくつかのsteamアカウントをhttps://steamidfinder.com/ で適当に検索すると、
初めの"76561198"は全員同じだった。

for i in tqdm(range(5)):
    # 76561198000000000 ~ 76561198999999999が格納されたジェネレータから候補ID抽出
    random_numbers = (i for i in range(76561198000000000, 76561199000000000))
    ids = random.sample(list(random_numbers), 100)
    result = GetPlayerSummaries(api_key, ids)
    print(len(result))
# 54, 65, 62, 71, 59。期待値62.2

# IDの先頭8桁目を変えてみる
for i in tqdm(range(5)):
    # 76561197000000000 ~ 76561197999999999が格納されたジェネレータから候補ID抽出
    random_numbers = (i for i in range(76561197000000000, 76561198000000000))
    ids = random.sample(list(random_numbers), 100)
    result = GetPlayerSummaries(api_key, ids)
    print(len(result))
# 3,2,1,2,2 ほとんどヒットしない

for i in tqdm(range(5)):
    # 76561199000000000 ~ 76561199999999999が格納されたジェネレータから候補ID抽出
    random_numbers = (i for i in range(76561199000000000, 76561200000000000))
    ids = random.sample(list(random_numbers), 100)
    result = GetPlayerSummaries(api_key, ids)
    print(len(result))
# 19, 14, 12, 18, 28 微妙。

この部分をsteamidの識別子として利用していると仮定して、残り9桁を乱数で生成。
デタラメなidから実在するアカウントを1万件探して情報を取得。

標本は10000件程度欲しいので、多めに見積もって20000個乱数を生成する。想定通りなら12000件ぐらい取れるはず。


# ID候補が入ったジェネレータ作成
random_numbers = (i for i in range(76561198000000000, 76561199000000000))
# 2万件抽出
ids = random.sample(list(random_numbers), 20000)
# 100件ずつしかリクエストできないので分割
ids_list = list(np.array_split(ids, 200))
# 出力ファイル初期化
pd.DataFrame(
    {
        "steamid": [],
        "personaname": [],
        "timecreated": [],
    }
).to_csv("user_info.csv")

for i in tqdm(ids_list):
    # リクエスト用のID文字列作成
    ids_str = ",".join([str(j) for j in i])
    # ユーザー情報取得
    result = GetPlayerSummaries(api_key, ids_str)
    # ID, name, createtimeを取得して辞書化しリストに格納
    user_info_list = []
    for i in result:
        user_info = {}
        user_info["steamid"] = i["steamid"]
        user_info["personaname"] = i["personaname"]
        # 作成日がないユーザーに対しては例外処理
        try:
            user_info["timecreated"] = i["timecreated"]
        except KeyError:
            user_info["timecreated"] = ""
        user_info_list.append(user_info)

    # 出力
    # 大体60件ずつ追加されていることを確認したいのでindexは残す
    pd.DataFrame(user_info_list).to_csv("user_id.csv", mode="a", header=False)

確認すると11508件入手できた。
入手したIDを使ってユーザー情報を取得。1件あたり1~2秒程度かかる。

# ユーザー情報を取得
user_info_list = []
steam_ids = df["steamid"]
for id in tqdm(steam_ids):
    user_info = {}
    user_info["steamid"] = id

    # 情報が取得できないユーザーがいたので例外処理
    # プロフィールを非表示設定にしている場合は取得できない?

    # 所持ゲーム
    ownd_games = GetOwnedGames(api_key, id)
    try:
        user_info["game_count"] = ownd_games["game_count"]
        user_info["games"] = ownd_games["games"]
    except KeyError:
        user_info["game_count"] = None
        user_info["games"] = None

    # バッジ情報
    badges = GetBadges(api_key, id)
    try:
        user_info["badges"] = badges["badges"]
        user_info["player_xp"] = badges["player_xp"]
        user_info["player_level"] = badges["player_level"]
        # コミュニティバッジ進捗
        community_Badge_Progress = GetCommunityBadgeProgress(api_key, id, 2)
        user_info["cleard_quests"] = len(
            [i for i in community_Badge_Progress if i["completed"] == True]
        )
        user_info["quests"] = community_Badge_Progress
    except KeyError:
        user_info["badges"] = None
        user_info["player_xp"] = None
        user_info["player_level"] = None
        user_info["cleard_quests"] = None
        user_info["quests"] = None

    user_info_list.append(user_info)
    # ウェイト時間を設けておく
    time.sleep(0.5)

# DF化→jsonで出力
badge_df = pd.DataFrame(user_info_list)
badge_df.to_json("user_info.json")

次回は、作成したjsonファイルを使ってデータを分析してみる予定。

作成したスクリプトは以下のページで閲覧できます。

https://github.com/Chronona/FetchSteamUserInfo/blob/main/fetch_user_info.ipynb

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?