异步等待函数中的 Python asyncio.semaphore

Posted

技术标签:

【中文标题】异步等待函数中的 Python asyncio.semaphore【英文标题】:Python asyncio.semaphore in async-await function 【发布时间】:2016-11-28 03:34:02 【问题描述】:

我正在尝试自学 Python 的异步功能。为此,我构建了一个异步网络爬虫。我想限制我一次打开的连接总数,以便成为服务器上的好公民。我知道信号量是一个很好的解决方案,并且 asyncio 库内置了一个 semaphore 类。我的问题是 Python 在 async 函数中使用 yield from 时会抱怨,因为您正在组合 yieldawait句法。以下是我正在使用的确切语法...

import asyncio
import aiohttp

sema = asyncio.BoundedSemaphore(5)

async def get_page_text(url):
    with (yield from sema):
        try:
            resp = await aiohttp.request('GET', url)
            if resp.status == 200:
                ret_val = await resp.text()
        except:
            raise ValueError
        finally:
            await resp.release()
    return ret_val

引发此异常:

File "<ipython-input-3-9b9bdb963407>", line 14
    with (yield from sema):
         ^
SyntaxError: 'yield from' inside async function

我能想到的一些可能的解决方案...

    只需使用 @asyncio.coroutine 装饰器 使用线程。信号量?这似乎可能会导致其他问题 出于this 原因,在 Python 3.6 的 Beta 版中尝试此操作。

我对 Python 的异步功能非常陌生,所以我可能会遗漏一些明显的东西。

【问题讨论】:

python3.7 不允许将yield from 与关键字async 一起使用。 yield, yield from 是和装饰器一起使用的 @asyncio.coroutine BoundedSemaphore(5) 是否意味着您每秒只发出 5 个请求?或者是如何工作的? 【参考方案1】:

您可以使用async with 语句来获取异步上下文管理器:

#!/usr/local/bin/python3.5
import asyncio
from aiohttp import ClientSession


sema = asyncio.BoundedSemaphore(5)

async def hello(url):
    async with ClientSession() as session:
        async with sema, session.get(url) as response:
            response = await response.read()
            print(response)

loop = asyncio.get_event_loop()
loop.run_until_complete(hello("http://httpbin.org/headers"))

示例取自 here。总的来说,该页面也是asyncioaiohttp 的良好入门。

【讨论】:

我觉得这个例子很有趣,但是有些东西让我感到惊讶。就是这一行:async with sema, session.get(url) as response: 你怎么在同一个上下文管理器上使用 sema, session...?那条线有什么作用?你能解释一下吗? 那里没什么特别的,我认为这个答案会有所帮助:***.com/questions/3024925/… 如何使用信号量与限制连接池大小不同 - docs.aiohttp.org/en/stable/… 为什么BoundedSemaphore 接受参数5?我们可以发送不同的输入吗?【参考方案2】:

好的,所以这真的很愚蠢,但我只是在信号量上下文管理器中将 yield from 替换为 await,它运行良好。

sema = asyncio.BoundedSemaphore(5)

async def get_page_text(url):
    with (await sema):
        try:
            resp = await aiohttp.request('GET', url)
            if resp.status == 200:
                ret_val = await resp.text()
        except:
            raise ValueError
        finally:
            await resp.release()
    return ret_val

【讨论】:

更好的是,您可以使用async with 声明:async with sema: [...] 异步对我来说也很有趣,我发现您的主题很有趣,并投票决定将其保留在这里。顺便说一句,你能在 github 上分享 ex 的整个代码吗? 是的,我会这样做并稍后在 cmets 中发布一个链接。 @EugeneLisitsky 这是代码...github.com/brucepucci/asyncio_scraper。我正在尝试获取此文件结构中以“gid”开头的所有链接...gd2.mlb.com/components/game/mlb/year_2016

以上是关于异步等待函数中的 Python asyncio.semaphore的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中等待之前,不会在异步函数中调用 print 函数

ApiController Post 中的异步和等待

Redux 中的异步/等待

Promise.all()函数中的JS“等待仅在异步函数中有效”[重复]

如何在同步函数中等待 JavaScript 中的异步调用?

了解python socket io / aiohttp服务器中的异步等待