TwitterのAPIを使って特定のハッシュタグが含まれた投稿を取得する

##目的
特定のハッシュタグが含まれたツイートをAPIを用いて取得する
取得したツイートをスプレッドシートに書き込む

##方法
TwitterのAPIを使えば、特定のハッシュタグを含む投稿を簡単に取得可能。
今回はGoogleAppsScript使った。色々便利なので。
それをそのままスプレッドシートに書き込めば、特定のハッシュタグが含まれたツイートのリストが完成！

APIで日時の設定もできるので、〇月〇日～×月×日までのツイートとかでも行ける！

#手順① TwitterのAPIを取得する
まずツイッターのAPIを取る。ここらへん見たらだいたい分かる。「Twitter API」とかで調べたらいくらでも出てくる。
たぶん２，３日かかる。

https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/

#手順②　GASと連携する
↓の記事見て、GASとTwitterのAPIを連携する。
Oauthまわりが面倒ですけど、丁寧にやれば意外と簡単カンタン！2021/07/23時点ではこの記事の通りで全く問題なかった。

https://tech-cci.io/archives/4228

#手順③　コード書く
##今回やりたいこと
「#metoo」というハッシュタグが含まれた投稿だけを取得する

##今回できなかったこと
日時指定をやろうと思ったけど煩雑になるのでやめた。多分簡単にできると思うけど、とりあえずめっちゃエラーでた！
意味わからない。

##書いたコード
書いたコードはこんな感じ！
原則的にこのポストを参考にして書いたので、ぜひこの記事を見てください。

https://tech-cci.io/archives/4228

```javascript
/*ここからOauth認証用コード*/
// 認証用URL取得
function getOAuthURL() {
  Logger.log(getService().authorize());
}
 
// サービス取得
function getService() {
  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(PropertiesService.getScriptProperties().getProperty("CONSUMER_API_KEY"))
      .setConsumerSecret(PropertiesService.getScriptProperties().getProperty("CONSUMER_API_SECRET"))
      .setCallbackFunction('authCallback')
      // 認証情報をプロパティストアにセット（これにより認証解除するまで再認証が不要になる）
      .setPropertyStore(PropertiesService.getUserProperties());
}
 
//  認証成功時に呼び出される処理を定義
function authCallback(request) {
  var service = getService();
  var authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('success!!');
  } else {
    return HtmlService.createHtmlOutput('failed');
  }    
}


/*ここまでOauth認証*/


/*ここからツイートの取得用コード*/


// タイムライン取得用のAPIを起動する関数
function getTimeLine() {
  
  //クエリ設定
  //クエリはUTF8にして入れないとエラーでるから気をつけて！
  var query="%23metoo";
  
  //言語設定 (言語コードはISO 639-1に準拠) 
  var lang="ja";
  
  // スプレットシート読み込み
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  //シート名は任意
  var sheet = ss.getSheetByName('sheet1');
  
  //スプレッドシート書き込みのために最終行取得
  const AValues = sheet.getRange('A:A').getValues();　 //A列の値取得
  var LastRow = AValues.filter(String).length;　　//空白の要素を除いた長さを取得
  
  var twitterService = getService();
  
  if (twitterService.hasAccess()) {
    // 検索結果取得
    var twMethod = { method:"GET" };
    var json = twitterService.fetch("https://api.twitter.com/1.1/search/tweets.json?q="+query+"&result_type=recent&count=100&lang="+lang, twMethod);
 
    // json形式で返ってくるのでパース
    var array = JSON.parse(json);
        Logger.log(array);

    for (var i=1; i<100; i++){
　　//ここで配列の中から、日付とcontent取得してる。いいね/リツイートはここに同じ感じで書き込んだら取れる。
    var created_at = array["statuses"][i]['created_at'];
    var content = array["statuses"][i]["text"];
　　//ふつうは意味ないけど、デバック用にログに出力してる
    console.log(created_at);
    console.log(content);
    
　　//ここで日付をシートに書き込む。日付は見やすいようにちょっと変換する。
    sheet.getRange(LastRow+1,1).setValue(Utilities.formatDate(new Date(created_at), 'Asia/Tokyo','yyyy/MM/dd HH:mm:ss'));
　　//ここでcontentを書き込む
    sheet.getRange(LastRow+1,2).setValue(content);
    LastRow++;
    }
    
    // 取得した項目をログに表示
    Logger.log(array);
  
  } else {
  Logger.log(service.getLastError());
  }
}
/*ここまで*/
```

#実行してみる
function gettimelineを実行。こんな感じで「#metoo」が含まれた投稿が100件スプレッドシートに出力できた！
A列には日時、B列には投稿内容を出力した。いいね数とかリツイート数も取得できる（↑のコードには書いてない）。
プライバシー保護のために強めにモザイクをかけてる。
![privacy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719996/b3481a9e-bc5b-d09f-2200-0a835c124088.png)

#おわりに
こんな感じで簡単に特定のハッシュタグが含まれた投稿を取得できる。
簡単とはいえ、きちんと使い物にするにはもう少しコード書かないといけないけど、基本的にはこれでOK。

#追補 20210826
この前書いたやつだと100件しか取得できなかったのでたくさんできるようにコードを書き換えた。
謎のエラーでるけど、300件くらいまでは余裕で取得できる。

```javascript
/*ここからOauth認証用コード*/
// 認証用URL取得
function getOAuthURL() {
  Logger.log(getService().authorize());
}
 
// サービス取得
function getService() {
  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(PropertiesService.getScriptProperties().getProperty("CONSUMER_API_KEY"))
      .setConsumerSecret(PropertiesService.getScriptProperties().getProperty("CONSUMER_API_SECRET"))
      .setCallbackFunction('authCallback')
      // 認証情報をプロパティストアにセット（これにより認証解除するまで再認証が不要になる）
      .setPropertyStore(PropertiesService.getUserProperties());
}
 
//  認証成功時に呼び出される処理を定義
function authCallback(request) {
  var service = getService();
  var authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('success!!');
  } else {
    return HtmlService.createHtmlOutput('failed');
  }    
}


/*ここまでOauth認証*/


/*ここからツイートの取得用コード*/


// タイムライン取得用のAPIを起動する関数
function getTimeLine() {
  
  //クエリ設定
  //クエリはUTF8にして入れないとエラーでるから気をつけて！
  var query="%23metoo%20exclude%3Aretweets";
  
  //言語設定 (言語コードはISO 639-1に準拠) 
  var lang="ja";
  
  // スプレットシート読み込み
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  //シート名は任意に変更可能
  var sheet = ss.getSheetByName('sheet1');
  
  var twitterService = getService();
  
  if (twitterService.hasAccess()) {
    //認証が成功したら実行
    
    //GETメソッドの定義
    var twMethod = { method:"GET" };

    //for文ここから
    for (let step = 0; step < 5; step++) {
      
      if(step==0){
    //最初のGETがJSONで返るのでパース
    var json = twitterService.fetch("https://api.twitter.com/1.1/search/tweets.json?q="+query+"&result_type=recent&count=500&lang="+lang, twMethod);
    var array = JSON.parse(json); 
    
    //書き込みのためのfor文-array
    for (let i=1; i<100; i++){
      
      //スプレッドシート書き込みのために最終行取得
      const AValues = sheet.getRange('A:A').getValues();　 //A列の値取得
      var LastRow = AValues.filter(String).length;　　//空白の要素を除いた長さを取得
      
      var created_at = array["statuses"][i]['created_at'];
      var content = array["statuses"][i]["text"];
      sheet.getRange(LastRow+1,1).setValue(Utilities.formatDate(new Date(created_at), 'Asia/Tokyo','yyyy/MM/dd HH:mm:ss'));
      sheet.getRange(LastRow+1,2).setValue(content);
      
    }
          }else if(step==1){
    
    //ページネーション処理
    var json = twitterService.fetch("https://api.twitter.com/1.1/search/tweets.json?q="+query+"&result_type=recent&count=100&lang="+lang, twMethod);
    var array = JSON.parse(json); 
    var paginationQuery = array["search_metadata"]["next_results"];
            
    //２回目以降のGETがJSONで返るのでパース
    var jsonPagination = twitterService.fetch("https://api.twitter.com/1.1/search/tweets.json"+paginationQuery, twMethod);
    var arrayPagination = JSON.parse(jsonPagination); 
        
    //書き込みのためのfor文-arrayPagination
    for (let i=1; i<100; i++){
      
      //スプレッドシート書き込みのために最終行取得
      const AValues = sheet.getRange('A:A').getValues();　 //A列の値取得
      var LastRow = AValues.filter(String).length;　　//空白の要素を除いた長さを取得
      
      var created_at = arrayPagination["statuses"][i]['created_at'];
      var content = arrayPagination["statuses"][i]["text"];
      sheet.getRange(LastRow+1,1).setValue(Utilities.formatDate(new Date(created_at), 'Asia/Tokyo','yyyy/MM/dd HH:mm:ss'));
      sheet.getRange(LastRow+1,2).setValue(content);
    }
      }
      else{
    //ページネーション処理
    var paginationQuery = arrayPagination["search_metadata"]["next_results"];
    
    console.log(paginationQuery);
        
    //3回目以降のGETがJSONで返るのでパース
    var jsonPagination = twitterService.fetch("https://api.twitter.com/1.1/search/tweets.json"+paginationQuery, twMethod);
    var arrayPagination = JSON.parse(jsonPagination); 
        
    //書き込みのためのfor文-arrayPagination
    for (let i=1; i<100; i++){
      
      //スプレッドシート書き込みのために最終行取得
      const AValues = sheet.getRange('A:A').getValues();　 //A列の値取得
      var LastRow = AValues.filter(String).length;　　//空白の要素を除いた長さを取得
      
      var created_at = arrayPagination["statuses"][i]['created_at'];
      var content = arrayPagination["statuses"][i]["text"];
      sheet.getRange(LastRow+1,1).setValue(Utilities.formatDate(new Date(created_at), 'Asia/Tokyo','yyyy/MM/dd HH:mm:ss'));
      sheet.getRange(LastRow+1,2).setValue(content);
    }
    
    //最初のGETとそれ以降のGETを分岐させるif文ここまで
      }

    //取得→書き込みのためのfor文ここまで
    }
    

}else {
  //認証に失敗したらエラーログを吐く
  Logger.log(service.getLastError());
  }
}
/*ツイート取得用のコードここまで*/
```