Dengan Python 3.2, saya dapat membuka dan membaca halaman web HTTPS dengan http.client, tetapi urllib.request gagal membuka halaman yang sama

Saya ingin membuka dan membaca https://yande.re/ dengan urllib.request, tetapi saya mendapatkan kesalahan SSL. Saya dapat membuka dan membaca halaman tersebut dengan baik menggunakan http.client dengan kode ini:

import http.client

conn = http.client.HTTPSConnection('www.yande.re')
conn.request('GET', 'https://yande.re/')
resp = conn.getresponse()
data = resp.read()

Namun, kode berikut yang menggunakan urllib.request gagal:

import urllib.request

opener = urllib.request.build_opener()
resp = opener.open('https://yande.re/')
data = resp.read()

Ini memberi saya kesalahan berikut: ssl.SSLError: [Errno 1] _ssl.c:392: error:1411809D:SSL routines:SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list. Mengapa saya dapat membuka halaman dengan HTTPSConnection tetapi tidak dengan opener.open?

Edit: Ini versi OpenSSL saya dan penelusuran balik dari upaya membuka https://yande.re/

>>> import ssl; ssl.OPENSSL_VERSION
'OpenSSL 1.0.0a 1 Jun 2010'
>>> import urllib.request
>>> urllib.request.urlopen('https://yande.re/')
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    urllib.request.urlopen('https://yande.re/')
  File "C:\Python32\lib\urllib\request.py", line 138, in urlopen
    return opener.open(url, data, timeout)
  File "C:\Python32\lib\urllib\request.py", line 369, in open
    response = self._open(req, data)
  File "C:\Python32\lib\urllib\request.py", line 387, in _open
    '_open', req)
  File "C:\Python32\lib\urllib\request.py", line 347, in _call_chain
    result = func(*args)
  File "C:\Python32\lib\urllib\request.py", line 1171, in https_open
    context=self._context, check_hostname=self._check_hostname)
  File "C:\Python32\lib\urllib\request.py", line 1138, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [Errno 1] _ssl.c:392: error:1411809D:SSL routines:SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list>
>>> 

person user1406902    schedule 21.05.2012    source sumber
comment
Bisakah Anda menempelkan keluaran import ssl; ssl.OPENSSL_VERSION, dan hasil urllib.request.urlopen('https://yande.re/')   -  person Burhan Khalid    schedule 21.05.2012
comment
FWIW, mungkin titik data untuk debugging. Kode Python 2.7.x yang setara (ditunjukkan di bawah) berfungsi dengan baik: import urllib2 req = urllib2.Request('yande.re') resp = urllib2.urlopen(req) resp.read()   -  person sateesh    schedule 21.05.2012
comment
kode untuk http.client salah. Maksud Anda mungkin: conn.request('GET', '/')   -  person jfs    schedule 05.09.2012


Jawaban (4)


Kebetulan sekali! Saya mengalami masalah yang sama seperti Anda, dengan komplikasi tambahan: Saya berada di belakang proxy. Saya menemukan laporan bug mengenai https-not-working-with-urllib. Untungnya, mereka memposting solusinya.

import urllib.request
import ssl

##uncomment this code if you're behind a proxy
##https port is 443 but it doesn't work for me, used port 80 instead

##proxy_auth = '{0}://{1}:{2}@{3}'.format('https', 'username', 'password', 
##             'proxy:80')
##proxies = { 'https' : proxy_auth }
##proxy = urllib.request.ProxyHandler(proxies)
##proxy_auth_handler = urllib.request.HTTPBasicAuthHandler()
##opener = urllib.request.build_opener(proxy, proxy_auth_handler, 
##                                     https_sslv3_handler)

https_sslv3_handler = 
         urllib.request.HTTPSHandler(context=ssl.SSLContext(ssl.PROTOCOL_SSLv3))
opener = urllib.request.build_opener(https_sslv3_handler)
urllib.request.install_opener(opener)
resp = opener.open('https://yande.re/')
data = resp.read().decode('utf-8')
print(data)

Ngomong-ngomong, terima kasih sudah menunjukkan cara menggunakan http.client. Saya tidak tahu kalau ada perpustakaan lain yang bisa digunakan untuk terhubung ke internet. ;)

person Annie Lagang    schedule 04.12.2012
comment
Terima kasih banyak, ini sebenarnya membantu saya dengan masalah urllib yang sedikit berbeda - person Corvin; 27.09.2013
comment
Cuplikan kode ini tidak berfungsi untuk saya; Saya berakhir dengan kegagalan jabat tangan: ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 peringatan kegagalan jabat tangan (_ssl.c:748) - person MikeB; 12.08.2017

Hal ini disebabkan oleh bug di awal implementasi OpenSSL 1.x kriptografi kurva elips. Perhatikan lebih dekat bagian pengecualian yang relevan:

_ssl.c:392: error:1411809D:SSL routines:SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list

Ini adalah kesalahan dari kode perpustakaan OpenSSL yang mendasarinya yang merupakan akibat dari kesalahan penanganan ekstensi TLS format titik EC. Salah satu solusinya adalah dengan menggunakan metode SSLv3 dan bukan SSLv23, solusi lainnya adalah dengan menggunakan spesifikasi cipher suite yang menonaktifkan semua cipher suite ECC (Saya mendapatkan hasil yang baik dengan ALL:-ECDH, gunakan openssl ciphers untuk pengujian). Cara mengatasinya adalah dengan memperbarui OpenSSL.

person Daniel Roethlisberger    schedule 20.12.2012
comment
bisakah Anda menjelaskan bagaimana mungkin menggunakan spesifikasi cipher suite dalam contoh pengguna? - person mic.sca; 10.12.2014

Masalahnya adalah karena nama host yang Anda berikan pada dua contoh:

import http.client
conn = http.client.HTTPSConnection('www.yande.re')
conn.request('GET', 'https://yande.re/')

Dan...

import urllib.request
urllib.request.urlopen('https://yande.re/')

Perhatikan bahwa pada contoh pertama, Anda meminta klien untuk membuat koneksi ke host: www.yande.re dan pada contoh kedua, urllib pertama-tama akan menguraikan url 'https://yande.re' dan kemudian mencoba permintaan di host yande.re

Meskipun www.yande.re dan yande.re mungkin menggunakan alamat IP yang sama, dari sudut pandang server web, keduanya adalah host virtual yang berbeda. Dugaan saya adalah Anda mengalami masalah konfigurasi SNI di sisi server web Anda. Mengingat pertanyaan awal telah diposting pada 21 Mei, dan sertifikat saat ini di yande.re dimulai 28 Mei, menurut saya Anda sudah memperbaiki masalah ini?

person parselmouth    schedule 30.05.2012

Coba ini:

import connection #imports connection
import url 

url = 'http://www.google.com/'    
webpage = url.open(url)

try:
    connection.receive(webpage)
except:
    webpage = url.text('This webpage is not available!')
    connection.receive(webpage)
person Python    schedule 22.08.2012