Serverless CLI を使って AWS Lambda + API Gateway から 特定のハッシュタグの Tweets を取得する方法 (Twitter API を app auth で使ったメモ)
タイトル通り。
勉強会で Tweet をしてもらった後、それをみんなで見ながら「わいわい」するために 勉強会用のハッシュタグの Tweet を一覧化したかった。
公式の埋め込みタイムラインなどは、ハッシュタグに対応していない。
なので、自分で作りました。
今回の本題は、特定のハッシュタグの Tweets を取得するところ。
ライブラリなども揃っていて難しいことは無いけど、キーなどを生成する手順が(個人的に欲しいサイズで)まとまっていなかったので、書いておく。
やりたいのは、 Twitter API
のうち Standard Search API
を app auth
で使うこと。
これによって、無料の範囲でできるだけ高頻度に Tweets の検索を実行できる。
(15分の間に 450回 のリクエストが可能。2秒に一回のリクエストまで可能。)
これを使うためには、 Consumer API keys
の API key
と API secret key
、それらから生成する Bearer Token
の3つが必要となる。
目次
前提
$ serverless -v
Framework Core: 1.79.0
Plugin: 3.7.1
SDK: 2.3.1
Components: 2.34.6
$ node -v
v12.4.0
$ yarn -v
1.22.4
$
手順
Twitter API の各種キー取得・作成
Twitter Developer の アプリケーション一覧ページ の Create an app
から、アプリを作成する。
(詳しいのは検索すれば出てきます。)
作成できれば、 Keys and tokens
タブから必要な情報が得られる。
まずは、ここで Consumer API keys
の API key
と API secret key
を確認。
コンソールで curl
を使い Bearer Token
を生成。
curl -u "$API_KEY:$API_SECRET_KEY" \
--data 'grant_type=client_credentials' \
'https://api.twitter.com/oauth2/token'
結果、下記のような JSON が得られる。
{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAMLheAAAAAAA0%2BuSeid%2BULvsea4JtiGRiSDSJSI%3DEUifiRBkKG5E2XzMDjRfl76ZC9Ub0wnz4XsNiRVBChTYbJcE3F"}
これの "access_token"
の値を使う。
プロジェクト作成
$ serverless create --template aws-nodejs -p twhash
$ cd twhash
$ yarn add twitter
serverless.yml
service: twhash
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: ap-northeast-1
functions:
twhash:
handler: handler.twhash
events:
- http:
path: twhash/{hashTag}/{sinceId}
method: get
request:
parameters:
paths:
hashTag: true
sinceId: true
environment:
CONSUMER_KEY: 'YOUR_CONSUMER_KEY'
CONSUMER_SECRET: 'YOUR_CONSUMER_SECRET'
BEARER_TOKEN: 'YOUR_BEARER_TOKEN
hashTag
に検索したハッシュタグ(をURLエンコードしたもの)、 sinceId
に検索の起点となる Tweet の ID を指定する。
取得結果の中 search_metadata.max_id_str
を指定すると、取得後の新しい Tweets を取得できる。
handler.js
'use strict';
const Twitter = require('twitter')
const client = new Twitter({
consumer_key: process.env.CONSUMER_KEY,
consumer_secret: process.env.CONSUMER_SECRET,
bearer_token: process.env.BEARER_TOKEN
})
const getTweets = function (params) {
return new Promise((resolve, reject) => {
client.get('search/tweets', params, function(error, tweets, response) {
if (!error) {
resolve(tweets)
} else {
console.error(error)
reject(error)
}
});
})
}
module.exports.twhash = async event => {
const hashTag = decodeURIComponent(event.pathParameters.hashTag)
const sinceId = event.pathParameters.sinceId ? event.pathParameters.sinceId : '0'
console.log(hashTag, sinceId)
const params = {q: hashTag, since_id: sinceId, count: 100, result_type: 'recent'};
const tweets = await getTweets(params)
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(tweets)
}
}
getTweets()
でAPIから結果を取得し、そのまま返すだけのコード。
受け取ったフロントエンドで、自由に加工して表示すればOK。
結果
今回のイベントの “#ゆるWeb札幌” を検索してみる。
$ curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/dev/twhash/%23%E3%82%86%E3%82%8BWeb%E6%9C%AD%E5%B9%8C/0
{"statuses":[{"created_at":"Wed Aug 26 12:03:46 +0000 2020","id":1298591969879326700,"id_str":"1298591969879326723","text":"RT @tacck: ゆるWeb勉強会@札幌 OnLine #8 / #ゆるWeb札幌 - Togetter https://t.co/TLWa93Eke1 @togetter_jpより \n\n遅くなりましたが、8/24のまとめ作成しました!","truncated":false,"entities":{"hashtags":[{"text":"ゆるWeb札幌","indices":[35,43]}],"symbols":[],"user_mentions":[{"screen_name":"tacck","name":"t a c c k 🏡 Kihara, Takuya","id":6475652,"id_str":"6475652","indices":[3,9]},{"screen_name":"togetter_jp","name":"Togetter公式","id":72555864,"id_str":"72555864","indices":[79,91]}],"urls":
...(snip)...
,"search_metadata":{"completed_in":0.159,"max_id":1298591969879326700,"max_id_str":"1298591969879326723","next_results":"?max_id=1297858994791313411&q=%23%E3%82%86%E3%82%8BWeb%E6%9C%AD%E5%B9%8C&count=100&include_entities=1&result_type=recent","query":"%23%E3%82%86%E3%82%8BWeb%E6%9C%AD%E5%B9%8C","refresh_url":"?since_id=1298591969879326723&q=%23%E3%82%86%E3%82%8BWeb%E6%9C%AD%E5%B9%8C&result_type=recent&include_entities=1","count":100,"since_id":0,"since_id_str":"0"}}
$
このような感じで、手軽に検索結果を取れるようになる。
フロントはこのようなイメージで出せる。