GASとスプシだけで、特定キーワードのツイート数分析をする

# はじめに
突然ですが，サイゼリヤって最高ですよね！僕もサイゼリヤが大好きで，週2くらいで通ってます。
ところで，サイゼリヤと言えば，頻繁にツイッターで炎上していることをご存じでしょうか？

https://togetter.com/li/1904993

簡単に説明すると，「サイゼリヤで満足とか心が貧しい」等のツイートに対して，多くの人が反対をして盛り上がるといった流れです。また，これが定期的に起こるのです。

サイゼリヤを愛する者として，サイゼリヤがいつツイッターで炎上しているかは把握しておきたいものです。

そこで今回は，GASとスプシを使って「サイゼ」を含むツイートの数を日付ごとに集計しようと思います。


# 概要
GASとTwitter APIを使って，スプレッドシートに指定したキーワード(今回は「サイゼ」)を含むツイートの本文を貯めるスクリプトを書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359441/378e3168-a277-d6b3-a783-8f955ad95ec3.png)

また，日付ごとのツイート数を別のシートに集計するスクリプトも書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359441/4c229c0f-46c6-904f-0a36-8aa0db20e674.png)

それぞれのスクリプトはGASの機能で自動実行させるので，放置しておけばスプレッドシートが更新されていきます。

# 前提
まず前提として，現状では一般ユーザーのTwitter APIは過去7日間までのツイートしか参照できません。

https://qiita.com/ume-san/items/88a6c891aed3fd48c6d5

また，今後の展望として，ツイートのネガポジ判定等も行いたかったので，今回はスプレッドシートにツイート本文を貯めていくという手法を選びました。


# 実装
今回はTwitter APIの利用方法については省略します。

(1) ツイート集める

```js
let VAL_CONSUMER_API_KEY     = '取得したAPIキー';
let VAL_CONSUMER_API_SECRET  = '取得したAPIシークレット';

function logOAuthURL() {
  var twitterService = getTwitterService();
  Logger.log(twitterService.authorize());
}

function getTwitterService() {
  return OAuth1.createService('twitter')
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authorize')
      .setConsumerKey(VAL_CONSUMER_API_KEY)
      .setConsumerSecret(VAL_CONSUMER_API_SECRET)
      .setCallbackFunction('authCallback')
      .setPropertyStore(PropertiesService.getUserProperties());
}

function authCallback(request) {
  var twitterService = getTwitterService();
  var isAuthorized = twitterService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}

function getTweets(){
  const key = "サイゼ"
  const sheetName = "data"
  const service = getTwitterService();
  if (service.hasAccess()) {
    const url = 'https://api.twitter.com/1.1/search/tweets.json?q=' + key + '&count=100&locale=ja&result_type=recent';
    const response = service.fetch(url, {method: 'get'});
    const results = JSON.parse(response.getContentText());
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName(sheetName);

    var lastRow = sheet.getLastRow();
    const existSentences = sheet.getRange(1, 1, lastRow).getValues().map(value => value[0])

    results['statuses'].forEach(e => {
      if (!existSentences.includes(e['text'])) {
        sheet.getRange(lastRow + 1, 1).setValue(e['text']);
        sheet.getRange(lastRow + 1, 2).setValue(e['created_at']);
        lastRow += 1;
      }
    })

    // 10000行を超えたら昔のものから順次削除
    if (lastRow > 10000){
      sheet.deleteRows(1, 1000)
    }
  } else {
    Logger.log("認証エラー");
  }
}
```

今回は"サイゼ"という単語を含むツイートを取得していきます。

また，すべての情報をスプシに保存し続けると重くなっていしまうので，10000行を超えたら昔のものから順次削除する処理を最後に追加しています。

(2) 集計する

```js
function aggregateData(){
  const dataSheetName = "data"
  const aggregateSheetName = "aggregate"
  const today = Utilities.formatDate(new Date(), 'JST', 'yyyy-MM-dd');
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const dataSheet = ss.getSheetByName(dataSheetName);
  const aggregateSheet = ss.getSheetByName(aggregateSheetName);

  var lastRow = dataSheet.getLastRow();
  const newDates = dataSheet.getRange(1, 2, lastRow).getValues().map(value => value[0])
  const dateObj = {}
  newDates.forEach(e => {
    const postedDate = Utilities.formatDate(new Date(Date.parse(e)), 'JST', 'yyyy-MM-dd');
    if (dateObj[postedDate]) {
      dateObj[postedDate] += 1;
    } else {
      dateObj[postedDate] = 1;
    }
  })

  lastRow = aggregateSheet.getLastRow();
  const existDates = aggregateSheet.getRange(1, 1, lastRow).getDisplayValues().map(value => value[0])

  for (const [key, value] of Object.entries(dateObj)) {
    if (!existDates.includes(key)) {
      aggregateSheet.getRange(lastRow + 1, 1).setValue(key);
      aggregateSheet.getRange(lastRow + 1, 2).setValue(value);
      lastRow += 1;
    }
    else if (key === today) {
      aggregateSheet.getRange(lastRow, 2).setValue(value);
      lastRow += 1;
    }
  }
}
```

ツイート本文と投稿時間を保存したSheetを参照して，ツイート数保存用の別のSheetを更新します。

# 分析
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359441/0657b66c-5e6e-a28d-d843-da046f4f3767.png)

上の画像は2022/12/29～2023/3/29の約3か月の期間における，"サイゼ"という単語を含むツイートの数の推移です。

この３か月間で炎上は起きなかったので，特に大きな特徴は見られませんが，月曜日から日曜日にかけてツイート数が増加する傾向にあることが読み取れます（横軸の日付が日曜日）。あとは，年始のツイート数が極端に少ないことなども読み取れます。

# おわりに
ネガポジ，頻出単語分析などを組み合わせたらより面白そうなので，今度やってみようと思います。