我已经构建了一个基于 aiohttp 的小蜘蛛。这是一些缩写代码:
import asyncio
from aiohttp import ClientSession
from threading import Thread
class Spider:
def __init__(self, urls):
self.urls = urls
self.start()
async def fetch(self, session, url):
async with session.get(url) as response:
await self.handle_status(response) # undefined here for brevity
return await self.render_body(response) # undefined here for brevity
async def process_urls(self):
async with ClientSession() as session:
tasks = {self.fetch(session, url) for url in self.urls}
for task in asyncio.as_completed(tasks):
raw_data = await task
data = self.extract_data(*raw_data) # sync method undefined here for brevity
await self.store_data(data) # undefined here for brevity
def start(self) -> None:
try:
asyncio.run(self.process_urls())
except RuntimeError: # loop already running
x = Thread(target=asyncio.run, args=(self.process_urls(),))
x.start()
x.join()
start 方法旨在启动一个 asyncio 循环,但如果已经在运行,则它会在新线程中启动一个新循环。
无论是否从现有循环运行,代码都有效。但是如果从现有循环运行(例如使用 pytest.mark.asyncio()),我会收到此警告:
<块引用>RuntimeWarning: coroutine 'Spider.process_urls' 从未被等待 x.join()
这个警告是我应该关注的吗?
有没有更好的方法来处理这个不会导致这个警告的?
我试过 loop = asyncio.get_running_loop() 和 loop.create_task(self.process_urls()) 而不是创建一个新线程,但是失败了:
<块引用>运行时错误:协程忽略了 GeneratorExit
我尝试过 nest-asyncio,但我的主要用例涉及另一个使用 uvloop 的库,因此 nest-asyncio 不兼容。