2017-08-06 5 views
1

私はコルーチンを使ってウェブページをクロールして解析したいと思っています。私はサンプルとテストを書く。このプログラムは、ubuntu 16.04のpython 3.5でうまく動くかもしれません。すべての作業が終わったら終了します。ソースコードは以下の通りです。テスト中BeautifulSoupが 'タスク例外は一度も検索されませんでした'と関連するのはなぜですか?

import aiohttp 
import asyncio 
from bs4 import BeautifulSoup 

async def coro(): 
    coro_loop = asyncio.get_event_loop() 
    url = u'https://www.python.org/' 
    for _ in range(4): 
     async with aiohttp.ClientSession(loop=coro_loop) as coro_session: 
      with aiohttp.Timeout(30, loop=coro_session.loop): 
       async with coro_session.get(url) as resp: 
        print('get response from url: %s' % url) 
        source_code = await resp.read() 
        soup = BeautifulSoup(source_code, 'lxml') 

def main(): 
    loop = asyncio.get_event_loop() 
    worker = loop.create_task(coro()) 
    try: 
     loop.run_until_complete(worker) 
    except KeyboardInterrupt: 
     print ('keyboard interrupt') 
     worker.cancel() 
    finally: 
     loop.stop() 
     loop.run_forever() 
     loop.close() 

if __name__ == '__main__': 
    main() 

、私は、「タスク例外が取得されなかった」私は「Ctrlキー+ C」でプログラムをシャットダウンすると、エラーが発生します見つけます。

^Ckeyboard interrupt 
Task exception was never retrieved 
future: <Task finished coro=<coro() done, defined at ./test.py:8> exception=KeyboardInterrupt()> 
Traceback (most recent call last): 
    File "./test.py", line 23, in main 
    loop.run_until_complete(worker) 
    File "/usr/lib/python3.5/asyncio/base_events.py", line 375, in run_until_complete 
    self.run_forever() 
    File "/usr/lib/python3.5/asyncio/base_events.py", line 345, in run_forever 
    self._run_once() 
    File "/usr/lib/python3.5/asyncio/base_events.py", line 1312, in _run_once 
    handle._run() 
    File "/usr/lib/python3.5/asyncio/events.py", line 125, in _run 
    self._callback(*self._args) 
    File "/usr/lib/python3.5/asyncio/tasks.py", line 307, in _wakeup 
    self._step() 
    File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step 
    result = coro.send(None) 
    File "./test.py", line 17, in coro 
    soup = BeautifulSoup(source_code, 'lxml') 
    File "/usr/lib/python3/dist-packages/bs4/__init__.py", line 215, in __init__ 
    self._feed() 
    File "/usr/lib/python3/dist-packages/bs4/__init__.py", line 239, in _feed 
    self.builder.feed(self.markup) 
    File "/usr/lib/python3/dist-packages/bs4/builder/_lxml.py", line 240, in feed 
    self.parser.feed(markup) 
    File "src/lxml/parser.pxi", line 1194, in lxml.etree._FeedParser.feed (src/lxml/lxml.etree.c:119773) 
    File "src/lxml/parser.pxi", line 1316, in lxml.etree._FeedParser.feed (src/lxml/lxml.etree.c:119644) 
    File "src/lxml/parsertarget.pxi", line 141, in lxml.etree._TargetParserContext._handleParseResult (src/lxml/lxml.etree.c:137264) 
    File "src/lxml/parsertarget.pxi", line 135, in lxml.etree._TargetParserContext._handleParseResult (src/lxml/lxml.etree.c:137128) 
    File "src/lxml/lxml.etree.pyx", line 324, in lxml.etree._ExceptionContext._raise_if_stored (src/lxml/lxml.etree.c:11090) 
    File "src/lxml/saxparser.pxi", line 499, in lxml.etree._handleSaxData (src/lxml/lxml.etree.c:131013) 
    File "src/lxml/parsertarget.pxi", line 88, in lxml.etree._PythonSaxParserTarget._handleSaxData (src/lxml/lxml.etree.c:136397) 
    File "/usr/lib/python3/dist-packages/bs4/builder/_lxml.py", line 206, in data 
    def data(self, content): 
KeyboardInterrupt 

私はthe offical docs of pythonを通して見ましたが、ヒントを得ていません。私はcoro()でキーボード割り込みをキャプチャしようとします。

try: 
    soup = BeautifulSoup(source_code, 'lxml') 
except KeyboardInterrupt: 
    print ('capture exception') 
    raise 

BeautifulSoup()の周囲の 'try/except'がKeyboardInterruptをキャプチャするたびに、エラーが発生します。 BeautifulSoupがエラーに寄与しているようです。しかし、それに取り組む方法は?

+1

これはBeautifulSoupとは何の関係もありません。この警告は、タスク内で発生した例外を取得しない場合に発生します。あなたはどこかの 'worker.exception()'への呼び出しを追加する必要があります。 – dirn

答えて

2

task.cancel()を呼び出すと、実際にタスクがキャンセルされるわけではなく、取り消されるタスクがマークされます。タスクをキャンセルする実際のプロセスは、タスクが実行を再開するときに開始されます。 asyncio.CancelledErrorはタスク内ですぐに持ち上げられ、実際にキャンセルされます。タスクはこの例外で実行を終了します。

一方、asyncioは、(タスクの実行結果を確認しなかった)一部のタスクが例外なく終了した場合に警告します。

(あなたがそれを必要としないので、おそらく抑制)あなたはasyncio.CancelledErrorを受けて、タスクのキャンセルを待つ必要がある問題を回避するには、次の

import asyncio 
from contextlib import suppress 


async def coro(): 
    # ... 

def main(): 
    loop = asyncio.get_event_loop() 
    worker = asyncio.ensure_future(coro()) 
    try: 
     loop.run_until_complete(worker) 
    except KeyboardInterrupt: 
     print('keyboard interrupt') 

     worker.cancel() 
     with suppress(asyncio.CancelledError): 
      loop.run_until_complete(worker) # await task cancellation. 
    finally: 
     loop.close() 

if __name__ == '__main__': 
    main() 
関連する問題