18

我正在尝试自学 Python 的异步功能。为此,我构建了一个异步网络爬虫。我想限制我一次打开的连接总数,以便成为服务器上的好公民。我知道信号量是一个很好的解决方案,并且 asyncio 库内置了一个信号量类。我的问题是 Pythonyield fromasync函数中使用时会抱怨,因为你正在组合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

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

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

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

4

2 回答 2

22

您可以使用该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"))

示例取自这里。该页面也是一个很好的入门asyncio读物aiohttp

于 2016-11-28T13:09:44.160 回答
7

好的,所以这真的很愚蠢,但我只是在信号量上下文管理器中替换yield fromawait,它工作得很好。

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
于 2016-11-28T03:45:18.073 回答