前言
换用mac之后主要在写java,python虽然刚拿到机子就装到了电脑上但却一直没怎么用过。今晚做东西需要用到nltk,但是下载nltk_data时却报错connect refuse,多番排查之后发现是ssl证书的问题。
解决
问题原因
从python 3.6起,python有一个内置的OpenSSL,没有使用mac自带的OpenSSL,所以导致了python的ssl证书和系统的证书并不一致,发起ssl连接时因证书问题而出错。
解决方案
既然定位到了问题,解决起来还是很简单的,我们只要让python内置的OpenSSL该用系统证书即可。
Install Certificates.command
为了解决这个问题,官方为我们提供了一个工具Install Certificates.command
cd "/Applications/Python 3.7/" sudo "./Install Certificates.command"
但是我的python是用brew装的,并没有安装python GUI,所以也不存在上述的目录,自然是无法解决问题的。
手动执行Install Certificates.command
我在github上找到了Install Certificates.command
的源码,阅读理解了其中的逻辑之后发现其实手动执行也是可行的,所以就复制到了本地打算手动执行。
Install Certificates.command# install_certifi.py # # sample script to install or update a set of default Root Certificates # for the ssl module. Uses the certificates provided by the certifi package: # https://pypi.python.org/pypi/certifi import os import os.path import ssl import stat import subprocess import sys STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH ) def main(): openssl_dir, openssl_cafile = os.path.split( ssl.get_default_verify_paths().openssl_cafile) print(" -- pip install --upgrade certifi") subprocess.check_call([sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]) import certifi # change working directory to the default SSL directory os.chdir(openssl_dir) relpath_to_certifi_cafile = os.path.relpath(certifi.where()) print(" -- removing any existing file or link") try: os.remove(openssl_cafile) except FileNotFoundError: pass print(" -- creating symlink to certifi certificate bundle") os.symlink(relpath_to_certifi_cafile, openssl_cafile) print(" -- setting permissions") os.chmod(openssl_cafile, STAT_0o775) print(" -- update complete") if __name__ == '__main__': main()
写成python脚本直接sudo执行其实也是可以的,但是我又遇到了新问题:openssl_dir指向的目录又不存在,脚本异常退出。
最终成功方案
首先,我在终端打印出来了openssl_dir, openssl_cafile两个变量的值
打印出来的路径是/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/etc/ssl
一找才发现这个目录到/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7
就断了,etc/ssl
根本不存在,于是就有了下面的解决办法。
这个目录时python内置ssl证书的默认路径,那我其实可以创建好这个路径然后把它软链到系统证书路径,这样两边的证书就统一了。# 确保这个目录存在 cd /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/ sudo mkdir etc # 软链接到系统的ssl证书 sudo ln -s /etc/ssl ./etc
经过测试,可以正常使用。
后记
一般来说,这种问题的解决过程我是不会去专门写东西的(主要是懒!)。但是这次这个问题直接搜索基本上只能得到解决方案1,然后大概又要像我一样绕这么一圈才能解决问题。为了方便之后遇到问题的人(当然也包括我自己),在此记录。