源自于前几天对一批转录本的批量化操作的一些记录。

即想要通过 Python 在线获取某个转录本对应的基因 Symbol 时,发现出现 SSL 无法获取本地证书:unable to get local issuer certificate (_ssl.c:1056)

  1. >>> from Bio import SeqIO
  2. >>> from Bio import Entrez
  3. >>> Entrez.email = "A.N.Other@example.com"
  4. >>> records = SeqIO.parse(Entrez.efetch(id='NM_001009537.4', db="nucleotide", rettype="gb", retmode="text"), "gb")
  5. Traceback (most recent call last):
  6. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 1317, in do_open
  7. encode_chunked=req.has_header('Transfer-encoding'))
  8. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/http/client.py", line 1229, in request
  9. self._send_request(method, url, body, headers, encode_chunked)
  10. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/http/client.py", line 1275, in _send_request
  11. self.endheaders(body, encode_chunked=encode_chunked)
  12. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/http/client.py", line 1224, in endheaders
  13. self._send_output(message_body, encode_chunked=encode_chunked)
  14. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/http/client.py", line 1016, in _send_output
  15. self.send(msg)
  16. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/http/client.py", line 956, in send
  17. self.connect()
  18. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/http/client.py", line 1392, in connect
  19. server_hostname=server_hostname)
  20. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/ssl.py", line 412, in wrap_socket
  21. session=session
  22. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/ssl.py", line 853, in _create
  23. self.do_handshake()
  24. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/ssl.py", line 1117, in do_handshake
  25. self._sslobj.do_handshake()
  26. ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
  27. During handling of the above exception, another exception occurred:
  28. Traceback (most recent call last):
  29. File "<stdin>", line 1, in <module>
  30. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/site-packages/Bio/Entrez/__init__.py", line 184, in efetch
  31. return _open(cgi, variables, post=post)
  32. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/site-packages/Bio/Entrez/__init__.py", line 543, in _open
  33. handle = _urlopen(cgi)
  34. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 222, in urlopen
  35. return opener.open(url, data, timeout)
  36. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 525, in open
  37. response = self._open(req, data)
  38. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 543, in _open
  39. '_open', req)
  40. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 503, in _call_chain
  41. result = func(*args)
  42. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 1360, in https_open
  43. context=self._context, check_hostname=self._check_hostname)
  44. File "/Bioinfo/Pipeline/SoftWare/Python-3.7.3/lib/python3.7/urllib/request.py", line 1319, in do_open
  45. raise URLError(err)
  46. urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)>

根据网络上的解析,当使用 urllib.urlopen 打开一个 https 链接时,会验证一次 SSL 证书。而当目标网站使用的是自签名的证书时就会抛出如下异常:

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)

解决方法也很简单,下面是个人简单总结的 3 种常用解决方法。

方法一,全局取消证书验证

  1. >>> import ssl
  2. >>> ssl._create_default_https_context = ssl._create_unverified_context

方法二,指定位置安装证书

  1. 查看证书默认位置。

    1. >>> import ssl
    2. >>> print(ssl.get_default_verify_paths())
    3. DefaultVerifyPaths(cafile=None, capath='/Bioinfo/Pipeline/SoftWare/LibDependence/openssl-1.1.1/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/Bioinfo/Pipeline/SoftWare/LibDependence/openssl-1.1.1/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/Bioinfo/Pipeline/SoftWare/LibDependence/openssl-1.1.1/ssl/certs')

    由于在 openssl_cafile 中指定的 ca 文件(cert.pem)不存在,所以导致上面的错误。

  2. 下载 ca 文件,将下载的 ca 文件放到 openssl_cafile 指定位置。注意,如果放到 openssl_capath 目录下还会出现类似的问题,一定要放到 openssl_cafile 指定的位置。

    1. $ cd /Bioinfo/Pipeline/SoftWare/LibDependence/openssl-1.1.1/ssl
    2. $ wget http://curl.haxx.se/ca/cacert.pem -O cert.pem --no-check-certificate

方法三,设置环境变量

也可以参考 stackoverflow 上的做法,通过修改下面环境变量的方式解决(亲测可行)。

  1. export SSL_CERT_FILE=/usr/local/etc/openssl/cert.pem
  2. export REQUESTS_CA_BUNDLE=/usr/local/etc/openssl/cert.pem

最后,问题解决。

  1. >>> from Bio import SeqIO
  2. >>> from Bio import Entrez
  3. >>> Entrez.email = "A.N.Other@example.com"
  4. >>> records = SeqIO.parse(Entrez.efetch(id='NM_001009537.4', db="nucleotide", rettype="gb", retmode="text"), "gb")
  5. >>> for record in records:
  6. ... for feature in record.features:
  7. ... if feature.type == "gene":
  8. ... print(feature.qualifiers['gene'])
  9. ...
  10. ['Zfp799']