Piszę pająka, aby indeksować strony internetowe. Wiem, że asyncio może mój najlepszy wybór. Używam więc coroutines do asynchronicznego przetwarzania pracy. Teraz podrapuję się, jak wyjść z programu przez przerwanie klawiatury. Program może zamknąć się dobrze po wykonaniu wszystkich prac. Kod źródłowy może być uruchamiany w pythonie 3.5 i jest podany poniżej.Jak zgrabnie zamknąć procesory za pomocą 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()
Ale jeśli nacisnąć „Ctrl + C”, podczas gdy jest uruchomiony, może wystąpić jakieś dziwne błędy. Chodzi mi o to, że czasami program można zamknąć z wdziękiem za pomocą "Ctrl + C". Brak komunikatu o błędzie. Jednak w niektórych przypadkach program będzie nadal działał po naciśnięciu klawisza "Ctrl + C" i nie zostanie zatrzymany, dopóki wszystkie prace nie zostaną wykonane. Jeśli w tym momencie naciśnie "Ctrl + C", "Zadanie zostało zniszczone, ale jest w toku!" będzie tam.
Przeczytałem kilka tematów o asyncio i dodałem trochę kodu w main(), aby z wdziękiem zamknąć blogi. Ale to nie działa. Czy ktoś ma podobne problemy?
Przypuszczam, że masz rację. "except: pass" jest przypadkiem! Dodaję "raise" po "pass" w "except:" i mogłoby wyjść dobrze z "Ctrl + C". Więc jeśli chcę rejestrować błędy, powinienem przejąć wyjątki, aby main() mógł złapać te wyjątki, w tym asyncio.CancelledError. Ale wciąż nie rozumiem, dlaczego oryginalny kod mógł wyjść dobrze z "Ctrl + C" z niepewnym prawdopodobieństwem? Jeśli struktura "try-except" w fetch() może przechwycić wszystkie wyjątki, main() nic nie zrobi, w konsekwencji błąd wystąpi za każdym razem. – xssl
@xssl, zaktualizowałem odpowiedź, aby pokazać, co może się zdarzyć w różnych przypadkach. –