zoukankan      html  css  js  c++  java
  • 如何解决Requests的SSLError(转)

    add by zhj: 我使用方法2“更新系统的certificate”解决了问题

    原文:https://www.jianshu.com/p/8deb13738d2c

    这两天在Linux上爬Google Play的app列表时,发现之前的脚本不能用了,总是报SSLError。花了一天的时间进行定位,虽然还是不明白为什么脚本之前能用,现在却不能用,但总算找到了解决办法。记录一下解决思路和过程,以供他人遇到类似问题时参考。

    问题

    脚本是用Python 3.4写的,用到了一个开源的库play-scraper,调用其collectionAPI来获取Google Play的Top App列表。该库使用了requests作为客户端来对Google Play进行操作。当脚本执行时,会报如下错误:certificate verify failed。

    Traceback (most recent call last):
      File "/home/me/py3.4/lib/python3.4/site-packages/urllib3/connectionpool.py", line 600, in urlopen
        chunked=chunked)
      File "/home/me/py3.4/lib/python3.4/site-packages/urllib3/connectionpool.py", line 345, in _make_request
        self._validate_conn(conn)
      File "/home/me/py3.4/lib/python3.4/site-packages/urllib3/connectionpool.py", line 844, in _validate_conn
        conn.connect()
      File "/home/me/py3.4/lib/python3.4/site-packages/urllib3/connection.py", line 326, in connect
        ssl_context=context)
      File "/home/me/py3.4/lib/python3.4/site-packages/urllib3/util/ssl_.py", line 325, in ssl_wrap_socket
        return context.wrap_socket(sock, server_hostname=server_hostname)
      File "/usr/local/lib/python3.4/ssl.py", line 365, in wrap_socket
        _context=self)
      File "/home/me/py3.4/lib/python3.4/site-packages/gevent/_ssl3.py", line 232, in __init__
        raise x
      File "/home/me/py3.4/lib/python3.4/site-packages/gevent/_ssl3.py", line 228, in __init__
        self.do_handshake()
      File "/home/me/py3.4/lib/python3.4/site-packages/gevent/_ssl3.py", line 545, in do_handshake
        self._sslobj.do_handshake()
    ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/home/me/py3.4/lib/python3.4/site-packages/requests/adapters.py", line 440, in send
        timeout=timeout
      File "/home/me/py3.4/lib/python3.4/site-packages/urllib3/connectionpool.py", line 630, in urlopen
        raise SSLError(e)
    urllib3.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:600)
    

    定位过程

    仔细分析Traceback,发现问题出在def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None)中。注意verify参数,默认为True。在play-scraper中也是将其设为True的,说明在SSL握手过程中要验证certificate的。

    Google了一下错误信息,大致有以下几种解决方法:

    1. 将verify设为False,不验证certificate

    参考:https://stackoverflow.com/a/30373147/2510797
    简单粗暴,但是有效。不报错误了,但总是有Insecure request的告警。对于有代码洁癖的本人来说,这显然是不能接受的,除非时间非常紧迫。继续定位。

    2. 更新系统的certificate。

    参考:https://stackoverflow.com/a/24212501/2510797

    sudo apt-get install ca-certificates
    

    看了一下所用Linux系统的ca-certificates package,确实比较老了,但之前一直没有问题。死马当活马医试试吧,但问题依旧。

    3. 指定系统certificate的路径

    参考: https://stackoverflow.com/a/16085737/2510797
    Linux系统certificate的certificate路径在/etc/ssl/certs。使用verify="/etc/ssl/certs"试试,发现确实不报错误了。但是这个方法的弊端也是显而易见:play-scraper并没有在API中提供传入参数verify,必须要修改其代码才行。不同的操作系统,其certificate存放的位置肯定不一样,要是代码支持跨平台,就需要判断操作系统的类型,然后传入相应的verify值。对于一个相对使用比较广泛的requests库来说,这么做显然不太合理。

    4. 使用certifi的certitificate路径

    参考:https://stackoverflow.com/a/35791445/2510797
    看了一下requests的文档,发现它使用了certifi package。然后再去看certifi的文档,发现其certificate路径有两个:certifi.where()certifi.old_where()。快速浏览了一下requests的源码,发现如果verify=True的话,所用的certificate就是certifi.where(),所以就试了一下old_where(),居然不报错了。但看到certifi的文档中建议尽量不要用old_where(),所以还是不甘心,继续定位。

    5. 安装requests的security extras

    参考:https://stackoverflow.com/a/39580231/2510797

    pip install -U requests[security]
    

    注意后面的方括号,pip会安装三个security相关的package:pyopenssl cryptography idna。
    试了一下,果然有效,不再报错。再去读requests和urllib3的源码,发现确实使用了pyopenssl。具体是怎么用的,还没有来得及分析。
    至此个人觉得比较好的解决方法基本成型:修改play-scraper的dependency,使用requests[security]来安装那三个安全相关的包。
    另外,系统的openssl版本太旧或太新也可能会造成问题。在目前最新版本的openssl上,该解决方法是有效的。

    总结

    使用开源软件的好处是可以看实现源码,花点时间读源码,调试定位,问题基本不难解决。但是文档有可能不是那么完备,需要进行Google或仔细读源码。希望自己的分析思路对别人有所帮助吧。



    作者:PythonDeveloper
    链接:https://www.jianshu.com/p/8deb13738d2c
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
  • 相关阅读:
    Android LBS系列06 位置策略(二)模拟位置数据的方法
    Android LBS系列03 Geocoder类与地址显示
    Java 容器集合框架概览
    Android 按钮类控件大集锦:Button ToggleButton CheckBox RadioButton
    Java中的包与导入
    《Head First设计模式》 读书笔记01 策略模式
    Android Fragment和Activity
    Java中的final
    《Head First设计模式》 读书笔记07 封装调用:命令模式
    Google Maps Android API V2的使用及问题解决
  • 原文地址:https://www.cnblogs.com/ajianbeyourself/p/11156271.html
Copyright © 2011-2022 走看看