web-dev-qa-db-fra.com

Comment utiliser des requêtes en asyncio?

Je souhaite effectuer des tâches de requête http parallèles dans asyncio, mais j'estime que python-requests bloquerait la boucle d'événement de asyncio. J'ai trouvé aiohttp mais il n'a pas pu fournir le service de requête http à l'aide d'un proxy http.

Je veux donc savoir s’il est possible de faire des requêtes http asynchrones à l’aide de asyncio.

110
flyer

Pour utiliser des requêtes (ou toute autre bibliothèque bloquante) avec asyncio, vous pouvez utiliser BaseEventLoop.run_in_executor pour exécuter une fonction dans un autre thread et en céder le résultat. Par exemple:

import asyncio
import requests

@asyncio.coroutine
def main():
    loop = asyncio.get_event_loop()
    future1 = loop.run_in_executor(None, requests.get, 'http://www.google.com')
    future2 = loop.run_in_executor(None, requests.get, 'http://www.google.co.uk')
    response1 = yield from future1
    response2 = yield from future2
    print(response1.text)
    print(response2.text)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Cela donnera les deux réponses en parallèle.

Avec python 3.5, vous pouvez utiliser la nouvelle syntaxe await/async:

import asyncio
import requests

async def main():
    loop = asyncio.get_event_loop()
    future1 = loop.run_in_executor(None, requests.get, 'http://www.google.com')
    future2 = loop.run_in_executor(None, requests.get, 'http://www.google.co.uk')
    response1 = await future1
    response2 = await future2
    print(response1.text)
    print(response2.text)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Voir PEP0492 pour plus d'informations.

163
christian

aiohttp peut déjà être utilisé avec un proxy HTTP:

import asyncio
import aiohttp


@asyncio.coroutine
def do_request():
    proxy_url = 'http://localhost:8118'  # your proxy address
    response = yield from aiohttp.request(
        'GET', 'http://google.com',
        proxy=proxy_url,
    )
    return response

loop = asyncio.get_event_loop()
loop.run_until_complete(do_request())
70
mindmaster

Les réponses ci-dessus utilisent toujours les anciennes coroutines de style Python 3.4. Voici ce que vous écririez si vous obteniez Python 3.5+.

aiohttpsupporte le proxy http maintenant

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
            'http://python.org',
            'https://google.com',
            'http://yifei.me'
        ]
    tasks = []
    async with aiohttp.ClientSession() as session:
        for url in urls:
            tasks.append(fetch(session, url))
        htmls = await asyncio.gather(*tasks)
        for html in htmls:
            print(html[:100])

if __== '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
23
ospider

Requests ne prend actuellement pas en charge asyncio et il n'est pas prévu de fournir un tel support. Il est probable que vous puissiez implémenter un "adaptateur de transport" personnalisé (comme indiqué ci-dessus ici ) sachant utiliser asyncio.

Si je me trouve avec un peu de temps, je pourrais peut-être examiner cela, mais je ne peux rien promettre.

9
Lukasa

Il existe un bon cas de boucles asynchrones/wait et de threads dans un article de Pimin Konstantin Kefaloukos Requêtes HTTP simples et parallèles avec Python et asyncio :

Pour minimiser le temps total d'exécution, nous pourrions augmenter la taille du pool de threads pour l'adapter au nombre de demandes que nous devons effectuer. Heureusement, c'est facile à faire, comme nous le verrons plus tard. La liste de codes ci-dessous est un exemple de la façon de faire vingt demandes HTTP asynchrones avec un pool de threads de vingt threads de travail:

# Example 3: asynchronous requests with larger thread pool
import asyncio
import concurrent.futures
import requests

async def main():

    with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

        loop = asyncio.get_event_loop()
        futures = [
            loop.run_in_executor(
                executor, 
                requests.get, 
                'http://example.org/'
            )
            for i in range(20)
        ]
        for response in await asyncio.gather(*futures):
            pass


loop = asyncio.get_event_loop()
loop.run_until_complete(main())
7
Ilya Rusin