Избегайте ограничений API Twitter с помощью Tweepy
В каком-то вопросе на Stack Exchange я заметил, что ограничение может быть функцией количества запросов за 15 минут и зависит также от сложности алгоритма, за исключением того, что это не сложно.
Итак, я использую этот код:
import tweepy
import sqlite3
import time
db = sqlite3.connect('data/MyDB.db')
# Get a cursor object
cursor = db.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS MyTable(id INTEGER PRIMARY KEY, name TEXT, geo TEXT, image TEXT, source TEXT, timestamp TEXT, text TEXT, rt INTEGER)''')
db.commit()
consumer_key = ""
consumer_secret = ""
key = ""
secret = ""
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(key, secret)
api = tweepy.API(auth)
search = "#MyHashtag"
for tweet in tweepy.Cursor(api.search,
q=search,
include_entities=True).items():
while True:
try:
cursor.execute('''INSERT INTO MyTable(name, geo, image, source, timestamp, text, rt) VALUES(?,?,?,?,?,?,?)''',(tweet.user.screen_name, str(tweet.geo), tweet.user.profile_image_url, tweet.source, tweet.created_at, tweet.text, tweet.retweet_count))
except tweepy.TweepError:
time.sleep(60 * 15)
continue
break
db.commit()
db.close()
Я всегда получаю ошибку ограничения Twitter:
Traceback (most recent call last):
File "stream.py", line 25, in <module>
include_entities=True).items():
File "/usr/local/lib/python2.7/dist-packages/tweepy/cursor.py", line 153, in next
self.current_page = self.page_iterator.next()
File "/usr/local/lib/python2.7/dist-packages/tweepy/cursor.py", line 98, in next
data = self.method(max_id = max_id, *self.args, **self.kargs)
File "/usr/local/lib/python2.7/dist-packages/tweepy/binder.py", line 200, in _call
return method.execute()
File "/usr/local/lib/python2.7/dist-packages/tweepy/binder.py", line 176, in execute
raise TweepError(error_msg, resp)
tweepy.error.TweepError: [{'message': 'Rate limit exceeded', 'code': 88}]
Ответы
Ответ 1
Проблема в том, что ваш блок try: except:
находится в неправильном месте. Вставка данных в базу данных никогда не приведет к увеличению TweepError
- итерации по Cursor.items()
, которые будут. Я бы предложил рефакторинг вашего кода, чтобы вызвать метод next
Cursor.items()
в бесконечном цикле. Этот вызов должен быть помещен в блок try: except:
, так как он может вызвать ошибку.
Здесь (примерно), как выглядит код:
# above omitted for brevity
c = tweepy.Cursor(api.search,
q=search,
include_entities=True).items()
while True:
try:
tweet = c.next()
# Insert into db
except tweepy.TweepError:
time.sleep(60 * 15)
continue
except StopIteration:
break
Это работает, потому что, когда Tweepy повышает значение TweepError
, он не обновил ни одного из данных курсора. В следующий раз, когда он сделает запрос, он будет использовать те же параметры, что и запрос, который вызвал ограничение скорости, эффективно повторяя его до тех пор, пока он не пройдет.
Ответ 2
Для всех, кто наткнулся на это в Google, tweepy 3.2+ имеет дополнительные параметры для класса tweepy.api, в частности:
-
wait_on_rate_limit
- Независимо от того, будете ли вы автоматически ждать ограничения скорости, чтобы пополнить
-
wait_on_rate_limit_notify
- следует ли распечатать уведомление, когда Tweepy ожидает, чтобы ограничения по скорости были пополнены.
Установка этих флагов в True
будет делегировать ожидание экземпляру API, что достаточно хорошо для большинства простых случаев использования.
Ответ 3
Если вы хотите избежать ошибок и соблюдать ограничение скорости, вы можете использовать следующую функцию, которая принимает ваш объект api
в качестве аргумента. Он извлекает количество оставшихся запросов того же типа, что и последний запрос, и ждет, пока ограничение скорости будет reset, если это необходимо.
def test_rate_limit(api, wait=True, buffer=.1):
"""
Tests whether the rate limit of the last request has been reached.
:param api: The `tweepy` api instance.
:param wait: A flag indicating whether to wait for the rate limit reset
if the rate limit has been reached.
:param buffer: A buffer time in seconds that is added on to the waiting
time as an extra safety margin.
:return: True if it is ok to proceed with the next request. False otherwise.
"""
#Get the number of remaining requests
remaining = int(api.last_response.getheader('x-rate-limit-remaining'))
#Check if we have reached the limit
if remaining == 0:
limit = int(api.last_response.getheader('x-rate-limit-limit'))
reset = int(api.last_response.getheader('x-rate-limit-reset'))
#Parse the UTC time
reset = datetime.fromtimestamp(reset)
#Let the user know we have reached the rate limit
print "0 of {} requests remaining until {}.".format(limit, reset)
if wait:
#Determine the delay and sleep
delay = (reset - datetime.now()).total_seconds() + buffer
print "Sleeping for {}s...".format(delay)
sleep(delay)
#We have waited for the rate limit reset. OK to proceed.
return True
else:
#We have reached the rate limit. The user needs to handle the rate limit manually.
return False
#We have not reached the rate limit
return True