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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

2624閲覧

Pythonでユーザを指定してTwitterのツイートを取得したい

gymgym

総合スコア97

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/07/23 07:55

Pythonで多くのユーザのアカウントから1週間分のツイートを取得したいと考えています。

Python

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

account1.csvには、900のユーザのアカウント名が格納されています。

途中までは、うまくいっているのですが、急に以下のようなエラーが出ます。

Traceback (most recent call last): File "get_tweet_acc.py", line 262, in <module> for tweet in getter.collect(total=10000): File "get_tweet_acc.py", line 80, in collect raise Exception('Twitter API error %d' % res.status_code) Exception: Twitter API error 401

原因はどのように考えられるのでしょうか。

よろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

https://developer.twitter.com/en/docs/basics/response-codes

401エラーは
"Missing or incorrect authentication credentials. This may also returned in other undefined circumstances."
ですね。

鍵付きアカウントやブロックされているアカウントのツイートを取得する時に起きます(普通は)。

投稿2018/07/24 00:54

quickquip

総合スコア11038

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問