【GAS】teratailから任意の質問を取得する

4 min read読了の目安(約4000字

概要

今回はGoogle Apps Script(以下GAS)を使ってteratailから任意の質問を取得したいと思います。
よくteratailで回答をしているのですが、Javascriptなどの人気ジャンルの質問はすぐに回答されてしまうため、ベストアンサーにたどり着くのが難しいです。

そこで私は、自分が得意なタグの質問を自動取得して通知するような仕組みを自作していますが、その際にポイントになるのが「質問の取得方法」です。
特定タグだけならteratailにはRSSが用意されているので、それを使えばオッケーですが、「まだ誰も回答していない質問」や「投稿されて長い間回答がついていない質問」など細々とした条件をカスタマイズする場合はteratail APIを使うといいです。

※今回は「通知する」部分については記載しません

前提条件

以下のものは準備できている想定とします。

  • teratailのアカウント
  • GASプロジェクトの作成

teratail APIを使用する

teratailにはAPIが用意されています。

https://teratail.com/api

上記のページによると、以下のことが可能です。

teratail APIでは以下のようなことが可能です。

  • 質問一覧の取得
  • 質問詳細とその回答の取得
  • ユーザー詳細の取得
  • ユーザーのクリップしている質問の取得
  • タグ一覧の取得
  • タグを持つ質問の一覧の取得

今回は「質問一覧の取得」の機能を使います。

前準備「アクセストークンの取得」

APIを使用するためにはアクセストークンが必要になってくるので、取得します。
teratailにログインした状態で設定ページを開きます。
そしてメニューから 「アクセストークン管理」 を押下します。
下図のような表示になるので、「新規作成する」ボタンを押下します。

tera1

すると、「トークン名」を聞かれるので入力し生成します。
下記のようにトークンが表示されたら成功です。

tera2

この文字列をコピーしておきます。

APIの実行

利用するAPIの詳細仕様は以下のページに記載してあります。

https://teratailv1.docs.apiary.io/#reference

質問一覧を取得するエンドポイントはhttps://teratail.com/api/v1/questionsで、以下のようなJSON形式のレスポンスが返ります。
※実際にはquestionsにはmeta.hit_numの中の最新20件が含まれています。
※ページングしたい場合はパラメータを切り替えながらリクエストを投げていきます。

{
  "meta": {
    "message": "success",
    "total_page": 16433,
    "page": 1,
    "limit": 20,
    "hit_num": 328653
  },
  "questions": [
    {
      "id": 999999,
      "title": "質問さんぷる",
      "created": "2021-04-16 13:33:29",
      "modified": "2021-04-16 13:33:29",
      "count_reply": 0,
      "count_clip": 0,
      "count_pv": 4,
      "is_beginner": false,
      "is_accepted": false,
      "is_presentation": false,
      "tags": [
        "Javascript",
        "MySQL"
      ],
      "user": {
        "display_name": "test",
        "photo": "test.jpg",
        "score": 28
      }
    }
  ]
}

GASでリクエストを叩く

上記のエンドポイントをGASで叩くコードを記載します。
ポイントは取得したいタグを自身で設定している点です。
実際はハードコーディングでなく、ある程度自由に追加・削除できる形で運用しています。

// teratailで発行したアクセストークン
const token = '【teratailのアクセストークン】';
// 取得したいタグ
const targetTags = ['Javascript', 'HTML'];
// 質問URLの雛形部分
const urlBase = 'https://teratail.com/questions/';

/**
 * 対象となる質問を取得
 */
function getTargetQuestions() {
  
  let result = [];
    
  // Teratail API
  const uri = 'https://teratail.com/api/v1/questions';
  const headers = {
    'Authorization' : 'Bearer ' + token,
  };
  const option = {
    'method': 'get',
    'contentType': 'application/json',
    'headers' : headers
  }
  
  // API実行
  const response = UrlFetchApp.fetch(uri, option);
  // JSONに整形
  const result = JSON.parse(response.getContentText());

  // 対象の質問リスト
  var targetList = [];
    
  if(result.questions) {
    
    const questions = result.questions;
    
    for(var i = 0; i< questions.length; i++){
      const question = questions[i];
      const modified = new Date(question.modified)
      const isTargetTag = question.tags.some(function(t){ return targetTags.indexOf(t) >= 0 })
      
      // 対象タグだったら
      if(isTargetTag) {        
        // targetListに投稿に必要なデータをまとめてpush
        // ここではURL,件名,回答数を取得している
        targetList.push({ url : urlBase + question.id, title : question.title, countReply : question.count_reply })
      }
      
    }        
  }
    
  return targetList;
}

また、既に回答がついているかどうかを重視したい場合はquestion.count_replyを参照して条件付けをするといいかなと思います。

使ってみて実際どうか

かれこれ1年近くこのスクリプトを運用していますが、自動で自分が回答できそうな質問を拾ってきてくれるため使用感は上々です。
基本数分間隔でこの処理を走らせて通知を受けていますが、それでも先を超されるケースがあるので、同じような事をしている方も一定数いるのかもしれません。
※リクエストは1時間に300件までという制約があるので、その範囲内で利用しましょう

まとめ

今回はteratailの質問をAPIから取得する方法についてGASを用いたケースを紹介しました。
なぜGASかというと時間起動のトリガを使うことで、特に環境を用意しなくてもスケジュール実行してくれるからです。
当然Google系のサービスとは相性がいいので、スプレッドシートに収集内容を保持して解析に使ったりもできます。

リクエストを叩く箇所については一番オーソドックスな形にしてあるので、そのまま他サービスのAPIを叩くコードに置き換えるのもそれほど難しくないと思います。

今回の内容が少しでも役立てば幸いです。