簡體   English   中英

利用 asyncio 生成器和 asyncio.as_completed

[英]Utilizing asyncio generators and asyncio.as_completed

我有一些代碼用於抓取url,解析信息,然后使用 SQLAlchemy 將其放入數據庫中。 我正在嘗試異步執行此操作,同時限制同時請求的最大數量。

這是我的代碼:

async def get_url(aiohttp_session, url1, url2):
    async with session.get(url1) as r_url1:
       if r_url1.status == 200:
          async with session.get(url2) as r_url2:
             if r_url2.status == 200:
                return await r_url1.json(), await r_url2.json()

async def url_generator(formatted_start_date, formatted_end_date, machine_id, interval):
    interval_start = formatted_start_date
    interval_end = formatted_start_date + interval

    while interval_end <= formatted_end_date:
        yield (f"https://example.org/start={interval_start}"
               f"Start={datetime.strftime(interval_start, DATETIME_FORMAT)}"
               f"&End={datetime.strftime(interval_end, DATETIME_FORMAT)}"
               f"&machines={machine_id}",
               f"https://example.org/start={interval_start}"
               f"Start={datetime.strftime(interval_start, DATETIME_FORMAT)}"
               f"&End={datetime.strftime(interval_end, DATETIME_FORMAT)}"
               f"&machines={machine_id}&groupby=Job"
               )
        interval_start += interval
        interval_end += interval

async def parse(database, url1_json, url2_json):
    """ Do some parsing and save it using credentials stored in the database object """


def main(database, formatted_start_date, formatted_end_date, machine_id, interval):
    async for url1_json, url2_json in asyncio.as_completed(url_generator(formatted_start_date, formatted_end_date, machine_id, interval)):
         parse(database, url1_json, url2_json)

我得到的錯誤yield from should be used as context manager expression

我已經嘗試查看此處的文檔以及同步原語,但仍然對我哪里出錯以及我應該如何從我的生成器創建任務感到困惑。

貼出的代碼有幾個問題:

  • 您正在嘗試將as_completed用作異步迭代器,並使用async for迭代其結果。 但是, as_completed不返回異步迭代器(至少還沒有),並且必須使用常規for進行迭代,並明確地等待每個產生的 object,如文檔中所示

  • 您將異步迭代器傳遞給as_completed ,而它接受普通容器或(常規)可迭代。

  • 您在未使用async def定義的 function 中使用async for ,這應該是語法錯誤。 此外, parse()被定義為協程,您無需等待它。

好消息是,由於url_generator已經是一個生成器,你根本不需要as_completed ,你應該能夠迭代它:

async def main(database, formatted_start_date, formatted_end_date,
               machine_id, interval):
    async for url1_json, url2_json in url_generator(
            formatted_start_date, formatted_end_date,
            machine_id, interval)):
        await parse(database, url1_json, url2_json)

但是請注意, async for不會自動並行化迭代,它只會允許其他協程與迭代的協程並行運行。 要並行化迭代,您需要調用create_task來並行提交任務,並使用asyncio.Semaphore來限制並行任務的數量。 例如:

async def parse(database, url1_json, url2_json, limit):
    # async with applied to a semaphore ensures that no more than N
    # coroutines that use the same semaphore enter the "with" block
    # in parallel
    async with limit:
        ... code goes here ...

async def main(database, formatted_start_date, formatted_end_date,
               machine_id, interval):
    limit = asyncio.Semaphore(10)

    # create all coroutines in advance using create_task
    # and run them in parallel, relying on the semaphore
    # limit the number of simultaneous requests
    tasks = []
    async for url1_json, url2_json in url_generator(
            formatted_start_date, formatted_end_date,
            machine_id, interval)):
        # this create_task just creates the task - it will
        # start running when we return to the event loop
        tasks.append(asyncio.create_task(parse(database, url1_json, url2_json, limit))

    # suspend to the event loop, resuming this coroutine only after
    # all the tasks have finished (or any of them raises)
    await asyncio.gather(*tasks)

請注意, url_generator不需要是異步的,因為它不需要await任何東西。 您可以使用def定義它並使用for對其進行迭代。

暫無
暫無

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

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