wake-up-neo.com

aiohttp: Maximale Anzahl von Anforderungen pro Sekunde festlegen

Wie kann ich mit aiohttp die maximale Anzahl von Anforderungen pro Sekunde auf der Clientseite festlegen (einschränken)?

15
pngnviko

Ich habe hier eine mögliche Lösung gefunden: http://compiletoi.net/fast-scraping-in-python-with-asyncio.html

3 Anfragen gleichzeitig zu machen ist cool, 5000 zu tun ist aber nicht so schön. Wenn Sie versuchen, zu viele Anfragen gleichzeitig auszuführen, werden die Verbindungen möglicherweise geschlossen, oder Sie werden sogar von der Website gebannt.

Um dies zu vermeiden, können Sie ein Semaphor verwenden. Es ist ein Synchronisierungstool, mit dem die Anzahl der Coroutinen begrenzt werden kann, die irgendwann etwas tun. Wir erstellen nur das Semaphor, bevor die Schleife erstellt wird, und übergeben als Argument die Anzahl der gleichzeitigen Anforderungen, die wir zulassen möchten:

sem = asyncio.Semaphore(5)

Dann ersetzen wir einfach:

page = yield from get(url, compress=True)

durch dasselbe, aber durch ein Semaphor geschützt:

with (yield from sem):
    page = yield from get(url, compress=True)

Dadurch wird sichergestellt, dass maximal 5 Anforderungen gleichzeitig bearbeitet werden können.

20
pngnviko

Seit Version 2.0 begrenzt ClientSession bei Verwendung einer aiohttp die Anzahl der gleichzeitigen Verbindungen automatisch auf 100.

Sie können das Limit ändern, indem Sie Ihre eigene TCPConnector erstellen und in die ClientSession übergeben. So erstellen Sie beispielsweise einen Client, der auf 50 gleichzeitige Anforderungen begrenzt ist:

import aiohttp

connector = aiohttp.TCPConnector(limit=50)
client = aiohttp.ClientSession(connector=connector)

Falls es für Ihren Anwendungsfall besser geeignet ist, gibt es auch einen limit_per_Host-Parameter (der standardmäßig deaktiviert ist), den Sie übergeben können, um die Anzahl der gleichzeitigen Verbindungen zu demselben "Endpunkt" zu begrenzen. In den Unterlagen:

limit_per_Host (int) - Begrenzung für gleichzeitige Verbindungen zu demselben Endpunkt. Endpunkte sind gleich, wenn sie gleich (Host, port, is_ssl) dreifach sind.

Verwendungsbeispiel:

import aiohttp

connector = aiohttp.TCPConnector(limit_per_Host=50)
client = aiohttp.ClientSession(connector=connector)
20
Mark Amery

Sie können eine Verzögerung pro Anforderung festlegen oder die URLs in Batches gruppieren und die Batches drosseln, um die gewünschte Häufigkeit zu erreichen.

1. Verzögerung pro Anfrage

Erzwingen, dass das Skript mit asyncio.sleep zwischen Anforderungen wartet

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

Dies kann mit z. results = asyncio.run(app()).

2. Batchgas

Mit make_request von oben können Sie Stapel von URLs wie folgt anfordern und drosseln:

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

Dies kann wiederum mit asyncio.run(app()) ausgeführt werden.

0
AlexG