web-dev-qa-db-fra.com

Évitez la limitation de l'API Twitter avec Tweepy

J'ai vu dans une question sur Stack Exchange que la limitation peut être fonction du nombre de demandes par 15 minutes et dépend également de la complexité de l'algorithme, sauf que ce n'est pas complexe.

J'utilise donc ce code:

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()

J'obtiens toujours l'erreur de limitation 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}]
28
4m1nh4j1

Le problème est que votre bloc try: except: Est au mauvais endroit. L'insertion de données dans la base de données ne soulèvera jamais un TweepError - c'est une itération sur Cursor.items() qui le fera. Je suggère de refactoriser votre code pour appeler la méthode next de Cursor.items() dans une boucle infinie. Cet appel doit être placé dans le bloc try: except:, Car il peut déclencher une erreur.

Voici (grosso modo) à quoi devrait ressembler le code:

# 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

Cela fonctionne parce que lorsque Tweepy lève un TweepError, il n'a mis à jour aucune des données du curseur. La prochaine fois qu'il fera la demande, il utilisera les mêmes paramètres que la demande qui a déclenché la limite de débit, la répétant effectivement jusqu'à ce qu'elle aille.

24
Aaron Hill

Pour toute personne qui tombe sur cela sur Google, tweepy 3.2+ a des paramètres supplémentaires pour la classe tweepy.api , en particulier:

  • wait_on_rate_limit - Attendre ou non automatiquement que les limites de taux se reconstituent
  • wait_on_rate_limit_notify - Imprimer ou non une notification lorsque Tweepy attend que les limites de taux se reconstituent

La définition de ces indicateurs sur True déléguera l'attente à l'instance d'API, ce qui est suffisant pour la plupart des cas d'utilisation simples.

65
Dan Nguyen

Si vous voulez éviter les erreurs et respecter la limite de débit, vous pouvez utiliser la fonction suivante qui prend votre objet api comme argument. Il récupère le nombre de demandes restantes du même type que la dernière demande et attend jusqu'à ce que la limite de débit soit réinitialisée si vous le souhaitez.

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
17
Till Hoffmann

Il suffit de remplacer

api = tweepy.API(auth)

avec

api = tweepy.API(auth, wait_on_rate_limit=True)
12
Mayank Khullar
import tweepy
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
# will notify user on ratelimit and will wait by it self no need of sleep.
api = tweepy.API(auth, wait_on_rate_limit=True, wait_on_rate_limit_notify=True)
1
Malik Faiq