python SSLError("糟糕的握手:SysCallError(-1,'意外的EOF')",),))

时间:2018-03-08 05:24:12

标签: python-2.7 beautifulsoup openssl python-requests

我正在抓取这个aspx网站https://gra206.aca.ntu.edu.tw/Temp/W2.aspx?Type=2

根据需要,我必须在发送帖子请求时解析 __ VIEWSTATE __ EVENTVALIDATION 。现在我尝试首先发送 get 请求以获得这两个值,然后再解析。

但是,我已多次尝试发送获取请求。它总是抛出这个错误信息:

  

requests.exceptions.SSLError:HTTPSConnectionPool(host =' gra206.aca.ntu.edu.tw',port = 443):使用url超出了最大重试次数:/Temp/W2.aspx?Type= 2(由SSLError引起(SSLError("坏握手:SysCallError(-1,'意外的EOF')",),))

我试过了:

  1. 升级OpenSSL
  2. 下载请求[安全]
  3. 然而,它们都不起作用。

    我目前正在使用:

    env:
    python 2.7
    bs4 4.6.0
    request 2.18.4
    openssl 1.0.2n
    

    这是我的代码:

    import requests
    from   bs4 import BeautifulSoup
    
    with requests.Session() as s:
        s.auth = ('user', 'pass')
        s.headers.update({'x-test': 'true'})
        url = 'https://gra206.aca.ntu.edu.tw/Temp/W2.aspx?Type=2'
        r = s.get(url, headers={'x-test2': 'true'})
    
    soup = BeautifulSoup(r.content, 'lxml')
    viewstate  = soup.find('input', {'id': '__VIEWSTATE'         })['value']
    validation = soup.find('input', {'id': '__EVENTVALIDATION'   })['value']  
    print viewstate, generator, validation
    

1 个答案:

答案 0 :(得分:2)

我也在寻找解决方案。有些网站已弃用TLSv1.0,而Requests + Openssl(在Windows 7上)无法与此类对等主机构建握手。 Wireshark日志显示客户端发出了TLSv1客户端Hello,但主机没有正确回答。此错误在请求显示的错误消息中传播。即使使用最新的Openssl / pyOpenssl / Requests并在Py3.6 / 2.7.12上试用,也没有运气。有趣的是,当我将网址替换为“google.com”之类的其他网址时,日志显示TLSv1.2 Hello已发布并由主机响应。请检查图片tlsv1tlsv1.2。 显然,客户端具有TLSv1.2功能,但为什么在前一种情况下使用v1.0 Hello?

<强> [编辑] 我在之前的陈述中错了。 Wireshark误解了未完成的TLSv1.2 HELLO作为TLSv1交换。在深入研究之后,我发现这些主机期待纯TLSv1,但不是TLSv1.2的TLSv1回退。由于与Chrome中的日志相比,Openssl在Hello扩展字段(可能是支持的版本)中缺少某些字段。我找到了解决方法。 1.强制使用TLSv1协商。 2.将默认密码套件更改为py3.4样式以重新启用3DES。

import ssl
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager
#from urllib3.poolmanager import PoolManager
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

    # py3.4 default
CIPHERS = (
    'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
    'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
    '!eNULL:!MD5'
)

class DESAdapter(HTTPAdapter):
    """
    A TransportAdapter that re-enables 3DES support in Requests.
    """
    def create_ssl_context(self):
        #ctx = create_urllib3_context(ciphers=FORCED_CIPHERS)
        ctx = ssl.create_default_context()
        # allow TLS 1.0 and TLS 1.2 and later (disable SSLv3 and SSLv2)
        #ctx.options |= ssl.OP_NO_SSLv2
        #ctx.options |= ssl.OP_NO_SSLv3 
        #ctx.options |= ssl.OP_NO_TLSv1
        ctx.options |= ssl.OP_NO_TLSv1_2
        ctx.options |= ssl.OP_NO_TLSv1_1
        #ctx.options |= ssl.OP_NO_TLSv1_3
        ctx.set_ciphers( CIPHERS )
        #ctx.set_alpn_protocols(['http/1.1', 'spdy/2'])
        return ctx

    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=CIPHERS)
        kwargs['ssl_context'] = self.create_ssl_context()
        return super(DESAdapter, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=CIPHERS)
        kwargs['ssl_context'] = self.create_ssl_context()
        return super(DESAdapter, self).proxy_manager_for(*args, **kwargs)

tmoval=10
proxies={}
hdr = {'Accept-Language':'zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4', 'Cache-Control':'max-age=0', 'Connection':'keep-alive', 'Proxy-Connection':'keep-alive', #'Cache-Control':'no-cache', 'Connection':'close',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36',
        'Accept-Encoding':'gzip,deflate,sdch','Accept':'*/*'}
ses = requests.session()
ses.mount(url, DESAdapter())

response = ses.get(url, timeout=tmoval, headers = hdr, proxies=proxies)

<强> [EDIT2] 当您的HTTPS网址包含任何大写字母时,该修补程序将无法正常工作。您需要将它们反转为小写。 stack / urllib3 / openssl中的某些未知内容导致补丁逻辑恢复为其默认的TLS1.2方式。

<强> [EDIT3] 来自http://docs.python-requests.org/en/master/user/advanced/

  

挂载调用将传输适配器的特定实例注册到前缀。安装后,使用该URL以指定前缀开头的会话发出的任何HTTP请求都将使用给定的传输适配器。

因此,要使所有HTTPS请求包括服务器之后重定向以使用新适配器的请求,必须将此行更改为:

ses.mount('https://', DESAdapter())

不知何故,它解决了上面提到的大写问题。