質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.03%

  • Python

    5512questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    4066questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Windows 7

    331questions

    Microsoft Windows 7は過去にリリースされたMicrosoft WindowsのOSであり、Windows8の1代前です。2009年の7月にリリースされ販売されました。Windows7の前はWindowsVistaで、その更に3年前にリリースされました。

Windows環境のPythonでurllib使用時のエラー

解決済

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 254

nekomata913

score 1

 前提・実現したいこと

PythonでHTTPを使用して、WEBサイトの簡易的な死活監視を行いたいです。
以下のコードをpyファイルに保存してbatファイルから5分おきに実行したところ、
約18時間後以降は下記のエラーを出力し続け、システムを再起動するまでインターネット接続が不可になりました。
メッセージからするとリソース不足のようですが、何か明示的な解放操作が必要なのでしょうか?
なお、batファイルを使用せず、python中のwhileでループするコードでも同様の症状が発生しました。
ご教示お願いします。

 該当のソースコード

import urllib.request

def check_status(url):
    try:
        with urllib.request.urlopen(url, timeout=10) as response:
            return response.code
    except urllib.error.HTTPError as e:
        return e.code
    except Exception as e:
        print(str(e))
        return -1

status = check_status('https://www.hoge.com/')
print("access status:", status)
if status != 200:
    print("access error.")
    # 疎通失敗時のアクション

 発生している問題・エラーメッセージ

access status:-1
<urlopen error [WinError 10055] システムのバッファー領域が不足しているか、またはキューがいっぱいなため、ソケット操作を実行できませんでした。>

 補足情報(FW/ツールのバージョンなど)

環境 Windows 7 professional 32bit, Python 3.6.4 32bit 

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

コード上は問題が無いように見受けられます。
例外発生時に問題があるみたいなので、訂正線を追加

urlopen error [WinError 10055]

WinSockエラー 10055なので考えられる原因は3点あります。

1,MaxUserPortの設定を行っていなく、使用可能なポートが枯渇した。
■参考情報
5000 を超える番号の TCP ポートから接続しようとすると 'WSAENOBUFS (10055)' エラーが表示される

2,ファイアウォールやアンチウィルスソフトのバグによるポート枯渇。
OFFにしてもよいなら、危険ですが一度試してみてくださいな。

3,32bit OSなためシステムのリソース不足
64bit OSで試してみてくださいな。

■参考情報
Windows ソケットのエラー コード、値、および意味]

あとはnetstat -anop tcp 3 でポートがCLOSE_WAIT状態になっているのかの確認ぐらいでしょうか。


2018/02/14追記
少し気になって調べてみましたが、urllib.request.urlopenはHTTP 1.1のKeep-Alive接続をサポートしないみたいですね。

urllib.request モジュールは HTTP/1.1 を使用し、その HTTP リクエストに Connection:close ヘッダーを含みます。

よって接続するたびに新規ソケットが生成されるとのこと。

with urllib.request.urlopen(url, timeout=10) as response:
    # response ヘッダーを表示
    # ('Connection', 'close')が表示されると思います。
    print(response.getheaders())
    return response.code

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/13 23:54

    回答ありがとうございます。2,3は環境の都合上試せていませんが、1の対処を行い、実行間隔を5秒に縮めて確認したところ、12時間経過時点で問題なく動作しています。このまま様子を見たいと思います。

    キャンセル

  • 2018/02/14 00:34

    ダウンタイム許容時間が何分で何時間以上継続稼働する予定のプログラムか分かりませんが、
    時間がある時にProcess Exploperやgc.set_debug(gc.DEBUG_LEAK) などで、本質的な原因調査をされたほうが良いと思います。

    キャンセル

  • 2018/02/14 03:10

    ありがとうございます。上記のプログラムは補助的かつ短期間の用途ですので、現状は許容範囲内ですが、気にはなりますので調べたいと思います。

    キャンセル

  • 2018/02/14 23:42

    なるほど、urllib.request.urlopenの仕様として新規ソケットを生成し続けた結果、空きローカルポートが枯渇した、ということでしょうか。対処について調べたところ、MaxUserPortとTcpTimedWaitDelayの設定により改善したケースが多いようですね。現在はMaxUserPort=65534,TcpTimedWaitDelay=60に変更し、とくに問題なく動作しているようです。

    キャンセル

  • 2018/02/15 00:50 編集

    最初の回答でMaxUserPortの事を「原因」という言葉を使ってしまったのが良くなかったのですが。
    本来は「対処法」というのが正しい表現です。

    新規ソケットを生成し続けたのは質問文の症状発生の「起因」だと思いますが。
    質問文の「5分おきに実行」という点が気になってます。
    TcpTimedWaitDelay(240秒=4分)が経過したら、ポート番号がいつかは再利用されるため。
    もう一箇所どこかに「原因」があるのではないかと推測しています。
    netstat で接続先のHOSTがESTABLISHED状態のポートが複数無いことを念のため確認してみてくださいな。

    キャンセル

  • 2018/02/16 00:33

    確認してみましたが、それらしい接続は見当たらないですね…。当初とまったく同じ条件下、というわけではないですが。せめてMaxUserPortとTcpTimedWaitDelayを戻せば、もう少し実のある調査ができそうですが、すみません、ちょっとそこまで手が回ってないですね。

    キャンセル

  • 2018/02/16 15:17

    なるほど、調査ありがとうございました。

    キャンセル

+1

withステートメントは__enter__関数と__exit__関数のラッパーになっていますが、この内__enter__関数内でエラーが発生した場合に__exit__関数は呼ばれません(python3.6.3で確認)。
この場合は安全のために、withを使わずに明示的にオープン・クローズをした方がいいかと思います。
修正例としてはこのようなコードになります。

import urllib.request

def check_status(url):
    try:
        response = urllib.request.urlopen(url, timeout=10)
        return response.code
    except urllib.error.HTTPError as e:
        return e.code
    except Exception as e:
        print(str(e))
        return -1
    finally:
        response.close()

status = check_status('https://www.hoge.com/')
print("access status:", status)
if status != 200:
    print("access error.")
    # 疎通失敗時のアクション


urlopen関数の怖いところは、標準のopensocket.socket.connectなどと違って名前解決が出来なかった、そもそもソケットが作れなかった等の場合を除きエラーが発生するときには既に接続が確立していることです。
もし、標準以外のライブラリを使うことに抵抗が無いならurllibではなくrequests等の高水準ライブラリを使うとよいと思います。
一応サンプルコードもおいときますね。

from requests import get

def check_status(url):
    #hostが見つからない場合、404が返ります
    return get(url, timeout=10).status_code

status = check_status('https://www.hoge.com/')
print("access status:", status)
if status != 200:
    print("access error.")
    # 疎通失敗時のアクション

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/13 23:58

    回答ありがとうございます。withのくだりは知りませんでした。勉強になります。requestsは環境制約上使用を見送りましたが、HTTPを扱う際には使用を検討したいと思います。

    キャンセル

  • 2018/02/14 00:35

    自分も実際に遭遇するまで知りませんでした^^);

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 91.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python

    5512questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    4066questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Windows 7

    331questions

    Microsoft Windows 7は過去にリリースされたMicrosoft WindowsのOSであり、Windows8の1代前です。2009年の7月にリリースされ販売されました。Windows7の前はWindowsVistaで、その更に3年前にリリースされました。