Comme vous le savez, lorsque vous recevez des tweets avec l'API Twitter, le nombre de fois où vous les recevez sur une certaine période de temps et le nombre de tweets que vous recevez à la fois sont limités.
Afin de bien gérer cette limitation, voici quelques conseils pour l'obtenir en continu. Veuillez noter que cet article ne couvre pas le POST. (Bien que ce soit presque la même chose)
Assurez-vous de vérifier les informations principales pour les valeurs numériques. L'exemple de code est publié sur Gist. (J'ai entendu dire qu'il existe un package Twitter, mais je ne l'utilise pas)
Les limites sont les suivantes.
GET endpoints
The standard API rate limits described in this table refer to GET (read) endpoints. Note that endpoints not listed in the chart default to 15 requests per allotted user. All request windows are 15 minutes in length. These rate limits apply to the standard API endpoints only, does not apply to premium APIs.
Extrait de Limites de taux - Développeurs Twitter
Il y a une limite au nombre d'acquisitions par unités de 15 minutes. Selon cette page, par exemple, les restrictions pour «« recherche / tweets »» sont les suivantes (oh, Standard).
Extrait de [Limites de taux - Développeurs Twitter](https://developer.twitter.com/en/docs/basics/rate-limits)
Endpoint Resource family Requests / window (user auth) Requests / window (app auth) GET search/tweets search 180 450
Vous pouvez obtenir l'état de la limite actuelle en tant que point de terminaison à l'adresse https://api.twitter.com/1.1/application/rate_limit_status.json. Le paramètre «ressources» est facultatif et spécifie «Famille de ressources».
user auth (OAuth v1.1)
{
"rate_limit_context": {
"access_token": "*******************"
},
"resources": {
"search": {
"/search/tweets": {
"limit": 180,
"remaining": 180,
"reset": 1591016735
}
}
}
}
app auth (OAuth v2.0)
{
"rate_limit_context": {
"application": "dummykey"
},
"resources": {
"search": {
"/search/tweets": {
"limit": 450,
"remaining": 450,
"reset": 1591016736
}
}
}
}
Chacun des nombres «reset» correspond à l'heure de l'époque, qui indique l'heure à réinitialiser.
Dans l'exemple ci-dessus, ʻuser auth (OAuth v1.1) with epoch time
1591016735=
2020-06-01 22: 05: 35, ʻapp auth
(OAuth v2.0) with 1591016736
= Indique qu'il sera réinitialisé à 2020-06-01 22: 05: 36
.
Si vous ne respectez pas la limite, le nombre de "restant" sera "0".
Les éléments des informations acquises sont les suivants. (Exemple de famille de recherche)
Category | Family | Endpoint | Key | Value |
---|---|---|---|---|
rate_limit_context | access_token (user auth (v1.1)) | Contenu du jeton d'accès | ||
application (app auth (v2.0)) | dummykey (Semble être fixe) | |||
resources | ||||
search | ||||
/search/tweets | ||||
limit | Nombre maximum de fois dans le délai | |||
remaining | Nombre restant de fois accessibles dans le délai imparti | |||
reset | Heure à laquelle la limite de temps est réinitialisée(epoch time) |
ressources
.L'exemple ci-dessous est celui où vous spécifiez par erreur «utilisateur» pour la famille de ressources. (En fait, vous devez spécifier «utilisateurs» (avec s))
user auth
{
"rate_limit_context": {
"access_token": "*******************"
}
}
app auth
{
"rate_limit_context": {
"application": "dummykey"
}
}
Les deux renvoient " rate_limit_context
"mais n'ont pas de" ressources
".
Si une erreur de limite de débit se produit, 429 est renvoyé dans res.status_code
(code d'état HTTP). (420 peut être retourné [^ 1].)
[^ 1]: Généralement, 429 " Too Many Requests: Renvoyé lorsqu'une demande ne peut pas être servie en raison de l'épuisement de la limite de débit de l'application pour la ressource .
"est renvoyé, mais très rarement 420" ʻAméliorer votre Calme: renvoyé lorsqu'une application est soumise à une limitation de débit pour avoir effectué trop de demandes. "" Peut être retourné. Ce dernier peut se produire si vous faites accidentellement plusieurs demandes en même temps (non vérifiées). → Il y avait une explication dans "Connexion à un point de terminaison de streaming - Développeurs Twitter", donc je l'ai ajoutée au texte. (03/06/2020).
Extrait de [Codes de réponse - Développeurs Twitter](https://developer.twitter.com/en/docs/basics/response-codes)
Code Text Description 420 Enhance Your Calm Returned when an app is being rate limited for making too many requests. 429 Too Many Requests Returned when a request cannot be served due to the app's rate limit having been exhausted for the resource. See Rate Limiting.
[Mis à jour le 06/03/2020] Il y avait une explication détaillée de 420 ci-dessous.
Extrait de [Connexion à un point de terminaison de streaming - Développeurs Twitter](https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/connecting)
420 Rate Limited The client has connected too frequently. For example, an endpoint returns this status if:
- A client makes too many login attempts in a short period of time.
- Too many copies of an application attempt to authenticate with the same credentials.
88 est entré dans le code d'erreur JSON.
{
"errors": [
{
"code": 88,
"message": "Rate limit exceeded"
}
]
}
Extrait de [Codes de réponse - Développeurs Twitter](https://developer.twitter.com/en/docs/basics/response-codes)
Code Text Description 88 Rate limit exceeded Corresponds with HTTP 429. The request limit for this resource has been reached for the current rate limit window.
Pour les exceptions telles que les «demandes», veuillez consulter chaque site.
Le flux de traitement considère-t-il la limite de débit comme suit?
while True:
try:
res =Demande à l'API get/post
res.raise_for_status()
except requests.exceptions.HTTPError:
#429 lorsque la limite de débit est atteinte/420 est retourné
if res.status_code in (420, 429):
Obtenez des informations sur la limite de taux ← Ici
Attendez tranquillement jusqu'à l'heure de réinitialisation
continue
420/Traitement des exceptions autre que 429
except OtherException:
Gestion des exceptions
Traitement lorsqu'il peut être acquis avec succès
casser ou retourner ou céder etc.
Ce qui suit est une mesure concrète pour la partie «Obtenir des informations sur la limite de débit».
En tant qu'échantillon d'acquisition d'informations, il n'y a pas beaucoup de mérite à l'implémenter dans une classe, mais compte tenu de l'incorporer réellement dans un programme, je pense qu'il serait préférable de l'écrire sous une forme facile à modulariser, alors j'en ai fait une classe appelée GetTweetStatus. Il y a. (Il y a aussi une volonté d'éviter autant que possible l'accès de l'extérieur comme apikey et Bearer ...)
class GetTweetStatus
def __init__(self, apikey, apisec, access_token="", access_secret=""):
self._apikey = apikey
self._apisec = apisec
self._access_token = access_token
self._access_secret = access_secret
self._access_token_mask = re.compile(r'(?P<access_token>"access_token":)\s*".*"')
La dernière ligne, re.compile (), sert à masquer l'affichage du ʻaccess_token` reçu.
user auth (OAuth v1.1)
GetTweetStatus.get_limit_status_v1( )
def get_limit_status_v1(self, resource_family="search"):
"""OAuth v1.Obtenir le statut en utilisant 1"""
#Utilisez OAuth1Session car OAuth est compliqué
oauth1 = OAuth1Session(self._apikey, self._apisec, self._access_token, self._access_secret)
params = {
'resources': resource_family # help, users, search, statuses etc.
}
try:
res = oauth1.get(STATUS_ENDPOINT, params=params, timeout=5.0)
res.raise_for_status()
except (TimeoutError, requests.ConnectionError):
raise requests.ConnectionError("Cannot get Limit Status")
except Exception:
raise Exception("Cannot get Limit Status")
return res.json()
. Vous devez installer
requests_oauthlib et
à partir de requests_oauthlib import OAuth1Session`.app auth (OAuth v2.0)
GetTweetStatus.get_limit_status_v2( )
def get_limit_status_v2(self, resource_family="search"):
"""OAuth v2.0 (Bearer)Obtenir le statut en utilisant"""
bearer = self._get_bearer() #Obtenir le porteur
headers = {
'Authorization':'Bearer {}'.format(bearer),
'User-Agent': USER_AGENT
}
params = {
'resources': resource_family # help, users, search, statuses etc.
}
try:
res = requests.get(STATUS_ENDPOINT, headers=headers, params=params, timeout=5.0)
res.raise_for_status()
except (TimeoutError, requests.ConnectionError):
raise requests.ConnectionError("Cannot get Limit Status")
except Exception:
raise Exception("Cannot get Limit Status")
return res.json()
bearer = self._get_bearer () C'est la partie
_get_bearer () appelée par #Get Bearer
.
GetTweetStatus._get_bearer( ), _get_credential( )
def _get_bearer(self):
"""Obtenir le porteur"""
cred = self._get_credential()
headers = {
'Authorization': 'Basic ' + cred,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'User-Agent': USER_AGENT
}
data = {
'grant_type': 'client_credentials',
}
try:
res = requests.post(TOKEN_ENDPOINT, data=data, headers=headers, timeout=5.0)
res.raise_for_status()
except (TimeoutError, requests.ConnectionError):
raise Exception("Cannot get Bearer")
except requests.exceptions.HTTPError:
if res.status_code == 403:
raise requests.exceptions.HTTPError("Auth Error")
raise requests.exceptions.HTTPError("Other Exception")
except Exception:
raise Exception("Cannot get Bearer")
rjson = res.json()
return rjson['access_token']
def _get_credential(self):
"""Générer des informations d'identification"""
pair = self._apikey + ':' + self._apisec
bcred = b64encode(pair.encode('utf-8'))
return bcred.decode()
grant_type =" client_credentials "
Il est implémenté comme une méthode. Est-ce un endroit pour implémenter quelque chose qui renvoie "reset" en utilisation réelle?
GetTweetStatus.disp_limit_status( )
def disp_limit_status(self, version=2, resource_family="search"):
"""Afficher la limite de débit par version"""
if version == 2:
resj = self.get_limit_status_v2(resource_family=resource_family)
elif version == 1:
resj = self.get_limit_status_v1(resource_family=resource_family)
else:
raise Exception("Version error: {version}")
#Afficher JSON
print(self._access_token_mask.sub(r'\g<access_token> "*******************"',
json.dumps(resj, indent=2, ensure_ascii=False)))
#Affichage démonté(remain/Exemple de réinitialisation)
print("resources:")
if 'resources' in resj:
resources = resj['resources']
for family in resources:
print(f" family: {family}")
endpoints = resources[family]
for endpoint in endpoints:
items = endpoints[endpoint]
print(f" endpoint: {endpoint}")
limit = items['limit']
remaining = items['remaining']
reset = items['reset']
e2d = epoch2datetime(reset)
duration = get_delta(reset)
print(f" limit: {limit}")
print(f" remaining: {remaining}")
print(f" reset: {reset}") #← En fait un formulaire qui renvoie ceci
print(f" reset(epoch2datetime): {e2d}")
print(f" duration: {duration} sec")
else:
print(" Not Available")
Il s'agit de l'utilitaire de manipulation de l'heure et du début du fichier.
getTwitterStatus.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Exemple d'acquisition d'informations sur la limite de débit Twitter"""
import os
import sys
import json
from base64 import b64encode
import datetime
import time
import re
import argparse
#!pip install requests
import requests
#!pip install requests_oauthlib
from requests_oauthlib import OAuth1Session
USER_AGENT = "Get Twitter Staus Application/1.0"
TOKEN_ENDPOINT = 'https://api.twitter.com/oauth2/token'
STATUS_ENDPOINT = 'https://api.twitter.com/1.1/application/rate_limit_status.json'
def epoch2datetime(epoch):
"""Heure de l'époque(Heure UNIX)Datetime(localtime)Convertir en"""
return datetime.datetime(*(time.localtime(epoch)[:6]))
def datetime2epoch(d_utc):
"""datetime (UTC)Heure de l'époque(Heure UNIX)Convertir en"""
#Convertir UTC en heure locale
date_localtime = \
d_utc.replace(tzinfo=datetime.tzinfo.tz.tzutc()).astimezone(datetime.tzinfo.tz.tzlocal())
return int(time.mktime(date_localtime.timetuple()))
def get_delta(target_epoch_time):
"""target_epoch_Renvoie la différence entre l'heure et l'heure actuelle"""
return target_epoch_time - int(round(time.time(), 0))
Comme c'est un gros problème, j'ai essayé de rendre possible la spécification de la version OAuth et de la famille de ressources avec l'argument de la ligne de commande.
main( )
def main():
"""main()"""
# API_KEY, API_Confirmation des variables d'environnement telles que SEC
apikey = os.getenv('API_KEY', default="")
apisec = os.getenv('API_SEC', default="")
access_token = os.getenv('ACCESS_TOKEN', default="")
access_secret = os.getenv('ACCESS_SECRET', default="")
if apikey == "" or apisec == "": #Si la variable d'environnement ne peut pas être obtenue
print("API de variable d'environnement_CLÉ et API_Veuillez régler SEC.", file=sys.stderr)
print("OAuth v1.Variable d'environnement ACCESS lors de l'utilisation de 1_TOKEN et ACCÈS_Définissez également SECRET.",
file=sys.stderr)
sys.exit(255)
#Réglage de l'argument
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--oauthversion', type=int, default=0,
metavar='N', choices=(0, 1, 2),
help=u'Spécification de la version OAuth[1|2]')
parser.add_argument('-f', '--family', type=str, default='search',
metavar='Family',
help=u'Spécification de la famille d'API. Séparé par des virgules pour plusieurs')
args = parser.parse_args()
oauthversion = args.oauthversion
family = args.family
#Objet GetTweetStatus
gts = GetTweetStatus(apikey, apisec, access_token=access_token, access_secret=access_secret)
# User Auth (OAuth v1.1)Acquisition et affichage de la limite de débit par
if (oauthversion in (0, 1)) and (access_token != "" and access_secret != ""):
print("<<user auth (OAuth v1)>>")
gts.disp_limit_status(version=1, resource_family=family)
# App Auth (OAuth v2.0)Acquisition et affichage de la limite de débit par
if oauthversion in (0, 2):
print("<<app auth (OAuth v2)>>")
gts.disp_limit_status(version=2, resource_family=family)
if __name__ == "__main__":
main()
getTwitterStatus.py
[^ 2]: J'ai essayé d'utiliser Gist pour la première fois. Je m'inquiète de savoir si l'utilisation est correcte.
$ python3 getTwitterStatus.py
<<user auth (OAuth v1)>>
{
"rate_limit_context": {
"access_token": "*******************"
},
"resources": {
"search": {
"/search/tweets": {
"limit": 180,
"remaining": 180,
"reset": 1591016735
}
}
}
}
resources:
family: search
endpoint: /search/tweets
limit: 180
remaining: 180
reset: 1591016735
reset(epoch2datetime): 2020-06-01 22:05:35
duration: 899 sec
<<app auth (OAuth v2)>>
{
"rate_limit_context": {
"application": "dummykey"
},
"resources": {
"search": {
"/search/tweets": {
"limit": 450,
"remaining": 450,
"reset": 1591016736
}
}
}
}
resources:
family: search
endpoint: /search/tweets
limit: 450
remaining: 450
reset: 1591016736
reset(epoch2datetime): 2020-06-01 22:05:36
duration: 900 sec
$
Il y a une limite au nombre de fois où vous pouvez l'obtenir en même temps, quelle que soit la limite de temps.
200 ($ count \ leq200 search / tweets
.
Il existe diverses autres restrictions, mais dans le cas de search / tweets
, l'élément next_results
sera inclus dans search_metadata
afin qu'il puisse être récupéré en continu.
{
"statuses": [
...
],
"search_metadata": {
"completed_in": 0.047,
"max_id": 1125490788736032770,
"max_id_str": "1125490788736032770",
"next_results": "?max_id=1124690280777699327&q=from%3Atwitterdev&count=2&include_entities=1&result_type=mixed",
"query": "from%3Atwitterdev",
"refresh_url": "?since_id=1125490788736032770&q=from%3Atwitterdev&result_type=mixed&include_entities=1",
"count": 2,
"since_id": 0,
"since_id_str": "0"
}
}
Il y a next_results
dans search_metadata
, donc si vous demandez ceci comme nouveau paramètre, vous pouvez également obtenir le reste des résultats de la recherche (dans les unités spécifiées dans count).
Tant que vous n'atteignez pas la limite de temps, vous pouvez vous y référer et répéter pour obtenir les résultats en continu. Autrement dit, vous pouvez obtenir $ count $ (jusqu'à 100) $ × limite $ (180 pour l'authentification de l'utilisateur) $ = 18 000 Tweet $ dans la limite de taux.
Dans le cas de l'exemple ci-dessus, $ count = 2 $, donc si vous continuez tel quel, vous pouvez obtenir $ count (2) tweets / time x limit (180) times / 15 minutes = 360 tweets / 15 minutes $, et vous serez limité. Reach (bien sûr, si vous demandez un cancer).
Lorsque tous les résultats de la recherche ont été obtenus, next_results
disparaît de search_metadata
.
De plus, parfois, si vous le réacquérir, next_results
peut être restauré, vous voudrez peut-être attendre un moment et réessayer.
Dans le cas de statuses / user_timeline
etc., * _metadata
n'est pas inclus, alors faites bon usage de la spécification de max_id
et générez quelque chose d'équivalent à next_results
de recherche par vous-même. est nécessaire. (En fait, je ne l'ai pas utilisé pour autre chose que la recherche, donc je ne suis pas sûr, mais je pense que ce n'est pas si loin.)
Dans le cas de la recherche, les 7 derniers jours sont ciblés, mais comme ʻuser_timeline` correspond aux dernières 24 heures, je pense que l'objectif est différent en premier lieu ...
Recommended Posts