GASを使ってゲームの戦績をスクレイピングした話

## 背景・目的
* 趣味の格闘ゲーム(ストリートファイターV)の戦績の推移を確認したい
* 公式戦績サイトでは今現在のポイントと直近に戦った対戦相手のデータしか確認できなくて困ってる

## 利用するもの
* Google Apps Script(GAS)
* スプレッドシート
* 対戦ゲームの公式戦績サイト
* 対戦ゲームのアカウント

それぞれの詳細は以下の通り。
### Google Apps Script(GAS)
Google Apps Script（通称GAS）とは、Googleが提供しているスクリプト言語のこと。
Googleのサービスと連携したり、タスクの自動化などができることが特徴。

### スプレッドシート
言わずと知れたGoogleが提供しているウェブベースの表計算ソフト。

### 対戦ゲームの公式戦績サイト
ストリートファイターVの戦績を確認できるWebサイト「シャドルー格闘家研究所」。
戦績を確認するには、ゲームに紐づいたPSNアカウントかSteamアカウントでのログインが必要。
今回はこのサイトからスクレイピングを行います。

### 対戦ゲームのアカウント
ゲームに紐づいたPSNアカウントかSteamアカウント。

## 作成する仕組みの全体図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/9be08c92-f219-e6ee-5907-18b75139af44.png)

①ユーザが手動で公式戦績サイトにアクセス
②公式戦績サイトからログイン時に発行されたCookieをメモ
③メモしたCookieをプログラムに入力して、プログラムから公式戦績サイトにアクセス
④公式戦績サイトからログイン時に発行されたCookieと戦績データを取得（Cookieは次回ログインに利用）
⑤スプレッドシートに戦績データとCookieを記録
⑥スプレッドシートで戦績データからグラフを作成


③~⑥を一定時間ごとに繰り返してスプレッドシート上の戦績データを更新する

## 実装
それでは、上記の仕組みを実際に実装する流れを見ていきます。
### スプレッドシートの作成
まずは、戦績データとCookieを記録するためのスプレッドシートを作成します。

1. Googleスプレッドシートにアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/e8c9e500-250e-da58-0bc2-7b39361d3d91.png)

2. 「＋」の形のアイコンを選択してスプレッドシートを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/973a2cfc-bd72-bb55-f306-1f032d41d5b7.png)


3. 「戦績シート」などの名前を付けて保存する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/14fda322-b5d6-2c67-feb0-0063704944a4.png)

4. 同様の手順で「Cookieシート」などの名前を付けて保存する
5. シートの作成は以上でとりあえず完了
### GASの作成
次に公式戦績サイトからスクレイピングする処理を担当するGASを作成します

1. Googleドライブにアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/846e8a5d-b031-d21e-4d0c-84513845675e.png)


2. 「新規」をクリックして、「Google Apps Script」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/e1efafd0-fbf7-5ba6-b995-1d2893965d5c.png)


3. スクリプトが作成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/de2e3fdc-d06f-a6f2-e2fe-29e8e81287e1.png)


4. ライブラリの「+」ボタンから利用する以下のライブラリIDを指定する
```
1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/26e3f85b-eac6-3f1e-e905-8fd399198f1b.png)

:::note info
このライブラリはGoogleが[公開](https://script.google.com/home/projects/1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw/edit)している公式のParserライブラリです。  
これを利用することでHTML形式のテキストデータから容易に任意の情報を抽出することができます。
:::

５. 「検索」>「追加」を選択してスクレイピング処理用のライブラリである「Parser」が追加されていることを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/bfa5c94b-6999-b8db-a494-6d0c0e36262e.png)


６. 以下のスクリプトを書き込む
```
function main() {
  //ログイン処理
  const login_url = 'https://game.capcom.com/cfn/sfv/profile/XXXXXX';

  //CookieシートからCookie取得
  const cookie_sheet = SpreadsheetApp.openById('XXXXXXXXXXXX');
  const cookie_sheet_obj = cookie_sheet.getSheetByName('シート1');
  const cookies = cookie_sheet_obj.getRange('A1').getValue();
  const headers = {Cookie:cookies};

  const get_options = {
    method:"get",
    headers:headers,
    followRedirects:true
  };

  //データ取得
  const response = UrlFetchApp.fetch(login_url,get_options);
  const content = response.getContentText('UTF-8');
  //Logger.log(content);

  //データ抽出
  let LP_text = Parser.data(content).from('<dt>League Points</dt>').to('</dd>').build();
  LP_text = Parser.data(LP_text).from('<dd>').to('<span>').build();
  Logger.log(LP_text);

  let recent_opp = Parser.data(content).from('<dl class="fId">').to('<div class="fighter">').build();
  let recent_opp_LP = Parser.data(recent_opp).from('<dt>League Points</dt>').to('/dd>').build();
  recent_opp_LP = Parser.data(recent_opp_LP).from('<dd>').to('<').build();

  let recent_opp_char = Parser.data(recent_opp).from('<dt>Fought Character</dt>').to('/dd>').build();
  recent_opp_char = Parser.data(recent_opp_char).from('<dd>').to('<').build();

  let recent_opp_id = Parser.data(recent_opp).from('ID</dt>').to('/dd>').build();
  recent_opp_id = Parser.data(recent_opp_id).from('<dd>').to('<').build();

  //今日の日付を取得
  date = Utilities.formatDate(new Date(),'JST','yyyy/MM/dd HH:mm:ss');

  //headerからcookieを取得
  let all_cookies = response.getAllHeaders()['Set-Cookie'];
  let next_scirid = Parser.data(all_cookies.filter(matchScirid)[0]).from("scirid=").to(';').build();
  let next_fuel_csrf_token = Parser.data(all_cookies.filter(matchFuelCsrfToken)[0]).from("fuel_csrf_token=").to(';').build();

  Logger.log(all_cookies)
  Logger.log(next_scirid)
  Logger.log(next_fuel_csrf_token)

  //LPシート書込み
  const battle_record_sheet = SpreadsheetApp.openById('XXXXXXXXXXXXX');
  const battle_record_sheet_obj = battle_record_sheet.getSheetByName('シート1');
  const battle_record_sheet_lastrow = battle_record_sheet_obj.getLastRow();
  const battle_record_sheet_targetrow = battle_record_sheet_lastrow + 1;
  const old_LP = battle_record_sheet_obj.getRange('A' + battle_record_sheet_lastrow).getValue();

  Logger.log(battle_record_sheet_obj.getRange('B' + battle_record_sheet_lastrow).getValue())
  Logger.log(recent_opp_id)
  if(battle_record_sheet_obj.getRange('B' + battle_record_sheet_lastrow).getValue() != recent_opp_id){
    battle_record_sheet_obj.getRange('A' + battle_record_sheet_targetrow).setValue(LP_text);
    battle_record_sheet_obj.getRange('B' + battle_record_sheet_targetrow).setValue(recent_opp_id);
    battle_record_sheet_obj.getRange('C' + battle_record_sheet_targetrow).setValue(recent_opp_char);
    battle_record_sheet_obj.getRange('D' + battle_record_sheet_targetrow).setValue(recent_opp_LP);
    battle_record_sheet_obj.getRange('E' + battle_record_sheet_targetrow).setValue(LP_text - old_LP);
    battle_record_sheet_obj.getRange('F' + battle_record_sheet_targetrow).setValue(date);

  }

  //Cookieシート書込み
  cookie_sheet_obj.getRange('A1').setValue('scirid=' + next_scirid + '; fuel_csrf_token=' + next_fuel_csrf_token + ';')

}

function matchScirid(value) {
    return value.match('scirid'); 
}

function matchFuelCsrfToken(value) {
    return value.match('fuel_csrf_token'); 
}

```

７. サイドバーから「トリガー」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/56bea234-58c6-79b5-97d7-fb47d9dbec1b.png)

８. 「トリガーを追加」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/5dd142bf-4eb7-bda7-4714-76e9c6cadf0a.png)


９.  どの程度の頻度でスクレイピング処理を実行するかを選択する
（今回は「分ベースのタイマー」、「５分おき」を選択）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/8aa53048-1d1c-e6e4-0642-4a0657be48ee.png)

:::note alert
スクレイピング頻度が高すぎると対象サイトに大きな負荷を与えてしまう可能性があります。
スクレイピング頻度を高すぎる値に設定しないように注意してください。
:::

１０. 完成！

## 動作確認
では実際に動作確認してみます。
### 初期値設定
今回作成したスクレイピング処理を実行するには、Cookieと初期戦績データが必要なので設定していきます。
#### Cookie設定
1. 手動で公式戦席サイトにログインしてCookieをメモします。
（sciridとfuel_csrf_tokenの2種）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/1b28f271-244b-5a59-f3df-908768684e86.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/bd47f5f4-43c3-6405-744e-bd6fcbee4f66.png)


2. メモしたCookieを「Cookieシート」のA1に書き込む
```
scirid=XXXXX;fuel_csrf_token=XXXXX;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/8525fe68-d52f-33c2-2179-bec0fd76defd.png)


3. 公式戦績サイトから最後に対戦した相手の情報をメモして書き込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/73c0f211-f24f-25af-d3a5-3055d5c5729a.png)


4. 以上で初期設定は完了！

### テスト
1. まずは1戦だけプレイして戦績を更新する
2. 作成したGASの「実行」を選択
3. 戦績シートを確認すると、先ほどの1戦のデータが追加されているのが確認できるはず
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884549/6c56cb73-7dc7-1404-382c-4a1f70eb43ff.png)

## 参考資料
https://qiita.com/Brutus/items/efbd5fda4c9316fc9e3b
## 商標
Google Apps Script、Google スプレッドシートはGoogle LLC の商標です。
ストリートファイターは株式会社カプコンの商標です。
記載の会社名、製品名、サービス名等はそれぞれの会社の商標または登録商標です。