Comment définir le nombre maximal de demandes par seconde (les limiter) côté client à l'aide de aiohttp?
J'ai trouvé une solution possible ici: http://compiletoi.net/fast-scraping-in-python-with-asyncio.html
Faire 3 demandes en même temps, c'est cool, faire 5000, cependant, n'est pas si gentil. Si vous essayez de faire trop de demandes en même temps, les connexions pourraient commencer à se fermer, voire vous être bannies du site Web.
Pour éviter cela, vous pouvez utiliser un sémaphore. C'est un outil de synchronisation qui peut être utilisé pour limiter le nombre de routines faisant quelque chose à un moment donné. Nous allons simplement créer le sémaphore avant de créer la boucle, en passant comme argument le nombre de requêtes simultanées que nous voulons autoriser:
sem = asyncio.Semaphore(5)
Ensuite, nous remplaçons simplement:
page = yield from get(url, compress=True)
par la même chose, mais protégé par un sémaphore:
with (yield from sem):
page = yield from get(url, compress=True)
Cela garantira que 5 demandes au maximum peuvent être effectuées en même temps.
Depuis la version 2.0, lorsque vous utilisez un ClientSession
, aiohttp
limite automatiquement le nombre de connexions simultanées à 100.
Vous pouvez modifier la limite en créant votre propre TCPConnector
et en le passant dans la ClientSession
. Par exemple, pour créer un client limité à 50 demandes simultanées:
import aiohttp
connector = aiohttp.TCPConnector(limit=50)
client = aiohttp.ClientSession(connector=connector)
Si cela convient mieux à votre cas d'utilisation, il existe également un paramètre limit_per_Host
(désactivé par défaut) que vous pouvez passer pour limiter le nombre de connexions simultanées au même "noeud final". Selon les documents:
limit_per_Host
(int
) - limite pour les connexions simultanées au même noeud final. Les points d'extrémité sont les mêmes s'ils ont le même(Host, port, is_ssl)
triple.
Exemple d'utilisation:
import aiohttp
connector = aiohttp.TCPConnector(limit_per_Host=50)
client = aiohttp.ClientSession(connector=connector)
Vous pouvez définir un délai par demande ou regrouper les URL par lots et limiter les lots à la fréquence souhaitée.
Forcer le script à attendre entre les demandes en utilisant asyncio.sleep
import asyncio
import aiohttp
delay_per_request = 0.5
urls = [
# put some URLs here...
]
async def app():
tasks = []
for url in urls:
tasks.append(asyncio.ensure_future(make_request(url)))
await asyncio.sleep(delay_per_request)
results = await asyncio.gather(*tasks)
return results
async def make_request(url):
print('$$$ making request')
async with aiohttp.ClientSession() as sess:
async with sess.get(url) as resp:
status = resp.status
text = await resp.text()
print('### got page data')
return url, status, text
Ceci peut être exécuté avec par exemple results = asyncio.run(app())
.
En utilisant make_request
ci-dessus, vous pouvez demander et limiter des lots d'URL comme ceci:
import asyncio
import aiohttp
import time
max_requests_per_second = 0.5
urls = [[
# put a few URLs here...
],[
# put a few more URLs here...
]]
async def app():
results = []
for i, batch in enumerate(urls):
t_0 = time.time()
print(f'batch {i}')
tasks = [asyncio.ensure_future(make_request(url)) for url in batch]
for t in tasks:
d = await t
results.append(d)
t_1 = time.time()
# Throttle requests
batch_time = (t_1 - t_0)
batch_size = len(batch)
wait_time = (batch_size / max_requests_per_second) - batch_time
if wait_time > 0:
print(f'Too fast! Waiting {wait_time} seconds')
time.sleep(wait_time)
return results
De nouveau, ceci peut être exécuté avec asyncio.run(app())
.