GoogleAppsScriptでTwitterの画像を収集する

本記事は本記事は[マイネット Advent Calendar](http://qiita.com/advent-calendar/2015/mynet)17日目の記事です。
[7日目](http://qiita.com/w_cota/items/48667a845cd06d969d4a)も書かせて頂きました@w_cotaがお送り致します。


# はじめに
[弊社 Advent Calendar 8日目](http://qiita.com/peto_tn/items/d3ae45430352a8f16cc4)で@peto_tnも申しておりましたが弊社では効率化など様々の目的からSpreadsheetsを始めとする多くのGoogle Appsを利用して運営を行っております。
社内でも運営に関わるKPI指標なりイベントスケジュールなりをSpreadsheetsなどで管理しており、それらを効率化するため社内のエンジニアはGoogleAppsScript(以下GAS)を活用して便利スクリプトなどを書いているのですが、私は殆どGASを触ったことがありませんでした。
そこで今回このような機会も得たことですし一念発起、まずはGASを触ってみようじゃないかということで完全に趣味のスクリプトを書いてみましたのでそのご紹介など。

# 概要
GASとTwitterAPIを利用して特定ユーザのタイムラインに投稿された画像をひたすら集めます。
集めた画像はSpreadsheetsにURLを書き出し、GoogleDriveに保存を行います。

# 実装
```コード.gs
// OAuth1認証
var twitter = TwitterWebService.getInstance(
  'consumer_key',
  'consumer_secret'
);

// Twitter認証
function authorize() {
  twitter.authorize();
}

// コールバック
function authCallback(request) {
  return twitter.authCallback(request);
}

// 特定ユーザのタイムラインを取得
function getUserTimeline() {
  
  // 書き込み先のスプレッドシートを取得
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var as = SpreadsheetApp.getActiveSheet();
  
  // 現在のシートの状態を確認
  var col = as.getLastRow() + 1;
  var urls = as.getRange(1, 1, col).getValues();
  
  // 画像を保存するドライブのフォルダを指定
  // https://developers.google.com/apps-script/reference/drive/folder
  var folder = DriveApp.getFolderById('画像を保存するGoogleDriveのフォルダID');
  
  // TwitterAPIでタイムラインを取得してJSONパース
  // https://dev.twitter.com/rest/reference/get/statuses/user_timeline
  var twitter_id = 'タイムラインを取得したいTwitterID';
  var service  = twitter.getService();
  var response = service.fetch('https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name='+twitter_id);
  var tweets = JSON.parse(response);
  
  var url, is_duplicate, tweet, imageBlob;
  
  or (var i=0; i<tweets.length-1; i++) {
    
    is_duplicate = false;
    tweet = tweets[i];
    
    // 写真が添付されているツイートのみピックアップ
    if (tweet.entities.media!=undefined && tweet.entities.media[0].type=='photo') {
      
      // ツイートに添付されている枚数分だけ繰り返す
      for(var k=0; k<tweet.extended_entities.media.length; k++) {
        
        // JSONから画像のURLを取得
        url = tweet.extended_entities.media[k].media_url;
        
        // 写真のURLが重複しているかどうかチェック
        for (var j=0; j<col; j++) {
          if (urls[j][0] == url) {
            is_duplicate = true;
            break;
          }
        }
        
        // 重複していなかったらシートへ書き込み
        if(!is_duplicate) {
          as.getRange(col, 1).setValue(url);
          col++;
          urls = as.getRange(1, 1, col).getValues();
          
          // ドライブにアップロード
          // https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
          imageBlob = UrlFetchApp.fetch(url).getBlob();
          folder.createFile(imageBlob);
        }
      }
    }
  }
}
```

今回の主眼としてはGASを使ってみる事が目的でしたので、TwitterAPIへのアクセスは[yoshiyuki-hirano様のこちらの記事](http://yoshiyuki-hirano.hatenablog.jp/entry/2015/10/13/010317)で紹介されておりますライブラリを使用させて頂きました。


1. 書き込み先のSpreadsheet、sheet、書き込みを行う行と、これまで入っているデータを取得
2. 画像の保存先となるGoogleDriveのフォルダを取得
3. TwitterAPIを介してタイムラインを取得＆パース
4. ツイートの中身を洗う
    1. ツイートに画像が添付されているか確認
    2. 添付されていれば画像のURLを取得
    3. 現状のシートの中に同じURLの写真が無いか確認
    4. 無かったらシートへURLを格納 & ドライブの指定フォルダへアップ

と以上のように中身としては非常にシンプルになっていると思います。
最後にgetUserTimeline関数が定期的に動くようにGAS上で設定してあげます。
※ 実行間隔はAPIの上限を超えないように && 拾いたいユーザのTLの速度と兼ね合いで
![](https://i.gyazo.com/d15dd4e3a0df4e8d7304d3c6215a1347.png)

後は適当に待っていると…
![](https://i.gyazo.com/a41e0189eb9c0d3d8b3dfc85791ad491.png)

この通り！
ちゃんとドライブにもアップロードされてます。
![](https://i.gyazo.com/054a93d48d92fbdd041edb2358d92498.png)

これがどうスマホアプリの運営に繋がるのかは自分でも分かりませんがとりあえず思いついた事を形に出来てよかったです。

# 要改善点
* 別のアプリにアップロードされている画像も拾いたい
* ~~一つのツイートに対して複数の画像が紐付けてある場合は2枚目以降が取得できない~~
* ~~たまにエラー吐いてる~~
  * 対応&コード修正済

# 注意点
ここまで書いてなんですが、ご自身以外のTwitterに流れてる画像をGoogleDriveにアップするとか、著作権なり肖像権なりめんどくさい気がするのでぶっちゃけやらない方がいいと思います。


# 参考資料
http://yoshiyuki-hirano.hatenablog.jp/entry/2015/10/13/010317
http://blog.hika69.com/blog/2014/01/28/google-apps-script/
https://developers.google.com/apps-script/reference/spreadsheet/
https://dev.twitter.com/rest/public