エラーSSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLEDへの対応

ドバイのコワーキングスペースで書いています。

新しいプロジェクトの予備的な分析のためにスクレイピングでデータを集めようと2年前に書いたコードを走らせたら動かなくなっていた。[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED]とのことで、SSL通信周りのエラーっぽい。requestsでURL指定してgetしただけなんだけど、おおよそのエラー内容を貼っておく。

SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007)

During handling of the above exception, another exception occurred:

SSLError                                  Traceback (most recent call last)
SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007)

The above exception was the direct cause of the following exception:

MaxRetryError                             Traceback (most recent call last)
MaxRetryError: HTTPSConnectionPool(host='www.cosme.net', port=443): Max retries exceeded with url: /product/product_id/10010221/top (Caused by SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007)')))

During handling of the above exception, another exception occurred:

SSLError                                  Traceback (most recent call last)
/usr/local/lib/python3.10/dist-packages/requests/adapters.py in send(self, request, stream, timeout, verify, cert, proxies)
    515             if isinstance(e.reason, _SSLError):
    516                 # This branch is for urllib3 v1.22 and later.
--> 517                 raise SSLError(e, request=request)
    518 
    519             raise ConnectionError(e, request=request)

SSLError: HTTPSConnectionPool(host='www.cosme.net', port=443): Max retries exceeded with url: /product/product_id/10010221/top (Caused by SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007)')))

要はSSLで通信するのに古臭い方法でアタックかけてるのが悪いわけですね。ということでサクッと直す。

たとえば、Googleのトップページ(https://google.co.jp)を取ってきたければこんな感じになる。

import requests, ssl, urllib3
url = 'https://www.google.co.jp/'

class CustomHttpAdapter (requests.adapters.HTTPAdapter):
    def __init__(self, ssl_context=None, **kwargs):
        self.ssl_context = ssl_context
        super().__init__(**kwargs)

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = urllib3.poolmanager.PoolManager(
            num_pools=connections, maxsize=maxsize,
            block=block, ssl_context=self.ssl_context)

session = requests.session()
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
ctx.options |= 0x4
session.mount('https://', CustomHttpAdapter(ctx))
res = session.get(url)

print(res.content)

これは2行目のurlの変数の値を変えるだけで動くようになったので、ついでに関数化しておく。

import requests, ssl, urllib3

class CustomHttpAdapter (requests.adapters.HTTPAdapter):
    def __init__(self, ssl_context=None, **kwargs):
        self.ssl_context = ssl_context
        super().__init__(**kwargs)

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = urllib3.poolmanager.PoolManager(
            num_pools=connections, maxsize=maxsize,
            block=block, ssl_context=self.ssl_context)

def get_url(url='https://www.google.co.jp/'):
    session = requests.session()
    ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
    ctx.options |= 0x4
    session.mount('https://', CustomHttpAdapter(ctx))
    res = session.get(url)
    return res.content

get_url('https://stackoverflow.com/questions/71603314/ssl-error-unsafe-legacy-renegotiation-disabled')

これでget_url()に欲しいURL指定するだけで指定URLの中身を取ってくるようになる。

やっぱ昔書いたコードの保守ってめんどくさいなあ。まあこれはこの先も使うだろうから仕方ないけどね。

今回のエラー対応は以下の内容を参考にしました。

SSL error unsafe legacy renegotiation disabled – StackOverflow (https://stackoverflow.com/questions/71603314/ssl-error-unsafe-legacy-renegotiation-disabled)