簡體   English   中英

如何在 Python 中使用 httpx(與 aiohttp)進行並行異步 HTTP 請求?

[英]How do I make parallel async HTTP requests using httpx (versus aiohttp) in Python?

這是基於一個錯字和簡單的錯誤。

沒有刪除,因為它有 httpx 的示例代碼。

我正在嘗試利用asyncio來並行化幾個長時間運行的 web 請求。 因為我是從requests庫遷移的,所以我想使用httpx庫,因為類似的 API。 我的環境是 Python 3.7.7 Anaconda 發行版,安裝了所有必需的軟件包(Windows 10)。

但是,盡管能夠將httpx用於同步 web 請求(或用於串行執行一個接一個運行的異步請求),但我無法一次成功運行多個異步請求,盡管使用aiohttp庫。

這是在aiohttp中干凈運行的示例代碼:(請注意,我在 Jupyter 中運行,所以我已經有一個事件循環,因此缺少asyncio.run()

import aiohttp
import asyncio
import time
import httpx

async def call_url(session):
    url = "https://services.cancerimagingarchive.net/services/v3/TCIA/query/getCollectionValues"        
    response = await session.request(method='GET', url=url)
    #response.raise_for_status() 
    return response

for i in range(1,5):
    start = time.time() # start time for timing event
    async with aiohttp.ClientSession() as session: #use aiohttp
    #async with httpx.AsyncClient as session:  #use httpx
        await asyncio.gather(*[call_url(session) for x in range(i)])
    print(f'{i} call(s) in {time.time() - start} seconds')

這會產生預期的響應時間配置文件:

1 call(s) in 7.9129478931427 seconds
2 call(s) in 8.876991510391235 seconds
3 call(s) in 9.730034589767456 seconds
4 call(s) in 10.630006313323975 seconds

但是,如果我取消注釋async with httpx.AsyncClient as session: #use httpx並注釋掉async with aiohttp.ClientSession() as session: #use aiohttp (to swap in httpx for aiohttp ) 然后我得到以下錯誤:

AttributeError                            Traceback (most recent call last)
<ipython-input-108-25244245165a> in async-def-wrapper()
     17         await asyncio.gather(*[call_url(session) for x in range(i)])
     18     print(f'{i} call(s) in {time.time() - start} seconds')

AttributeError: __aexit__

在我的在線研究中,我只能找到 Simon Hawe 的一篇 Medium 文章,展示了如何使用httpx進行並行請求。 請參閱https://medium.com/swlh/how-to-boost-your-python-apps-using-httpx-and-asynchronous-calls-9cfe6f63d6ad

但是,示例異步代碼甚至不使用異步 session object 所以我剛開始有點懷疑。 該代碼不會在 Python 3.7.7 環境或 Jupyter 中執行。 (代碼在這里: https://gist.githubusercontent.com/Shawe82/a218066975f4b325e026337806f8c781/raw/3cb492e971c13e76a07d1a1e77b48de94aa7229cpy.con

它導致此錯誤:

Traceback (most recent call last):
  File ".\async_http_test.py", line 24, in <module>
    asyncio.run(download_all_photos('100_photos'))
  File "C:\Users\stborg\AppData\Local\Continuum\anaconda3\envs\fastai2\lib\asyncio\runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "C:\Users\stborg\AppData\Local\Continuum\anaconda3\envs\fastai2\lib\asyncio\base_events.py", line 587, in run_until_complete
    return future.result()
  File ".\async_http_test.py", line 16, in download_all_photos
    resp = await httpx.get("https://jsonplaceholder.typicode.com/photos")
TypeError: object Response can't be used in 'await' expression

我顯然做錯了什么,因為httpx是為異步構建的。 我只是不確定它是什么!

好的。 坦率地說,這很尷尬。 無需解決方法。 在問題陳述中,我完全忽略了調用 AsyncClient 構造函數......我不敢相信我錯過了這么久。 天啊...

要修復,只需將缺少的括號添加到 AsyncClient 構造函數:

    async with httpx.AsyncClient() as session:  #use httpx
        await asyncio.gather(*[call_url(session) for x in range(i)])

在進一步嘗試編寫此問題時,我發現httpxaiohttp處理上下文管理器的方式存在細微差別。

在引入問題的代碼中,以下代碼與aiohttp一起使用:

    async with aiohttp.ClientSession() as session: #use aiohttp
        await asyncio.gather(*[call_url(session) for x in range(i)])

此代碼將 ClientSession 上下文作為參數傳遞給call_url方法。 我假設在asyncio.gather()完成后,按照正常with語句清理資源。

但是,與httpx相同的方法失敗了,如上所述。 但是,這可以通過完全避免with語句並手動關閉AsyncClient來輕松解決。

換句話說,替換

    async with httpx.AsyncClient as session:  #use httpx
        await asyncio.gather(*[call_url(session) for x in range(i)])

    session = httpx.AsyncClient() #use httpx
    await asyncio.gather(*[call_url(session) for x in range(i)])
    await session.aclose()

解決問題。

這是完整的工作代碼:

import aiohttp
import asyncio
import time
import httpx

async def call_url(session):
    url = "https://services.cancerimagingarchive.net/services/v3/TCIA/query/getCollectionValues"
    response = await session.request(method='GET', url=url)
    return response

for i in range(1,5):
    start = time.time() # start time for timing event
    #async with aiohttp.ClientSession() as session: #use aiohttp
    session = httpx.AsyncClient() #use httpx
    await asyncio.gather(*[call_url(session) for x in range(i)])
    await session.aclose()
    print(f'{i} call(s) in {time.time() - start} seconds')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM