Twitter API Free Planで自分のユーザ情報を獲得:GAS

GASTwitterAPIからユーザー情報取得
◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【5/21開催】Azure OpenAI ServiceによるRAG実装ガイドを公開しました
生成AIを活用したユースケースで最も一番熱いと言われているRAGの実装ガイドを公開しました。そのガイドの紹介をおこなうイベントです!!
https://tech-lab.connpass.com/event/315703/

今回は、Google Apps Script(GAS)でTwitter APIのFree Plan v2を使用して自分のユーザー情報を定期実行でスタックするプログラムを作成しました。Twitter Free Planの制限なども知らなくて、苦労をしたので、注意するべきところ含めて解説しています。昔はできた!ってのが羨ましくちょっと悲しい

はじめに

ども!最近はブログサムネイル警察という取り組みを始めた龍ちゃんです。ブログが投稿されて、サムネイルに問題が発生している場合は、サムネイルをお届けするサービスを社内でひっそり展開しています。

さて!今回は、Google Apps Script(GAS)でTwitter APIを触る会になります。私の部署でやっているTwitterが、バッチを獲得しました。その流れもあって、Twitterの動向を定期的に取りたいというのが動機になっています。このブログでわかることは以下になります。

  • GAS Free Planの制限に関して
  • GASでTwitter API v2をOAuth2.0で使用するためのスクリプト
  • 毎日、自分のフォロワー数・フォロー数・いいねされた数を取得してスプレッドシートに保存するスクリプト

それでは、Twitter APIに5日ほど悩まされた恨みを込めてまとめていきます。

Twitter Free Planの制限

もし、以下のエラー文で悩まされている方がいたらここをしっかり読んでみるといいかもしれません。

When authenticating requests to the Twitter API v2 endpoints, you must use keys and tokens from a Twitter developer App that is attached to a Project. You can create a project via the developer portal.

訳して、「ちゃんと設定しているのに!!」となった人は一度深呼吸をしましょう。落ち着いたら以下のサイトを開いてみてください。こちらの公式サイトを開いてください。各プランのアクセスすることができる制限が記載されています。

FreePlanの制限

Free Planの場合は、Only Tweet creationとなっています。つまり、ツイートの作成しかできないわけです。(どおりでツイート周りの記事はいっぱいあると思った…)

次にTwitter Developer Portalを開いて、以下の画像を確認してください。ここでは、各プランがアクセスすることができるAPIが記載されています。

TwitterFreePlan

以上の情報から、Twitter API v2 Free Planでは、以下の二つのことが可能です。

それ以外のAPIにアクセスした時点ではじかれます。そして、もう一個制限がありまして。もし書くAPIにBearer tokenでアクセスした場合も同様にはじかれます。

これは、各リファレンスに記載があるのですが、OAuth2.0かOAuth1.0aじゃないと認証が通りません。(これを理解するのに3日かかりました…)

というわけで、本題に移っていきます。

Twitter API v2(OAuth2.0)で自分の情報を取得する

まずはAPIの利用申請を行う必要があります。その後、システムの全体像の説明と実装の説明に入っていきます。ちょっと長いですが、必要なところだけ抜き出しちゃってください。

Twitter API利用申請

まずは、利用申請です。こちらは、英語で作文が必要なのですが、ChatGPTと会話しながら作成したら行けると思います。まずは、原案をChatGPTにぶん投げます。それを添削してもらえば楽です。

ChatGPTと乗り越えるAPI申請

原案はこちらの方を参考にさせていただきました。

全体像と事前に用意するもの

全体像としては以下になります。

TwitterAPI経由ユーザー情報取得全体像

ひとまず、Google Apps Scriptファイルと情報を書き込み先のスプレッドシートを作成しましょう。GASのファイルは、この後の設定で使うので開いておいてください。

青色が認証認可の流れで、赤色がTwitterAPIを使用した情報取得になります。

Twitter OAuth2.0利用申請

Twitter Developer Portal上で、OAuth2.0の利用申請を行いましょう。

TwitterAPIOAuth2設定

クリックすると設定画面が開きます。権限周辺に関しては適したものを選択してください。今回の構成の場合はREADのみで大丈夫です。アプリケーションのタイプに関しても、Automated Appなので下です。Website URLは自分が持っているサイトのURLを書いておきましょう。もし、持っていない人は迷惑が掛からなさそうなところで申請しちゃいましょう。重要な設定としては、Callback URIになります。

詳細設定:URL Callbackの設定

ここは先ほど、作成したGASのIDを使用して以下のURLを作成します。

https://script.google.com/macros/d/{スクリプトID}/usercallback

すると、クライアントIDとシークレットが取得されます。一時的にメモしておきましょう。

GAS側にスクリプトプロパティ設定

先ほど取得した情報をスクリプトプロパティとして保存しておきましょう。スクリプトプロパティ何それ?という方は、こちらの記事を読んでみてください。

設定する項目としては、以下になります。

プロパティ名説明
CLIENT_IDTwitter API OAuth2.0で取得したやつ
CLIENT_SECRETTwitter API OAuth2.0で取得したやつ
SHEET_ID用意したGoogleシートのID

設定項目は以上です。ちゃんと保存をしておきましょう。

GASでOAuth2.0用ライブラリをインストール

GAS上でOAuth2.0の認証認可を行うためにライブラリをインストールしておく必要があります。左側の「ライブラリ」を開いて以下の文字列を検索してください。追加するライブラリの情報はこちらです。

1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF

以下の画面で追加してください。

OAuth2.0のライブラリ追加

これで実装前の準備が完了しました。

実装

それでは一気に実装に進んでいきます。ここまで準備が完璧だと、すんなり進んでいくと思います。

OAuth2.0認証を通す

まずは、APIを実行するための認証・認可を行う必要があります。ライブラリの公式リファレンスを参照に以下のコードをコピペしてmainを実行してください。

function getService() {
  pkceChallengeVerifier();
  const userProps = PropertiesService.getUserProperties();
  const scriptProps = PropertiesService.getScriptProperties();
  const clientId = scriptProps.getProperty('CLIENT_ID');
  const clientSecret = scriptProps.getProperty('CLIENT_SECRET');

  return OAuth2.createService('twitter')
    .setAuthorizationBaseUrl('https://twitter.com/i/oauth2/authorize')
    .setTokenUrl('https://api.twitter.com/2/oauth2/token?code_verifier=' + userProps.getProperty("code_verifier"))
    .setClientId(clientId)
    .setClientSecret(clientSecret)
    .setCallbackFunction('authCallback')
    .setPropertyStore(userProps)
    .setScope('users.read tweet.read tweet.write offline.access')
    .setParam('response_type', 'code')
    .setParam('code_challenge_method', 'S256')
    .setParam('code_challenge', userProps.getProperty("code_challenge"))
    .setTokenHeaders({
      'Authorization': 'Basic ' + Utilities.base64Encode(clientId + ':' + clientSecret),
      'Content-Type': 'application/x-www-form-urlencoded'
    })
}

function authCallback(request) {
  const service = getService();
  const authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('Success!');
  } else {
    return HtmlService.createHtmlOutput('Denied.');
  }
}

function pkceChallengeVerifier() {
  var userProps = PropertiesService.getUserProperties();
  if (!userProps.getProperty("code_verifier")) {
    var verifier = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";

    for (var i = 0; i < 128; i++) {
      verifier += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    var sha256Hash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, verifier)

    var challenge = Utilities.base64Encode(sha256Hash)
      .replace(/\\+/g, '-')
      .replace(/\\//g, '_')
      .replace(/=+$/, '')
    userProps.setProperty("code_verifier", verifier)
    userProps.setProperty("code_challenge", challenge)
  }
}

function logRedirectUri() {
  var service = getService();
  Logger.log(service.getRedirectUri());
}

function main() {
  const service = getService();
  if (service.hasAccess()) {
    Logger.log("Already authorized");
  } else {
    const authorizationUrl = service.getAuthorizationUrl();
    Logger.log('Open the following URL and re-run the script: %s', authorizationUrl);
  }
}

実行が完了したら、ログの部分に認証・認可用のURLが払い出されます。そこにアクセスしましょう。

Twitter 認証認可表示

許可を行い、「Success」が表示されたら成功です。

シートに記載する関数を作成

ここでは、実行結果を保存するための関数を用意しておきます。スクリプトプロパティからsheetIDを取得してシートを開いて最後の列に挿入しています。シート名をTwitterUserAnalytics という名前で用意する必要があります。

function insertSheet([followers_count, following_count, tweet_count, listed_count, like_count]) {
  const scriptProps = PropertiesService.getScriptProperties();
  const sheetID = scriptProps.getProperty('SHEET_ID');
  const file = SpreadsheetApp.openById(sheetID)
  const sheet = file.getSheetByName("TwitterUserAnalytics")
  const date = Utilities.formatDate(new Date(), 'JST', 'yyyy-MM-dd');

  sheet.appendRow([date, followers_count, following_count, tweet_count, listed_count, like_count])
}

Twitter APIからユーザー情報を取得して保存

最終的に、Twitter APIにアクセスして情報を取得しましょう。エンドポイントにパラメータを追加して、標準で受け取る情報を拡張しています。標準では、内部ID・ID・名前しか取得することができません。ほかに取得できるパラメータとしては、公式リファレンスに記載されています。

const endpointMe = "https://api.twitter.com/2/users/me?user.fields=public_metrics";

function getUserMe() {
  let service = getService();
  if (service.hasAccess()) {

    const response = UrlFetchApp.fetch(endpointMe, {
      method: "get",
      headers: {
        Authorization: 'Bearer ' + service.getAccessToken()
      },
      muteHttpExceptions: true,

      contentType: "application/json"
    });

    const result = JSON.parse(response.getContentText());

    const public_metrics = result["data"]["public_metrics"]
    insertSheet([public_metrics["followers_count"], public_metrics["following_count"], public_metrics["tweet_count"], public_metrics["listed_count"], public_metrics["like_count"]])

  } else {
    Logger.log("Not Authorized");
    return null;
  }
}

ちなみにレスポンスとしては、こんな感じで帰ってきます。

{
  "data": {
    "id": "xxxxxxxxxxxxxxxxx",
    "username": "RyuReina_Tech",
    "name": "龍:Ryu",
    "public_metrics": {
      "followers_count": 0,
      "following_count": 0,
      "tweet_count": 0,
      "listed_count": 0,
      "like_count": 0
    }
  }
}

実行してみて、書き込みの確認が取れたら完了です。あとは、トリガーで一日おきに実行するように設定を行えば完了です。

終わりです

お疲れ様でした。最初は全然できなくて、久々に悩んでしまう内容でした。OAuthにちょっと入門しておいてよかったです。全部、Bearer で行けると思うやんと思ってましたけど、公式リファレンスはちゃんと読もうという結論が出ましたね。

にしても、API利用申請がめっちゃ時間かかるというイメージだったんですけど、申請してすぐ使えたのは制限が厳しくなったおかげでアクセスが容易になったんですかね。

まぁChatGPTに作文してもらったんであんまり関係ないんですけどね。

さて!泣きながらまとめたので、もう十分でしょう!今回の参考資料です。

アバター画像
About 龍:Ryu 107 Articles
2022年入社で主にフロントエンドの業務でTailwindと遊ぶ日々。お酒とうまいご飯が好きで、運動がちょっと嫌いなエンジニアです。しゃべれるエンジニアを目指しておしゃべりとブログ執筆に注力中(業務もね)//
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる