Richieste asincrone con richieste Python


142

Ho provato l'esempio fornito nella documentazione della libreria delle richieste per Python.

Con async.map(rs), ottengo i codici di risposta, ma voglio ottenere il contenuto di ogni pagina richiesta. Questo, ad esempio, non funziona:

out = async.map(rs)
print out[0].content

Forse le risposte che stai ricevendo hanno un corpo vuoto?
Mariusz Jamro,

Per me va bene. Si prega di pubblicare l'errore completo che si sta ottenendo.
Chewie,

non c'è nessun errore. funziona solo per sempre dagli URL di prova forniti.
trbck,

appare ovviamente quando uso urls su https. http sta funzionando bene
trbck

Sembra che requests-threadsesista ora.
OrangeDog,

Risposte:


154

Nota

La risposta di seguito non è applicabile alle richieste v0.13.0 +. La funzionalità asincrona è stata spostata su grequest dopo che questa domanda è stata scritta. Tuttavia, potresti semplicemente sostituirlo requestscon grequestssotto e dovrebbe funzionare.

Ho lasciato questa risposta per riflettere la domanda originale che riguardava l'uso delle richieste <v0.13.0.


Per eseguire più attività in async.map modo asincrono è necessario:

  1. Definisci una funzione per ciò che vuoi fare con ogni oggetto (il tuo compito)
  2. Aggiungi quella funzione come hook di evento nella tua richiesta
  3. Chiama async.mapun elenco di tutte le richieste / azioni

Esempio:

from requests import async
# If using requests > v0.13.0, use
# from grequests import async

urls = [
    'http://python-requests.org',
    'http://httpbin.org',
    'http://python-guide.org',
    'http://kennethreitz.com'
]

# A simple task to do to each response object
def do_something(response):
    print response.url

# A list to hold our things to do via async
async_list = []

for u in urls:
    # The "hooks = {..." part is where you define what you want to do
    # 
    # Note the lack of parentheses following do_something, this is
    # because the response will be used as the first argument automatically
    action_item = async.get(u, hooks = {'response' : do_something})

    # Add the task to our list of things to do via async
    async_list.append(action_item)

# Do our list of things to do via async
async.map(async_list)

2
Bella idea di aver lasciato il tuo commento: a causa di problemi di compatibilità tra ultime richieste e richieste (mancanza dell'opzione max_retries nelle richieste 1.1.0) ho dovuto effettuare il downgrade delle richieste per recuperare asincrono e ho scoperto che la funzionalità asincrona è stata spostata con le versioni 0.13+ ( pypi.python.org/pypi/requests )
fuori tempo il

1
Domanda stupida: qual è l'aumento di velocità dell'utilizzo dei grequest rispetto alle semplici richieste? Quali sono i limiti per quanto riguarda le richieste? es. mettere 3500 richieste in async.map andrebbe bene?
droope

10
from grequests import asyncnon funziona .. e questa definizione di dosomething funziona per me def do_something(response, **kwargs):, la trovo da stackoverflow.com/questions/15594015/…
Allan Ruin,

3
se la chiamata async.map si blocca ancora, allora come è asincrono? Oltre alle richieste stesse inviate in modo asincrono, il recupero è ancora sincrono?
bryanph,

3
Sostituendo from requests import asynccon import grequests as asynclavorato per me.
Martin Thoma,

80

asyncè ora un modulo indipendente: grequests.

Vedi qui: https://github.com/kennethreitz/grequests

E lì: metodo ideale per inviare più richieste HTTP su Python?

installazione:

$ pip install grequests

utilizzo:

costruire uno stack:

import grequests

urls = [
    'http://www.heroku.com',
    'http://tablib.org',
    'http://httpbin.org',
    'http://python-requests.org',
    'http://kennethreitz.com'
]

rs = (grequests.get(u) for u in urls)

invia lo stack

grequests.map(rs)

il risultato è simile

[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]

grequest non sembra impostare una limitazione per le richieste simultanee, vale a dire quando più richieste vengono inviate allo stesso server.


11
Per quanto riguarda la limitazione delle richieste simultanee, è possibile specificare una dimensione del pool durante l'esecuzione di map () / imap (). cioè grequests.map (rs, size = 20) per avere 20 prese simultanee.
synthesizerpatel,

1
A partire da ora questo non è compatibile con python3 (gevent non riesce a creare v2.6 su py3.4).
Saarp

1
Non capisco bene la parte asincrona. se lascio results = grequests.map(rs)il codice dopo che questa riga è bloccata, posso vedere l'effetto asincrono?
Allan Ruin,

47

Ho testato sia richieste-future che richieste . Grequests è più veloce ma porta patch di scimmie e problemi aggiuntivi con dipendenze. request-futures è più volte più lento delle richieste. Ho deciso di scrivere le mie richieste e ho semplicemente spostato le richieste in ThreadPoolExecutor ed è stato veloce quasi quanto le richieste, ma senza dipendenze esterne.

import requests
import concurrent.futures

def get_urls():
    return ["url1","url2"]

def load_url(url, timeout):
    return requests.get(url, timeout = timeout)

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

    future_to_url = {executor.submit(load_url, url, 10): url for url in     get_urls()}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            resp_err = resp_err + 1
        else:
            resp_ok = resp_ok + 1

Che tipo di eccezione è possibile qui?
Rallenta Harry il

requests.exceptions.Timeout
Hodza

2
Mi dispiace non capisco la tua domanda. Utilizzare solo un singolo URL in più thread? Solo un caso DDoS attacca))
Hodza,

1
Non capisco perché questa risposta abbia ottenuto così tanti voti. La domanda del PO riguardava le richieste asincrone. ThreadPoolExecutor esegue i thread. Sì, puoi fare richieste in più thread, ma non sarà mai un programma asincrono, quindi come posso essere una risposta alla domanda originale?
nagylzs,

1
In realtà, la domanda era su come caricare gli URL in parallelo. E sì, l'esecutore del pool di thread non è l'opzione migliore, è meglio usare asincrono io, ma funziona bene in Python. E non capisco perché i thread non possano essere usati per l'asincrono? Cosa succede se è necessario eseguire l'attività associata alla CPU in modo asincrono?
Hodza,

29

forse il future di richieste è un'altra scelta.

from requests_futures.sessions import FuturesSession

session = FuturesSession()
# first request is started in background
future_one = session.get('http://httpbin.org/get')
# second requests is started immediately
future_two = session.get('http://httpbin.org/get?foo=bar')
# wait for the first request to complete, if it hasn't already
response_one = future_one.result()
print('response one status: {0}'.format(response_one.status_code))
print(response_one.content)
# wait for the second request to complete, if it hasn't already
response_two = future_two.result()
print('response two status: {0}'.format(response_two.status_code))
print(response_two.content)

È inoltre raccomandato nel documento dell'ufficio . Se non vuoi coinvolgere gevent, è una buona idea.


1
Una delle soluzioni più semplici. Il numero di richieste simultanee può essere aumentato definendo il parametro max_workers
Jose Cherian,

1
Sarebbe bello vedere un esempio di questo ridimensionato, quindi non stiamo usando un nome di variabile per articolo per il loop.
user1717828

avere un thread per richiesta è un inferno spreco di risorse! ad esempio, non è possibile fare contemporaneamente 500 richieste, ucciderà la tua CPU. questa non dovrebbe mai essere considerata una buona soluzione.
Corneliu Maftuleac,

@CorneliuMaftuleac buon punto. Per quanto riguarda l'utilizzo del thread, è necessario preoccuparsene e la libreria fornisce un'opzione per abilitare il pool di thread o il pool di elaborazione. ThreadPoolExecutor(max_workers=10)
Dreampuf,

Il pool di elaborazione @Dreampuf credo sia ancora peggio?
Corneliu Maftuleac,

11

Ho molti problemi con la maggior parte delle risposte pubblicate: usano librerie obsolete che sono state trasferite con funzionalità limitate o forniscono una soluzione con troppa magia sull'esecuzione della richiesta, rendendo difficile la gestione degli errori. Se non rientrano in una delle categorie di cui sopra, sono librerie di terze parti o obsolete.

Alcune delle soluzioni funzionano perfettamente solo nelle richieste http, ma le soluzioni non sono all'altezza di qualsiasi altro tipo di richiesta, il che è ridicolo. Una soluzione altamente personalizzata non è necessaria qui.

Il semplice utilizzo della libreria integrata di Python asyncioè sufficiente per eseguire richieste asincrone di qualsiasi tipo, oltre a fornire una fluidità sufficiente per la gestione di errori complessi e specifici.

import asyncio

loop = asyncio.get_event_loop()

def do_thing(params):
    async def get_rpc_info_and_do_chores(id):
        # do things
        response = perform_grpc_call(id)
        do_chores(response)

    async def get_httpapi_info_and_do_chores(id):
        # do things
        response = requests.get(URL)
        do_chores(response)

    async_tasks = []
    for element in list(params.list_of_things):
       async_tasks.append(loop.create_task(get_chan_info_and_do_chores(id)))
       async_tasks.append(loop.create_task(get_httpapi_info_and_do_chores(ch_id)))

    loop.run_until_complete(asyncio.gather(*async_tasks))

Come funziona è semplice. Stai creando una serie di attività che desideri eseguire in modo asincrono, quindi chiedi a un loop di eseguire tali attività e uscire al termine. Nessuna libreria extra soggetta a mancanza di manutenzione, nessuna mancanza di funzionalità richiesta.


2
Se capisco correttamente, questo bloccherà il ciclo degli eventi mentre eseguo la chiamata GRPC e HTTP? Quindi, se il completamento di queste chiamate richiede pochi secondi, l'intero ciclo di eventi verrà bloccato per secondi? Per evitare ciò, è necessario utilizzare le librerie GRPC o HTTP che lo sono async. Quindi ad esempio puoi farlo await response = requests.get(URL). No?
Coder n. 23

Sfortunatamente, provando questo, ho scoperto che creare un wrapper requestsè appena più veloce (e in alcuni casi più lento) rispetto al semplice chiamare un elenco di URL in modo sincrono. Ad esempio, richiedere un endpoint che impiega 3 secondi per rispondere 10 volte utilizzando la strategia sopra richiede circa 30 secondi. Se vuoi asyncprestazioni vere , devi usare qualcosa del genere aiohttp.
DragonBobZ

8

So che questo è stato chiuso per un po ', ma ho pensato che potesse essere utile promuovere un'altra soluzione asincrona costruita sulla libreria delle richieste.

list_of_requests = ['http://moop.com', 'http://doop.com', ...]

from simple_requests import Requests
for response in Requests().swarm(list_of_requests):
    print response.content

I documenti sono qui: http://pythonhosted.org/simple-requests/


@YSY Sentiti libero di pubblicare un problema: github.com/ctheiss/simple-requests/issues ; Uso letteralmente questa libreria migliaia di volte al giorno.
Monkey Boson,

Boston, come gestisci gli errori 404/500? che dire di https urls? apprezzerà uno snipping che supporta migliaia di URL. puoi per favore incollare un esempio? grazie
YSY,

@YSY Per impostazione predefinita, gli errori 404/500 generano un'eccezione. Questo comportamento può essere ignorato (vedi pythonhosted.org/simple-requests/… ). Gli URL HTTPS sono complicati a causa della dipendenza da gevent, che attualmente ha un bug eccezionale su questo ( github.com/gevent/gevent/issues/477 ). C'è uno spessore nel biglietto è possibile eseguire, ma sarà ancora gettare le avvertenze per i server SNI (ma sarà funzionerà). Per quanto riguarda lo snipping, temo che tutti i miei usi siano presso la mia azienda e chiusi. Ma ti assicuro che eseguiamo migliaia di richieste su decine di lavori.
Monkey Boson,

La biblioteca ha un aspetto elegante rispetto all'interazione. Python3 + è utilizzabile? Siamo spiacenti ma non ho visto alcuna menzione.
Isaac Philip,

@Jethro assolutamente giusto, la libreria avrebbe bisogno di una riscrittura totale poiché le tecnologie sottostanti sono abbastanza diverse in Python 3. Per ora, la libreria è "completa" ma funziona solo per Python 2.
Monkey Boson

4
threads=list()

for requestURI in requests:
    t = Thread(target=self.openURL, args=(requestURI,))
    t.start()
    threads.append(t)

for thread in threads:
    thread.join()

...

def openURL(self, requestURI):
    o = urllib2.urlopen(requestURI, timeout = 600)
    o...

4
si tratta di richieste "normali" nei thread. non è un cattivo esempio comprare è fuori tema.
Nick,



2

Puoi usarlo httpxper quello.

import httpx

async def get_async(url):
    async with httpx.AsyncClient() as client:
        return await client.get(url)

urls = ["http://google.com", "http://wikipedia.org"]

# Note that you need an async context to use `await`.
await asyncio.gather(*map(get_async, urls))

se vuoi una sintassi funzionale, la gamla lib lo avvolge get_async.

Quindi puoi farlo


await gamla.map(gamla.get_async(10), ["http://google.com", "http://wikipedia.org"])

Il 10è il timeout in secondi.

(disclaimer: io sono il suo autore)


E respxper deridere / testare :)
dal

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.