kivantium活動日記

プログラムを使っていろいろやります

TwitterのStreaming APIについて

2018年8月にTwitterのUser Streams APIが廃止されてしまい、タイムラインのツイートをリアルタイムで取得することができなくなってしまいました。 forest.watch.impress.co.jp

それに伴い、User Streams APIを使っていたリプライによるアイコン変更スクリプトなどが動かなくなっていました。

昨日から開発を始めた画像収集スクリプトを作るためにTweepyのドキュメントを眺めていたら、Streaming APIというものが現在も存在していることに気がついたのでこれを紹介します。

(2015年からTweepyに実装されていたのに今日まで気づかなかった……。ドキュメントはちゃんと読むべき。)

statuses/filterの仕様

2020年4月15日現在、Twitter APIの紹介ページでタイトルにrealtimeとつくものにはFilter realtime TweetsSample realtime Tweetsの2つがあります。このうち、無料ユーザーが使えるのはstatuses/filterだけっぽいです。

developer.twitter.com

このAPIを使うと特定の条件を満たすツイートをリアルタイムで取得できます。パラメータは5つあります。

  • follow: ユーザーIDのリスト(上限5000個)
  • track: 検索キーワード(上限400個)
  • locations: ツイートされた場所(上限25個)
  • delimited: メッセージを長さで区切るかどうか
  • stall_warnings: stall warningsの有無

follow, track, locationsから複数を指定した場合、ORでつないだものとみなされます。

followで指定したユーザーについて、以下を満たすツイートが取得できます。(カッコ内は原文)

  • 指定したユーザーによるツイート (Tweets created by the user.)
  • 指定したユーザーがリツイートしたツイート (Tweets which are retweeted by the user.)
  • 指定したユーザーの任意のツイートに対するリプライ (Replies to any Tweet created by the user.)
  • 指定したユーザーの任意のツイートのリツイート (Retweets of any Tweet created by the user.)
  • リプライボタンを押さずに行われた手動リプライ (Manual replies, created without pressing a reply button (e.g. “@twitterapi I agree”).)

しかし、以下のツイートは含まれません。

  • 指定したユーザーにメンションしているツイート (Tweets mentioning the user (e.g. “Hello @twitterapi!”).)
  • リツイートボタンを押さずに行われた手動リツイート (Manual Retweets created without pressing a Retweet button (e.g. “RT @twitterapi The API is great”).)
  • 非公開アカウントによるツイート (Tweets by protected users.)

特に最後の「非公開アカウントによるツイート」の制約は厳しく、実験した限りではフォローしている鍵アカウントのツイートであっても取得することができません

Tweepyでの使い方

Streaming With Tweepyを読んでください。

followに自分がフォローしているアカウントと自分を指定して、取得できたツイートに含まれる画像を保存するスクリプトの例を示します。

# -*- coding: utf_8 -*-

import os
import sys
import urllib.request
from urllib.parse import urlparse
import tweepy


class MyStreamListener(tweepy.StreamListener):
    def on_status(self, status):
        if 'media' in status.entities:
            for media in status.extended_entities['media']:
                media_url = media['media_url']
                filename = os.path.basename(urlparse(media_url).path)
                if not os.path.exists(filename):
                    try:
                        urllib.request.urlretrieve(media_url, filename)
                        print("Saved :", filename)
                    except IOError:
                        print("Failed to save the image from", media_url, file=sys.stderr)


consumer_key = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
consumer_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
access_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_secret)
api = tweepy.API(auth)

followee_ids = api.friends_ids(screen_name=api.me().screen_name)
watch_list = [str(user_id) for user_id in followee_ids]
watch_list.append(str(api.me().id))

# followには5000人までしか指定できない
# https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
assert(len(watch_list) <= 5000)

myStreamListener = MyStreamListener()
myStream = tweepy.Stream(auth=api.auth, listener=myStreamListener)
myStream.filter(follow=watch_list)

広告コーナー