Twitter API を使ってみる


Twitter API の使い方などの説明です (2011/4/12 新規作成)。

API / 種類 / 認証 / REST API / Streaming API / User Streams / エラー / 戻る / トップページ


Twitter API

Twitter は、 ユーザーが「ツイート」と呼ばれる 140 文字のつぶやきを投稿し、 閲覧できるコミュニケーション・サービスです。
 
Twitter API は、そのツイートの変更や参照・検索をプログラムから 扱えるようにする公式の API サービスです。 世の中に数多ある Twitter クライアントや、連携サービスも Twitter API を使って作成されており、相当のことができます。
 
Twitter API の解説をしている Web ページはそれなりにありますが、 API の仕様や周辺事情が色々と変化しているため、2011/4 時点で 私が調べた情報をまとめなおしたものをここに載せようと思います。

API の種類

Twitter API のドキュメント類は本家サイトで提供されています。 API Documentation dev.twitter.com がそのサイトです。 以前は Wiki ページで管理されていましたが、こちらのサイトが最新です。
 
Twitter API は大きく分けて、 REST APIStreaming API、 Search API の3種類があります。 REST API はツイートの更新や参照を行う最も基本的な API で、 URL にクエリーを渡してレスポンスを得る REST を使います。 Search API はツイートを検索する API です。 これも REST を使いますが、 特定のアカウントに関する操作ではないためログイン認証が不要です。 Streaming API は比較的新しい API で、タイムラインの変更をリアルタイムに 受け取れるものです。 これも実際には REST でアクセスしますが、タイムラインが更新されるまで レスポンスを返すのを保留させる (ロングポーリングと呼びます) ことで、 リアルタイム性を確保しています。
 
またこれら以外に、API ではないのですが、 自分の Web ページに Twitter 関係の フォローボタン・ツイートボタン・ウィジェットを載せたいのであれば こちら から ブログパーツ (スクリプト) を入手するだけで目的が達成できます。 他に JavaScript でフォローする・ツイートするパーツを Web ページに 埋め込む簡易ライブラリの @anywhere もあります。 また、さらに 2011/4 から Web Intents という 自分の Web ページから、 ツイートする・返信する・リツイートする・お気に入りにする、 を開くリンクを貼る仕組みも公開されました。 これらでやりたいことが達成できるのであれば、これらを使う方が簡単です。
 
REST API、Search API、@anywhere には 1時間に同じ人(IPアドレスで判定)が 150 回までとの API 呼び出し回数の制限があります。 後述する認証を行えば、Access Token ごとに 350 回になります。 この数値も時期と共に変動しており、サーバー負荷が深刻になった時に 一時的に引き下げられたこともあります。 こちら で 最新の制限を確認すると良いです。 なお、一部の開発者にはより緩い制限を許す制度もあったようですが、 今は受け付けられていません。

認証

Twitter API の多く (特定のアカウントに関する情報を扱う場合) は 認証を必要とします。従来 Basic 認証が使われていましたが、 セキュリティ強化のため、現在では OAuth 認証がデフォルトとなっています。 Basic 認証はすでに廃止されているので、面倒でも OAuth 認証を使いましょう。
 
Twitter API で OAuth 認証を使うには、 Consumer key, Consumer secret の取得が必要です。 また、BOT のように、クライアントアプリケーションから 特定のアカウントを操作する場合は、 Access Token, Access Token Secret の準備も必要になります。 ここでは、BOT から Twitter API を扱う場合について説明します。
 
Consumer key, Consumer secret の取得のために、 開発者ページでアプリケーションを登録します。 Twitterアプリケーション の ページに自分の Twitter アカウントでログインし (BOT を作成する場合、 後述する Access Token の情報をすぐ得られるように BOT のアカウントで ログインした方が楽です)、 「新しいアプリケーションを登録する」ボタンから 作成するアプリケーションの情報を入力して登録します。 この時、BOT 自体は単体のプログラムとして動作するので 「アプリケーションの種類」は「クライアントアプリケーション」を、 「標準のアクセスタイプ」はツイートを発する (データを書き込む) ので 「Read & Write」を選びます。 正しい情報を入力して「アプリケーションを登録する」ボタンを押すと、 すぐに登録は完了し、 Consumer key, Consumer secret が表示されている画面がでるはずです。
 
Access Token, Access Token Secret はアカウント毎の情報なので、 BOT の場合は BOT のアカウントでログインして、 さきほどのアプリケーション情報の画面の右に表示されている 「My Access Token」ボタンを押すと、参照できます。 以前はここも自分でプログラムを書いて取得する必要がありましたが、 今は Web 上で簡単に参照できるようになってます。
 
OAuth 認証は複雑ですが、クライアントアプリケーションの場合は API にアクセスする際に、ヘッダー名 Authorization に必要情報を 詰め込めば認証できます。 その情報を作るのが手間ですが、ライブラリを利用するなりすれば良いでしょう。 ここでは、私が作った Python のヘッダー生成コードを載せておきます。 以後のコードは全て仕組みを理解するために、 Python の標準機能のみを使ってます。
	import urllib
	import hmac, sha
	import time, random
	
	# この関数の返り値を Authorization ヘッダーに設定する
	def getOAuthHeader(url,method,params):
		# 事前に確保した consumerKey, consumerSecret, accessToken, accessTokenSecret が設定されていること
		
		oAuthParams = {
			"oauth_consumer_key"     : consumerKey,
			"oauth_signature_method" : "HMAC-SHA1",
			"oauth_timestamp"        : str(int(time.time())),
			"oauth_nonce"            : str(random.getrandbits(64)),
			"oauth_version"          : "1.0",
			"oauth_token"            : accessToken
		}
		
		allParams=params.copy()
		allParams.update(oAuthParams)
		allParamList=[]
		for key in sorted(allParams):
			allParamList.append(key+"="+allParams[key])
		
		msg=method+"&"+urllib.quote(url,"")+"&"+urllib.quote("&".join(allParamList),"~")
		h=hmac.new(consumerSecret+"&"+accessTokenSecret,msg,sha)
		oAuthParams["oauth_signature"]=h.digest().encode("base64").strip()
		
		oAuthParamsList=[]
		for key in oAuthParams:
			oAuthParamsList.append(key+"=\""+urllib.quote(oAuthParams[key])+"\"")
		
		return "OAuth "+(", ".join(oAuthParamsList))
 
シグネチャ oauth_signature の生成が一手間です。 HTTP リクエストメソッド (GET か POST か) と、リクエスト URL、 API に送るパラメータ (ここでは引数 params に渡される辞書) の アルファベット順ソート、を URI エスケープしつつ記号 & で接続し、 その文字列を HMAC-SHA1 によってダイジェスト値を生成し、 BASE64 でエンコードします。 シグネチャの key は Consumer secret と Access Token Secret を & 記号で接続したものです。 この時、oauth_ で始まる OAuth 用のパラメータをいくつか含ませます。
 
この生成情報をミスしていると (認証に失敗すると)、 { "error" : "Incorrect signature" } とのエラーメッセージが返ってきます。 空白文字は + ではなく %2b に、~ (チルダ) はそのまま ~ に エスケープするのが正解のようです。
 
なお、アイコンの更新などでマルチパートでリクエストを送信することが ありますが、その場合は API に送るパラメータにはそれらの情報は 含みませんのでご注意下さい (私はそこで苦労しました...)。

REST API 基本パターン

まずは REST API から。 ドキュメントを参照して、HTTP リクエストメソッドが GET か POST か、 認証が必須かどうか、必須パラメータは何かを確認します。
 
例えば、ツイートをする API は statuses/update で、 各項目を確認すると、"Supported request methods" から POST でのアクセスで、 "Requires Authentication" が true なので認証必須、 "Parameters Required" から必須パラメータは status (ツイートする内容を書くところ) と分かります。
 
なお、"Requires Authentication" が false でも人によって参照できる 情報が違うようなものでは、認証の有無で結果が変わってきます。 例えば、リストのタイムラインを表示する :user/lists/:id/statuses では、 非公開のリストは認証ありでアクセスしないと、 404 Not found エラーになります。
	import urllib, urllib2
	import mimetypes
	
	# 他の API でも使えるように汎用化した関数
	def getAPIRequest(url,method,authentication,params={}):
		multipart=False
		
		newParams={}
		for key in params:
			value=params[key]
			if value==None:
				pass
			elif isinstance(value,int):
				newParams[key]=str(value)
			elif isinstance(value,float):
				newParams[key]=str(value)
			elif isinstance(value,str):
				newParams[key]=urllib.quote(value,"~")
			elif isinstance(value,unicode):
				newParams[key]=urllib.quote(value.encode("utf-8"),"~")
			elif isinstance(value,tuple):
				newParams[key]=value
				multipart=True
		params=newParams
		
		url=url.encode("utf-8")
		
		if method=="GET":
			paramList=[]
			for key in params:
				paramList.append(key+"="+params[key])
			request=urllib2.Request(url+"?"+"&".join(paramList))
		else:
			request=urllib2.Request(url)
		
		if authentication:
			# OAuth 対応のために必要なのはここだけ
			if not multipart:
				header=getOAuthHeader(url,method,params)
			else:
				header=getOAuthHeader(url,method,{})
			request.add_header("Authorization",header)
		
		if method=="POST":
			if not multipart:
				data=[]
				for key in params:
					data.append(key+"="+params[key])
				request.add_data("&".join(data))
			else:
				BOUNDARY="---AntunBoundaryBoundaryBoundary"
				data=""
				
				for key in params:
					data+="--"+BOUNDARY+"\r\n"
					
					if not isinstance(params[key],tuple):
						data+="Content-Disposition: form-data; name=\""+str(key)+"\""+"\r\n"
						data+="\r\n"
						data+=str(params[key])+"\r\n"
					else:
						fileType=mimetypes.guess_type(params[key][0])[0]
						assert fileType!=None,"Could not determine file type"
						
						data+="Content-Disposition: form-data; name=\""+str(key)+"\"; filename=\""+str(params[key][0])+"\""+"\r\n"
						data+="Content-Type: "+str(fileType)+"\r\n"
						data+="\r\n"
						data+=str(params[key][1])+"\r\n"
				
				data+="--"+BOUNDARY+"--"+"\r\n"
				
				request.add_header("Content-Type","multipart/form-data; boundary="+BOUNDARY)
				request.add_header("Content-Length",str(len(data)))
				request.add_data(data)
		else:
			assert multipart==False
		
		return request
	
	# statuses/update 固有の処理はこんなにシンプル
	def updateStatuses(tweet):
		params={
			"status" : tweet,
		}
		request=getAPIRequest("http://api.twitter.com/1/statuses/update.json","POST",True,params)
		stream=urllib2.urlopen(request)
		stream.read()
		stream.close()
一般的な REST のように、必要なパラメータ status を渡して API の URL に アクセスするだけです。ここでは POST アクセスなので、パラメータは URL にではなく、POST データで渡します。 なお、ほぼ全ての API で URL の末尾を .json にすれば JSON で、 .xml にすれば XML でレスポンスが得られます。 ただし API ごとにどのフォーマットが指定可能か決まっているので、 注意しましょう (.json のみのものがあるので、 JSON を使うのがベターなようです)。
 
また、パラメータで日本語を扱う場合は UTF-8 で扱います。 API の中には URL に名称を指定するものがあり、 例えばリストのメンバーを取得する :user/:list_id/members では、ユーザー名とリスト名を URL に渡しますが、 日本語のリスト名もあります。 このような場合も、文字列を UTF-8 で表現した後にエンコードすることで 指定ができます (扱えなかった時期がありましたが、 今は扱えるようになってます)。 ...でしたが、そのような API はパラメータに情報を引き渡すものが用意され、 廃止される流れになっているようです (例: :user/:list_id/members ⇒ lists/members)。

Streaming API 基本パターン

次に Streaming API。 Streaming API は、基本は REST API と同じで、 タイムラインの変更をリアルタイムに受け取るために、 タイムラインが更新されるまでレスポンスが返ってこない点が違います。 一度に何本も接続できない、切断と接続を短期間に連続できないなどの 制限はあるものの、Streaming API は、 API の呼び出し回数制限がかからない利点があります。
 
Streaming API でおそらく一番良く使われるのは User Streams で、 認証したユーザーのタイムラインの内容+αが流れます ( ちなみに指定のユーザーや検索語を流したい場合は statuses/filter を使います)。
	import json
	
	def getUserStreams():
		request=getAPIRequest("https://userstream.twitter.com/2/user.json","GET",True,{})
		
		return urllib2.urlopen(request,timeout=90.0)
	
	stream=self.twitter.getUserStreams()
	
	response=u""
	# 1行目のフォロワーリストを読み飛ばし (User Stream の場合だけ)
	while True:
		c=stream.read(1)
		if c==u"\n":
			break
		response+=c
	
	while True:
		# readline() でも一応動きますが、仕様 (関数の実装) 上、正確なリアルタイムでレスポンスが得られなくなるので、read() で読んでます
		response=u""
		while True:
			c=stream.read(1)
			if c==u"\n":
				break
			response+=c
		response=response.strip()
		
		# 接続維持のために、時々空行が送られてくる
		if response=="":
			continue
		
		response=json.loads(unicode(response),"utf-8")
		print(json.dumps(response,indent=4,ensure_ascii=False))
処理自体は REST API と同様に OAuth で認証しつつ、 コネクションを張ってレスポンスを待ちます。 レスポンスが長期間保留されるため、タイムアウト処理をしないように 呼び出し側の処理を気をつけなければいけません。
 
Streaming API は比較的新しいことから、API のアドレスの変更が激しいです。 http://stream.twitter.com/spritzer.json, http://chirpstream.twitter.com/2b/user.json, http://betastream.twitter.com/2b/site.json, http://betastream.twitter.com/1/statuses/filter.json などはすでに古く、 新しいアドレスに変更されているので、注意です。
 
なお、全てのツイートを対象とする、statuses/firehose, statuses/links, statuses/retweet, Site Streams はごく一部の限られた 開発者にのみ提供されており、通常は使えません。

User Streams

User Streams は他の Streaming API とは違い、ツイート以外にも、 自分がした、または自分を対象に他人がした、 リツイートやお気に入り登録・ツイートの削除などもリアルタイムに 流れてくる点が特殊です。
 
接続すると、一行目にフォロー相手のユーザー ID のリスト ({"friends":[1497,169686021,...]} のような JSON 形式) がまず返ってきます。 以後は、タイムラインにツイートが流れてくるか、 お気に入り登録などのイベントが起こるまではレスポンスは保留されます。 ただし、接続維持のために時々空行が送られてきますので、 逆に 90 秒以上何も返ってこない場合は切断する処理も必要です。
 
通常のタイムラインに流れるようなツイート/リツイート情報は、 API の statuses/home_timeline と 同じ形式で 1 ツイートごとに返ってきます。 お気に入り登録などのイベントの場合は、 "event" (イベントの種類)、"source" (イベントを引き起こした人)、 "target" (イベントの対象の人)、 "target_object" (人以外のイベント対象の情報)、 "created_at" (イベントが起こされた時間) の キーを持った情報が返ってきます (イベントの種類ごとに どれが返ってくるかは違う)。 そのためまず "event" キーを探して、 あれば各イベントとして、 なければツイートとして処理します。
 
イベントの種類
event キーの値 イベント内容 source target target_object created_at 備考
follow フォロー フォローした人 フォローされた人 なし フォローした時間
unfollow アンフォロー アンフォローした人 (=自分) アンフォローされた人 なし アンフォローした時間 他人からされた時は来ない
user_update 自分のプロフィールを更新した プロフィールを更新した人 (=自分) プロフィールが更新された人 (=自分) なし プロフィールを更新した時間
block ブロック ブロックした人 ブロックされた人 なし ブロックした時間
unblock ブロック解除 (未確認) (未確認) (未確認) (未確認)
favorite お気に入りに登録 登録した人 登録された人 登録されたツイート 登録した時間
unfavorite お気に入りから外す 外した人 外された人 外されたツイート 外した時間
list_created 自分がリストを新規作成した リストを作成した人 (=自分) リストが作成された人 (=自分) 作成されたリストの情報 リストを作成した時間 非公開リストの情報はこない
list_destroyed 自分がリストを削除した リストを削除した人 (=自分) リストが削除された人 (=自分) 削除されたリストの情報 リストを削除した時間 非公開リストの情報はこない
list_member_added リストに追加 リストに登録した人 リストに登録された人 登録されたリストの情報 リストに登録した時間 非公開リストの情報はこない
list_member_removed リストから外す リストから外した人 リストから外された人 外された後のリストの情報 リストから外した時間 非公開リストの情報はこない
list_updated リストの情報が更新された リストの情報を更新した人 更新されたリストの持ち主 (=source) 更新されたリストの情報 リストを更新した時間 非公開リストの情報はこない
他人のフォローしたリストの場合は未確認
list_user_subscribed リストをフォローした リストをフォローした人 リストをフォローされた人 フォローされたリストの情報 リストをフォローした時間
list_user_unsubscribed リストをフォロー解除した リストをフォロー解除した人 リストをフォロー解除された人 フォロー解除されたリストの情報 リストをフォロー解除した時間
access_revoked アプリ連携の許可を取り消した 許可を取り消した人 許可を取り消されたアプリの作者 取り消されたアプリとトークンの情報 許可を取り消した時間 他人に取り消された場合は未確認 (たぶん来ない)
access_unrevoked アプリ連携の許可を取り消しを止めた 許可の取り消しを止めた人 許可の取り消しを止められたアプリの作者 取り消しを止めたアプリとトークンの情報 許可の取り消しを止めた時間 他人に取り消された場合は未確認 (たぶん来ない)
 
ツイートとして処理する場合も以下の 5 パターンがあるようです。
 
ツイートの種類
識別方法 種類 備考
retweeted_status キーがある リツイート タイムラインにリツイートを表示しない設定にしているユーザーのリツイートも流れてくる
direct_message キーがある ダイレクトメッセージ direct_message.sender.* から direct_message.recipient.* に direct_message.* のメッセージ
delete キーがある ツイート削除 delete.status.user_id が delete.status.id のツイートを消した
リツイート元ツイートの削除、リツイートの取り消しもこれに含まれる (両者の区別は付かない)
ダイレクトメッセージの削除はこないらしい
scrub_geo キーがある 位置情報の削除 scrub_geo.user_id が scrub_geo.up_to_status_id のツイートの位置情報を消した
設定画面の「全ての位置情報を削除する」で発生
その他 ツイート
 
User Streams 以外の Streaming API でも、delete は流れてくるようです。

エラー処理

エラーが発生した場合、API は HTTP のステータス番号と同時に レスポンスで詳しい情報を返してくれます (API 1.1 以降)。
 
詳細なエラー内容は {"errors":[{"message":"Sorry, that page does not exist","code":34}]} のような JSON 形式で返ってきます。 エラー番号は Error Codes & Responses Twitter Developers に一覧がありますが、実際にはもっと種類があるようです。
 
エラー番号
エラー番号 エラーメッセージ 内容
34 Sorry, that page does not exist 指定された API が存在しない。 HTTP の 404 エラー相当。 API のアドレスが間違っていないか確認しましょう。 account/update_profile_image など問題修正までに時間が掛かる場合に、 一時的にこの状態にされていることがあります。
67 Backend service is unavailable Twitter のバックエンドシステムが落ちている ?
88 Rate limit exceeded API の利用回数制限に達した。 HTTP の 429 エラー相当。 X-Rate-Limit-Reset ヘッダーで示された UTC 時間まで待ちましょう。
93 This application is not allowed to access or delete your direct messages 許可されていないダイレクトメッセージアクセス。 HTTP の 403 エラー相当。 My applications の アクセスレベル設定でダイレクトメッセージが許可されているか確認しましょう。
130 Over capacity Twitter のシステムが限界に達している。 HTTP の 503 エラー相当。 しばらく待ちましょう。
131 Internal error Twitter のシステムの原因不明の内部エラー。 HTTP の 500 エラー相当。 しばらく待ってみて、治りそうにない場合は Twitter API のサポート掲示板 で指摘しましょう。
135 Could not authenticate you OAuth の認証に失敗している。 HTTP の 401 エラー相当。 oauth_timestamp がずれていないか、Authorization ヘッダーの生成が間違っていないか見直しましょう。
150 You cannot send messages to users who are not following you ダイレクトメッセージ指定のツイートができない。 HTTP の 403 エラー相当。 ツイートの頭に特定のテキストがあるとコマンドとして認識 されますが、「D フォローされてない人」や「M フォローされてない人」を 使おうとした時にこのエラーになります。 ツイートの頭の文を変えましょう。
185 User is over daily status update limit 1日のツイート数制限に達した。 HTTP の 403 エラー相当。 しばらく待ちましょう。 88, 191 番のようには、復旧時間は分かりません。 公式サポート によると、2〜3時間待つべきです。
186 Status is over 140 characters ツイートが 140 文字を超えている。 HTTP の 403 エラー相当。 ツイートを 140 文字以下に抑えましょう。
187 Status is a duplicate 重複ツイートしようとしている。 HTTP の 403 エラー相当。 すでにツイート済みのテキストを送っているので、ツイートをやめましょう。
189 Error creating status 原因不明。 HTTP の 403 エラー相当。 statuses/update_with_media でまれに発生。 少し間を置いてもう一度 API を呼び出すと成功するようです。
190 Status creation failed: ツイートが長すぎます. ツイートが長すぎる。 HTTP の 403 エラー相当。 ツイート中に URL が含まれており t.co に変換した結果、140 文字を超えるとおそらく 186 番ではなく、こちらが出るのだと思われます。 URL 込みのツイートを 140 文字以下に抑えましょう。
191 User アカウント名 is over daily photo limit 画像アップロード数の制限に達した。 HTTP の 403 エラー相当。 X-MediaRateLimit-Reset ヘッダーで示された UTC 時間まで待ちましょう (ツイート数制限に引っかかった時は復旧時間は分からない)。
215 Bad Authentication data OAuth 認証に失敗した。 HTTP の 400 エラー相当。 リクエストの Authorization ヘッダーを正しくしましょう。

戻る