質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

Q&A

解決済

1回答

3068閲覧

Twitter APIを使ってツイートを検索する

KenKenPaPPa

総合スコア24

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

0グッド

0クリップ

投稿2017/05/24 15:59

編集2017/05/25 03:15

###前提・実現したいこと
現在大学4年生のものです。
研究でツイッターのツイートを取得したいと思い、以下のブログを参考にプログラムを作りました。
http://ailaby.com/twitter_api/

一応データを取得はできたのですが、本来3000件のツイートを取得できるはずが、何回試しても30〜40件程度のツイートを入手したところでエラーがおきます。
原因が分からないので教えていただきたいです。

###補足
X-Rate-Limit-Remaining:アクセス可能回数 (アクセスするごとに -1 されていきます)
X-Rate-Limit-Reset:アクセスが可能になるまでの時間 (アクセス可能回数が 0 になっても、この値の時間まで待てば回数はリセットされます)

ここら辺のエラー処理をプログラムから除外すると、3000件取得できました。

###発生している問題・エラーメッセージ

Traceback (most recent call last): File "twitprint.py", line 242, in <module> for tweet in getter.collect(total = 3000): File "twitprint.py", line 107, in collect if(int(res.headers['X-Rate-Limit-Remainig']) ==0): File "/Library/Python/2.7/site-packages/requests/structures.py", line 54, in __getitem__ return self._store[key.lower()][1] KeyError: 'x-rate-limit-remainig'

###該当のソースコード

python

1#!/usr/bin/python python 2# -*- coding: utf-8 -*- 3 4from requests_oauthlib import OAuth1Session 5import json 6import datetime, time, sys 7from abc import ABCMeta, abstractmethod 8 9CK = 'v' # Consumer Key 10CS = '' # Consumer Secret 11AT = '' # Access Token 12AS = '' # Accesss Token Secert 13 14class TweetsGetter(object): 15 __metaclass__ = ABCMeta 16 17 def __init__(self): 18 self.session = OAuth1Session(CK, CS, AT, AS) 19 20 @abstractmethod 21 def specifyUrlAndParams(self, keyword): 22 ''' 23 呼出し先 URL、パラメータを返す 24 ''' 25 26 @abstractmethod 27 def pickupTweet(self, res_text, includeRetweet): 28 ''' 29 res_text からツイートを取り出し、配列にセットして返却 30 ''' 31 32 @abstractmethod 33 def getLimitContext(self, res_text): 34 ''' 35 回数制限の情報を取得 (起動時) 36 ''' 37 38 def collect(self, total = -1, onlyText = False, includeRetweet = False): 39 ''' 40 ツイート取得を開始する 41 ''' 42 43 #---------------- 44 # 回数制限を確認 45 #---------------- 46 self.checkLimit() 47 48 #---------------- 49 # URL、パラメータ 50 #---------------- 51 url, params = self.specifyUrlAndParams() 52 params['include_rts'] = str(includeRetweet).lower() 53 # include_rts は statuses/user_timeline のパラメータ。search/tweets には無効 54 55 #---------------- 56 # ツイート取得 57 #---------------- 58 cnt = 0 59 60 unavailableCnt = 0 61 while True: 62 res = self.session.get(url, params = params) 63 if res.status_code == 503: 64 # 503 : Service Unavailable 65 if unavailableCnt > 10: 66 raise Exception('Twitter API error %d' % res.status_code) 67 68 unavailableCnt += 1 69 print ('Service Unavailable 503') 70 self.waitUntilReset(time.mktime(datetime.datetime.now().timetuple()) + 30) 71 continue 72 73 unavailableCnt = 0 74 75 if res.status_code != 200: 76 raise Exception('Twitter API error %d' % res.status_code) 77 78 tweets = self.pickupTweet(json.loads(res.text)) 79 if len(tweets) == 0: 80 # len(tweets) != params['count'] としたいが 81 # count は最大値らしいので判定に使えない。 82 # ⇒ "== 0" にする 83 # https://dev.twitter.com/discussions/7513 84 break 85 86 for tweet in tweets: 87 if (('retweeted_status' in tweet) and (includeRetweet is False)): 88 pass 89 else: 90 if onlyText is True: 91 yield tweet['text'] 92 else: 93 yield tweet 94 95 cnt += 1 96 if cnt % 100 == 0: 97 print('%d件 ' % cnt) 98 99 if total > 0 and cnt >= total: 100 return 101 102 params['max_id'] = tweet['id'] - 1 103 104 #ヘッダ確認(回数制限) 105 #X-Rate-Limit_Remainingが入ってないことがあるので稀にチェック 106 if('X-Rate-Limit-Remaining' in res.headers and 'X-Rate-Limit-Reset' in res.headers): 107 if(int(res.headers['X-Rate-Limit-Remainig']) ==0): 108 self.waitUntilReset(int(res.headers['X-Rate-Limit-Reset'])) 109 self.checkLimit() 110 else: 111 print('not found - X-Rate-Limit-Remaining or X-Rate-Limit-Reset') 112 self.checkLimit() 113 114 def checkLimit(self): 115 ''' 116 回数制限を問い合わせ、アクセス可能になるまでwaitする 117 ''' 118 unavailableCnt = 0 119 while True: 120 url = "https://api.twitter.com/1.1/application/rate_limit_status.json" 121 res = self.session.get(url) 122 123 if res.status_code == 503: 124 #503 : Service Unavailable 125 if unavailableCnt > 10: 126 raise Exception('Twitter API error %d' % res.status_code) 127 128 unavailableCnt +=1 129 print('Service Unaveilable 503') 130 self.waitUntilReset(time.mktime(datetime.datetime.now().timetuple()) + 30) 131 continue 132 133 unavailableCnt = 0 134 135 if res.status_code != 200: 136 raise Exception('Twitter API error %d' % res.status_code) 137 138 remaining, reset = self.getLimitContext(json.loads(res.text)) 139 if(remaining == 0): 140 self.waitUntilReset(reset) 141 else: 142 break 143 144 def waitUntilReset(self,reset): 145 146 # reset 時刻まで sleep 147 148 149 seconds = reset - time.mktime(datetime,datetime.now().timetuple()) 150 seconds = max(seconds,0) 151 print('\n =====================') 152 print(' == waiting %d sec ==' % seconds) 153 print(' =====================') 154 sys.stdout.flush() 155 time.sleep(seconds + 10) #念のため+10秒 156 157 @staticmethod 158 def bySearch(keyword): 159 return TweetsGetterBySearch(keyword) 160 161 @staticmethod 162 def byUser(screen_name): 163 return TweetsGetterByUser(screen_name) 164 165 166class TweetsGetterBySearch(TweetsGetter): 167 ''' 168 キーワードでツイートを検索 169 ''' 170 def __init__(self, keyword): 171 super(TweetsGetterBySearch, self).__init__() 172 self.keyword = keyword 173 174 def specifyUrlAndParams(self): 175 176 # 呼出し先 URL、パラメータを返す 177 url = 'https://api.twitter.com/1.1/search/tweets.json' 178 params = {'q':self.keyword, 'count':100} 179 return url, params 180 181 def pickupTweet(self, res_text): 182 183 #res_text からツイートを取り出し、配列にセットして返却 184 results = [] 185 for tweet in res_text['statuses']: 186 results.append(tweet) 187 188 return results 189 190 def getLimitContext(self, res_text): 191 192 #回数制限の情報を取得 (起動時) 193 remaining = res_text['resources']['search']['/search/tweets']['remaining'] 194 reset = res_text['resources']['search']['/search/tweets']['reset'] 195 196 return int(remaining), int(reset) 197 198class TweetsGetterByUser(TweetsGetter): 199 ''' 200 ユーザーを指定してツイートを取得 201 ''' 202 def __init__(self, screen_name): 203 super(TweetsGetterByUser, self).__init__() 204 self.screen_name = screen_name 205 206 def specifyUrlAndParams(self): 207 ''' 208 呼出し先 URL、パラメータを返す 209 ''' 210 url = 'https://api.twitter.com/1.1/statuses/user_timeline.json' 211 params = {'screen_name':self.screen_name, 'count':200} 212 return url, params 213 214 def pickupTweet(self, res_text): 215 ''' 216 res_text からツイートを取り出し、配列にセットして返却 217 ''' 218 results = [] 219 for tweet in res_text: 220 results.append(tweet) 221 222 return results 223 224 def getLimitContext(self, res_text): 225 226 #回数制限の情報を取得 (起動時) 227 228 remaining = res_text['resources']['statuses']['/statuses/user_timeline']['remaining'] 229 reset = res_text['resources']['statuses']['/statuses/user_timeline']['reset'] 230 231 return int(remaining), int(reset) 232 233if __name__ == '__main__': 234 235 # キーワードで取得 236 getter = TweetsGetter.bySearch(u'渋谷') 237 238 # ユーザーを指定して取得 (screen_name) 239 #getter = TweetsGetter.byUser('AbeShinzo') 240 241 cnt = 0 242 for tweet in getter.collect(total = 3000): 243 cnt += 1 244 print ('------ %d' % cnt) 245 print ('{} {} {}'.format(tweet['id'], tweet['created_at'], '@'+tweet['user']['screen_name'])) 246 print (tweet['text']) 247

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tk1024

2017/05/24 17:41

Consumer Key / Consumer Secret / Access Token / Accesss Token Secert などは悪用できるので隠した方が良いと思います
退会済みユーザー

退会済みユーザー

2017/05/24 23:12

悪用できる。ではなくて、されるですね^^;隠しても履歴が残るので再取得して今のものは無効にして下さい。また、各種キーの取扱の出来ない人はAPIを触ってはいけません。まず各種ドキュメントを読むところから始めて下さい。
KenKenPaPPa

2017/05/25 03:23

あああああ、やっちゃいました、ご指摘ありがとうございます。以後気をつけます。
guest

回答1

0

ベストアンサー

エラーの原因は、単純なtypoです。

修正前

Python

1 if(int(res.headers['X-Rate-Limit-Remainig']) ==0):

修正後

Python

1 if(int(res.headers['X-Rate-Limit-Remaining']) ==0):

Remaining の綴りで、最後の 'n' が抜けております。

投稿2017/05/25 02:20

magichan

総合スコア15898

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問