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

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

新規登録して質問してみよう
ただいま回答率
85.36%
Python 3.x

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

SSL

SSL(Secure Sockets Layer)とは、暗号化されたプロトコルで、インターネット上での通信セキュリティを提供しています。

Python

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

Q&A

解決済

2回答

4797閲覧

requestsライブラリで捕捉される例外について

jjj001

総合スコア55

Python 3.x

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

SSL

SSL(Secure Sockets Layer)とは、暗号化されたプロトコルで、インターネット上での通信セキュリティを提供しています。

Python

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

0グッド

0クリップ

投稿2021/06/16 16:09

編集2021/06/16 16:10

requestsライブラリを使用し、特定のURLへアクセスを試み、そのページがSSL化されているかどうかをチェックするプログラムを作成致しました。
該当のコードは以下になります。

def check_ssl_certification(targetURL, waitsec_from=1, waitsec_to=3): err_msg = {} headers = { "User-Agent": ( "Mozilla/5.0 (X11; Linux x86_64; rv:57.0)" "Gecko/20100101 Firefox/57.0" ) } session = requests.Session() # retries = Retry(total=1, # リトライ回数 # backoff_factor=1, # sleep時間(リトライするごとに待機時間を指定した秒数加算する) # status_forcelist=[500, 502, 503, 504],raise_on_status=False) # timeout以外でリトライするステータスコード """ Session.mount(prefix, adapter) はこのセッションで行う URL が prefix で始まる HTTP 通信に対して、 アダプター adapter を設定する関数。 prefix="https://" の場合、URL が https:// で始まるすべての通信が対象 """ # session.mount("https://", HTTPAdapter(max_retries=retries)) # session.mount("http://", HTTPAdapter(max_retries=retries)) try: # print('ok') response = session.get( url=targetURL, headers=headers, stream=True, timeout=(20.0, 30.0) ) except ssl.SSLError: print("SSL認証エラー") except ssl.CertificateError as e: print("エラー", e) except requests.exceptions.ConnectTimeout: print("タイムアウトしました。") err_msg["connection_timeout"] = "ホストへの接続に失敗しました。" except requests.exceptions.ConnectionError as e: print("ホストが見つかりませんでした(https)", e) err_msg["no_host"] = "ホストが見つかりませんでした。" return response # HTTP ステータスコードが 4xx と 5xx の場合は urllib.error.HTTPError 例外が投げられる except requests.exceptions.HTTPError as e: print(e.response.status_code) # except requests.exceptions.RetryError: # print('再試行制限に到達しました') except requests.exceptions.ReadTimeout as e: print("requests.exceptions.ReadTimeoutエラー : ", e) except requests.exceptions.TooManyRedirects as e: print("エラー: ", e) else: print("レスポンスステータス: ", response.status_code) # sys.exit() s = randint(waitsec_from, waitsec_to) sleep(s) msg_headder = "random sleep " + str(s) + "sec" print( str(datetime.datetime.now()) + "," + msg_headder + "," + response.url, file=codecs.open("requests.log", "a", "utf-8"), ) # print('response.raise_for_status()' ,response.raise_for_status()) return response return err_msg

こちらのコードなのですが、仮にSSL化されていないサイトをtargetURLに設定し、リクエストをかけてみますと、except requests.exceptions.ConnectionError as e:こちらの例外が捕捉されてしまいます。
エラー内容は以下になります。

HTTPSConnectionPool(host='リクエストを送ったURL', port=443): Max retries exceeded with url: /ec_website/login.php (Caused by SSLError(CertificateError("hostname 'リクエストを送ったURL' doesn't match either of '*.php.xdomain.ne.jp', 'php.xdomain.ne.jp'",),))

エラー内容にCaused by SSLError(CertificateErrorと表示されている通り、根本的なエラーの原因はこちらだと思う為、上記コードにおきましては、except ssl.CertificateError as e:を捕捉したいと考えています。
エラー内容で調べていましたら、こちらの記事でもCaused by~~といった形でエラーが出力されていたのですが、ここの部分のエラーを捕捉するか判定するような方法はありませんでしょうか。
ご助言頂けましたら幸いです。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

ppaulさんの回答へのコメントでやりとりするにはボリュームが大きくなってしまったので、
こちらに回答させていただきます。

except で補足した例外の__context__で、その例外の原因となった別の例外を調べることができます。なので、e.__context__.__context__ とたどっていけば大元のexceptionがわかります。

下記のようなコードで一番最初の例外までたどることができます。

python

1def check_ssl_certification(targetURL, waitsec_from=1, waitsec_to=3): 2 headers = {} 3 session = requests.Session() 4 5 try: 6 response = session.get(url=targetURL, headers=headers, stream=True, timeout=(20.0, 30.0)) 7 except requests.exceptions.ConnectionError as e: 8 i = 0 9 while e: 10 print('Level', i) 11 print(type(e)) 12 print(repr(e)) 13 if isinstance(e, ConnectionRefusedError): 14 print('**Connection Refused**') 15 elif isinstance(e, ssl.SSLError): 16 print('**SSL Error**') 17 e = e.__context__ 18 i += 1 19 20#Level 0 21#<class 'requests.exceptions.SSLError'> 22#SSLError(MaxRetryError("HTTPSConnectionPool(host='192.168.**.**', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: #CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)')))")) 23#Level 1 24#<class 'urllib3.exceptions.MaxRetryError'> 25#MaxRetryError("HTTPSConnectionPool(host='192.168.**.**', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)')))") 26#Level 2 27#<class 'ssl.SSLCertVerificationError'> 28#SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)') 29#**SSL Error**

これを試してみて思ったのですが、
except requests.exceptions.SSLError
を requests.exceptions.ConnectionError より前に書いておけば
補足できるのではないかとも思いました。

投稿2021/06/18 05:54

bsdfan

総合スコア4790

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jjj001

2021/06/18 08:11 編集

大変ご丁寧に、ご回答頂きありがとうございます。 while文で例外を辿っていく方法など、大変勉強になりました。 > except requests.exceptions.SSLError 自分そもそもこちらの例外が存在することを知らなかったです... 「 except requests.exceptions.SSLError 」こちらの例外を追加し、判定させるように実装してみたいと思います。 こちらの例外につきましては、while文で回した際に、存在を把握された形になりますでしょうか...?
bsdfan

2021/06/18 08:36

exceptionのtypeを表示したら requests.exceptions.SSLError でした。 requests.exceptions.SSLError は requests.exceptions.ConnectionError のサブクラスみたいですね。
jjj001

2021/06/18 08:42

ありがとうございます。 > requests.exceptions.SSLError は requests.exceptions.ConnectionError のサブクラスみたいですね。 こちら、そうだったのですね。 勉強になりました。
guest

0

方法はないでしょう。

エラーハンドラは後で設定した方が先に例外を捕えます。

質問のコードでssl.CertificateErrorを捕捉しようとしても、その呼び出し先のsession.getが使っているライブラリがssl.CertificateErrorを捕捉するためのエラーハンドラを設定し、ssl.CertificateErrorを捕捉してしまいます。そして、メッセージを付けてrequests.exceptions.ConnectionErrorを送出しているのですから、ssl.CertificateErrorを捕捉を捕捉することはできません。

投稿2021/06/16 17:54

ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jjj001

2021/06/17 05:03

ご助言ありがとうございます。 何か、解決策がないか探してみたいと思います...
jjj001

2021/06/17 06:56

少々思ったのですが、例えば、「SSLError」であるかどうかを知りたかった場合、「 print("ホストが見つかりませんでした(https)", e) 」こちらの変数「e」によって出力される、文字列から「SSLError」であるかどうかの確認を行うといった方法もあるでしょうかね...?
bsdfan

2021/06/17 07:10

e.__cause__ で、原因の例外にアクセスできるので、それの型を見るのはどうでしょうか。
jjj001

2021/06/17 07:15

> e.__cause__ で、原因の例外にアクセスできるので、それの型を見るのはどうでしょうか。 こちらですが、 print(e.__cause__) として出力してみたのですが、「None」となってしまっておりました。
bsdfan

2021/06/17 07:38

__cause__にはセットされていないみたいですね。 ならば e.__context__ はどうでしょうか。 すいませんが、requestsで試していないので、うまくいくかどうか、確認していません。
jjj001

2021/06/18 03:31 編集

ありがとうございます > e.__context__ こちら、原因のエラーの出力と言いますか、変数「e」を出力した時と同じ内容が出力されてしまっている状況です...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問