1
7

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.

Twitter API と GAS を用いた情報収集 - 3. Google スプレッドシートとの連携 編

Last updated at Posted at 2021-08-24

一連の記事では、TwitterAPI の情報を GAS 経由で取得し、Google スプレッドシートに録り溜めていく方法を、ステップバイステップで説明していきます。

前回 は、OAuth1 ライブラリを用いて、GAS から TwitterAPI の呼び出しを行う最小のコードを示しました。

今回は、GAS から Google スプレッドシートに書かれたキーワードを参照し、そのキーワードを使って TwitterAPI の Search を呼び出し、得られたツイートデータを Google スプレッドシートに書き戻す方法について説明します。

しかしこの辺りは、TwitterAPI へのアクセスを除いて、ごくごく一般的な GAS の処理だと思うので、日々 GAS を書いてらっしゃる人からすると、新しい発見はこの記事にないかと思います。
…という点を踏まえて読んでいただけると助かります。

Google スプレッドシートの準備

GAS は、Google スプレッドシートに内包されるものではなく、前回用意した、スタンドアローンのものを拡張して使います。
ここでいう「内包されるもの」とは、Google スプレッドシート上で [Tools] ⇒ [Script Editor] で書き始められる GAS のことを言っています。

ちなみに、私は、次のように GAS を使い分けています。(完全に好みの問題です)

  • 既存の Google スプレッドシート上にあるデータで、お手軽に、秒でなんらかの簡単な処理をさせてしまいたい場合は、内包する GAS
  • 特定の Google スプレッドシートに依存しない、ある程度汎用性のある処理を作る場合はスタンドアローンの GAS

そういうわけで、Google スプレッドシートは別で用意し、ID を控えておきます。
ちなみに、ID は、Google スプレッドシートの URL の下記の場所になります。

https://docs.google.com/spreadsheets/d/{Google Spreadsheet ID}/edit#gid=0

Google スプレッドシートの仕様決め

仕様はいくらでも複雑にできますが、一連の記事ではシンプルさを重視して、次のように決めます。

  • A1 セルに検索用キーワードが書かれている
  • A2 セルに前回のキーワード保存される
  • 3 行目以降の行末に Search API で取得したツイートの情報が保存されていく
  • GAS が走ったとき、A1 ≠ A2 なら、新しいキーワードが指定されたものとして 3 行目以降をクリアする

蛇足: Google スプレッドシートの読み込みと書き込みについて

C/C++ や C# で Excel にアクセスするプログラムを作ったことのある人なら、メモリの使用量を気にして 1 セルずつ処理することを試みたことがあると思います。しかしこの方法だと恐ろしく処理時間がかかり、使い物になりません。

そうではなく、読み込み/書き込みするのに必要な複数のセルを Range でがばっとメモリ上に取り、メモリ上で必要な処理をした後、Range で Excel に書き戻すようにすると、使用に耐えられる処理速度が出てきます。

Google スプレッドシートの処理も、Excel のそれと似ており、getRange() で範囲指定した後、getValues() で必要なデータを配列に取り込み、配列を処理した後、setValues() でシートに書き戻します。

Range は、左上のセル (A1) から、データが書かれている最大行(getLastRow())、最大列(getLastColumn())まで取ることが多いですが、Google スプレッドシートでは、getDataRange() でそれを一発で取得できるなど、便利な関数が多数用意されています。

コード

前回の最後のコードに、Google スプレッドシートの読み込み/書き込み処理を付け足します。
シートへ記載する Tweet データは、ユーザー名、Tweet した日付け、Tweet 内容の 3 つを、それぞれ、A, B, C 列に書き込むこととしました。

let VAL_CONSUMER_API_KEY     = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
let VAL_CONSUMER_API_SECRET  = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
function logOAuthURL() {
  var twitterService = getTwitterService();
  Logger.log(twitterService.authorize());
}
function getTwitterService() {
  // Create a new service with the given name. The name will be used when
  // persisting the authorized token, so ensure it is unique within the
  // scope of the property store.
  return OAuth1.createService('twitter')
      // Set the endpoint URLs.
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authorize')
      // Set the consumer key and secret.
      .setConsumerKey(VAL_CONSUMER_API_KEY)
      .setConsumerSecret(VAL_CONSUMER_API_SECRET)
      // Set the name of the callback function in the script referenced
      // above that should be invoked to complete the OAuth flow.
      .setCallbackFunction('authCallback')
      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getUserProperties());
}
function resetTwitterService() {
    var twitterService = getTwitterService();
    twitterService.reset();
}
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 twitterSearch(keyword, maxCount) {
    let encodedKeyword = encodeURIComponent(keyword);
    try {
        var twitterService = getTwitterService();
        if (!twitterService.hasAccess()) {
            Logger.log(twitterService.getLastError());
            return null;
        }
        var url = 'https://api.twitter.com/1.1/search/tweets.json?q='
            + encodedKeyword
            + '&result_type=recent&lang=ja&locale=ja&count='
            + maxCount;
        var response = twitterService.fetch(url, { method: "GET" });
        var json = JSON.parse(response);
        Logger.log(json);
        return json;
    }
    catch (ex) {
        Logger.log(ex)
        return null;
    }
}
// 古い main() はコメントアウト
//function main() {
//  twitterSearch("天気",50);
//}
// ⇓ 今回付け足したコード ⇓
function updateSheet(sheet) {

  let keywordNew = ''; // 今回のキーワード
  let keywordOld = ''; // 前回処理時のキーワード

  // セル A1 から、2 行 1 列分の範囲を取得
  let range = sheet.getRange(1, 1, 2, 1);
  // 取得した範囲の値を取得
  let values = range.getValues();

  // セル A1 にある今回のキーワードの取得
  keywordNew = values[0][0];
  // セル A2 にある前回のキーワードの取得
  keywordOld = values[1][0];
  // セル A2 にある前回のキーワードを今回のキーワードで更新しておく
  values[1][0] = keywordNew;

  // 2 行を一旦書き込む
  range.setValues(values);

  // 今回のキーワードが空、または、新規キーワードなら、3 行目以降の全セルをクリア
  if ( sheet.getLastRow() > 3 && (keywordNew == '' || keywordNew != keywordOld ) ) {
    range = sheet.getRange(3, 1, sheet.getLastRow()-2, sheet.getLastColumn());
    values = range.getValues();
    for ( let row=0; row<values.length; row++) {
      for ( let col=0; col<values[row].length; col++) {
        values[row][col] = '';
      }
    }
    // 一旦書き込む
    range.setValues(values);
  }

  // keyword が空でなければ Tweet データを取得してシートの更新
  if ( keywordNew != '') {
    let json = twitterSearch(keywordNew, 50);
    let countTweets = json.statuses.length;
    // 更新をする最初の行の決定
    let rowUpdateStart = ( keywordNew != keywordOld )? 2 : sheet.getLastRow();
    // Tweet 記入用のレンジの取得
    range = sheet.getRange(rowUpdateStart+1, 1, countTweets, 3);
    values = range.getValues();
    let row = 0, col = 0;
    json.statuses.forEach( tweet => {
      values[row][col++] = tweet.user.name;
      values[row][col++] = tweet.created_at;
      values[row][col] = tweet.text;
      row++; col=0;
    });
    range.setValues(values);
  }
}
let VAL_ID_TARGET_BOOK = 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'; // Google スプレッドシートの ID
function main() {
  // Google スプレッドシートの取得
  let book = SpreadsheetApp.openById(VAL_ID_TARGET_BOOK);
  // 含まれる全シートの取得
  let sheets = book.getSheets();
  // 全シートについて updateSheet() を呼んで処理をする
  sheets.forEach(function (sheet) {
    updateSheet( sheet );
  }); 
}

これで、Google スプレッドシートのセル A1 に「天気」などと記入し、main() を実行すると、シートに結果が記入されます。

updateSheet() 関数で、検索キーワードの取得、前回のキーワードとの比較、データ消去、更新とうしているので、やや冗長で読みにくくなっているかもしれませんが、基本的には getRange()getValues() でシートのデータを取得して、setValues() で書き戻すことをしています。

後はこれを用途に合わせて発展させていけば、ニーズに合った仕様のツールを得られると思います。

以上で一連の記事を締めくくりたいと思います。


1
7
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
1
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?