urllib.URLopenerのエラーを詳しく検出する
タイムアウトの設定についてはPython 2.6以降urllib.urlopenerのオプション引数でサポートされるようになりました。後ほど書き直します。
urllib.URLopenerはすべてのエラーをIOErrorで返します。except文はひとつで済むのですが、DNSのエラー、タイムアウト、URLの不正形式からHTTPサーバにおけるNotFound、認証要求、リダイレクトまで全部一緒に扱われてしまいます。エラーの内容によって処理を振り分けたい場合、error valueを取って調べることになります。
e.errno e.strerror e.args ============== ========== ========================================= 'url error' '...' ('url error', '...') 'socket error' *1 ('socket error', '...') 'socket error' *1 ('socket error', 'timed out') None None ('http error', STATUS, '...', MIMEOBJECT) *1 ... socket関係のエラーはsocket.gaierrorのインスタンス、 タイムアウトのときはsocket.timeoutのインスタンスが入る
error valueの値は大きく分けて3種類となり、それぞれurllib・socket・httplibの各モジュールで発生したエラーであることを示しています。socketについてはさらにネットワーク接続不可とタイムアウトの2種類に原因を切り分けることができます。
from urllib import URLopener import socket opener = URLopener() url = '...' socket.setdefaulttimeout(10.0) try: f = opener.open(url) except IOError, e: if e.args[0] == 'http error': print 'サーバに接続しましたが、ステータス%dが返されました' % e.args[1] elif e.args[0] == 'url error': print 'URLが不正です - %s' % e.args[1] elif e.args[0] == 'socket error': if isinstance(e.strerror, socket.timeout): print '指定時間内に応答がありません' elif isinstance(e.strerror, socket.gaierror): print 'サーバに接続できません - %s' % e.args[1] else: print '通信関係のエラー - %s' % e.args[1] else: print 'その他のエラー - %s' % e.args[0]
なお、HTTPのステータスによって処理を振り分ける場合はurllib.FancyURLopenerを使った方がいいでしょう。このサブクラスとしてopenerを定義すれば各HTTPステータスに対応するメソッドを用意できますし、標準でリダイレクトの処理まで行ってくれます。