私はウェブページをクロールするためにスパイダーを書いています。私はasyncioが多分私の最高の選択を知っています。だから、コルーチンを使って作業を非同期に処理します。今私はキーボード割り込みによってプログラムを終了する方法について私の頭を傷つける。すべての作業が終わった後、プログラムは正常に終了する可能性があります。ソースコードはPython 3.5で動かすことができ、以下にアタッチされています。Ctrl + Cでコルーチンを正常にシャットダウンするには?
import asyncio
import aiohttp
from contextlib import suppress
class Spider(object):
def __init__(self):
self.max_tasks = 2
self.task_queue = asyncio.Queue(self.max_tasks)
self.loop = asyncio.get_event_loop()
self.counter = 1
def close(self):
for w in self.workers:
w.cancel()
async def fetch(self, url):
try:
async with aiohttp.ClientSession(loop = self.loop) as self.session:
with aiohttp.Timeout(30, loop = self.session.loop):
async with self.session.get(url) as resp:
print('get response from url: %s' % url)
except:
pass
finally:
pass
async def work(self):
while True:
url = await self.task_queue.get()
await self.fetch(url)
self.task_queue.task_done()
def assign_work(self):
print('[*]assigning work...')
url = 'https://www.python.org/'
if self.counter > 10:
return 'done'
for _ in range(self.max_tasks):
self.counter += 1
self.task_queue.put_nowait(url)
async def crawl(self):
self.workers = [self.loop.create_task(self.work()) for _ in range(self.max_tasks)]
while True:
if self.assign_work() == 'done':
break
await self.task_queue.join()
self.close()
def main():
loop = asyncio.get_event_loop()
spider = Spider()
try:
loop.run_until_complete(spider.crawl())
except KeyboardInterrupt:
print ('Interrupt from keyboard')
spider.close()
pending = asyncio.Task.all_tasks()
for w in pending:
w.cancel()
with suppress(asyncio.CancelledError):
loop.run_until_complete(w)
finally:
loop.stop()
loop.run_forever()
loop.close()
if __name__ == '__main__':
main()
実行中に 'Ctrl + C'を押すと、奇妙なエラーが発生することがあります。私はときどきプログラムが正常に 'Ctrl + C'でシャットダウンできることを意味します。エラーメッセージは表示されません。しかし、場合によっては、 'Ctrl + C'を押してもプログラムが実行されていて、すべての作業が完了するまで停止しないことがあります。その時点で「Ctrl + C」を押すと、「タスクは破棄されましたが保留中です!」そこにいるだろう。
私はasyncioに関するいくつかのトピックを読んでおり、コルーチンを正常に閉じるためにmain()にいくつかのコードを追加しました。しかし、それは動作しません。他の誰かにも同様の問題がありますか?
あなたが正しいと思います。 'except:pass'の場合です!私は 'except'に 'pass'の後に 'raise'を追加し、 'Ctrl + C'でうまく終了することができます。したがって、エラーをログに記録する場合は、main()がasyncio.CancelledErrorなどのこれらの例外をキャッチできるように、例外を再評価する必要があります。しかし、私はまだ、元のコードが不確実な確率で 'Ctrl + C'でうまく終了できない理由をほとんど理解していませんか? fetch()の 'try-except'構造体がすべての例外を捕捉できる場合、main()は何も取得しません。その結果、エラーは毎回発生します。 – xssl
@xssl、別のケースで何が起こる可能性があるかを示す答えを更新しました。 –