Pregunta

necesito para obtener los datos desde una URL con caracteres no ASCII, pero urllib2.urlopen se niega a abrir el recurso y aumentos:

UnicodeEncodeError: 'ascii' codec can't encode character u'\u0131' in position 26: ordinal not in range(128)

Sé que la URL no es compatible con las normas, pero no tengo la oportunidad de cambiarlo.

¿Cuál es la manera de acceder a un recurso apuntado por una dirección URL que contiene caracteres no ASCII utilizando Python?

editar En otras palabras, puede / urlopen cómo abrir una URL como:

http://example.org/Ñöñ-ÅŞÇİİ/
¿Fue útil?

Solución

URI En sentido estricto no pueden contener caracteres no ASCII; lo que tienes ahí es un IRI .

Para convertir un IRI a un plano ASCII URI:

  • caracteres no ASCII en la parte nombre de host de la dirección han de ser codificados usando el Punycode basado algoritmo IDNA;

  • caracteres no ASCII en el camino, y la mayoría de las otras partes de la dirección tienen que ser codificado usando UTF-8 y el% que codifica, como por la respuesta de Ignacio.

Así que:

import re, urlparse

def urlEncodeNonAscii(b):
    return re.sub('[\x80-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), b)

def iriToUri(iri):
    parts= urlparse.urlparse(iri)
    return urlparse.urlunparse(
        part.encode('idna') if parti==1 else urlEncodeNonAscii(part.encode('utf-8'))
        for parti, part in enumerate(parts)
    )

>>> iriToUri(u'http://www.a\u0131b.com/a\u0131b')
'http://www.xn--ab-hpa.com/a%c4%b1b'

(Técnicamente esto todavía no es bastante bueno suficiente en el caso general, ya no se divide urlparse distancia user:pass@ cualquier prefijo o sufijo :port en el nombre de host. Sólo la parte de host debe ser codificado IDNA. Es más fácil de codificar utilizando urllib.quote normales y .encode('idna') en el momento usted está construyendo una URL que tener que tirar de un IRI aparte.)

Otros consejos

Python 3 tiene bibliotecas para manejar esta situación. Utilizar urllib.parse.urlsplit para dividir la URL en sus componentes, y urllib.parse.quote adecuadamente presupuesto / escapar los caracteres Unicode y urllib.parse.urlunsplit para unirse de nuevo juntos.

>>> import urllib.parse
>>> url = 'http://example.com/unicodè'
>>> url = urllib.parse.urlsplit(url)
>>> url = list(url)
>>> url[2] = urllib.parse.quote(url[2])
>>> url = urllib.parse.urlunsplit(url)
>>> print(url)
http://example.com/unicod%C3%A8

En python3, utilice la función urllib.parse.quote en la cadena no ASCII:

>>> from urllib.request import urlopen                                                                                                                                                            
>>> from urllib.parse import quote                                                                                                                                                                
>>> chinese_wikipedia = 'http://zh.wikipedia.org/wiki/Wikipedia:' + quote('首页')
>>> urlopen(chinese_wikipedia)

codificar los unicode a UTF-8, a continuación, URL a codificar.

Método iri2uri

El uso de httplib2. Se hace lo mismo que por Bobin (es él / ella el autor de ese?)

Es más complejo que la respuesta aceptada @ de bobince sugiere:

  • netloc debe ser codificado usando IDNA;
  • ruta URL no ASCII debe ser codificado en UTF-8 y luego por ciento-escapado;
  • parámetros de consulta no ASCII debería ser codificada a la codificación de un URL de la página se extrajo a partir de (o a los usos de servidor de codificación), a continuación, escapado por ciento.

Este es el funcionamiento de todos los navegadores; se especifica en https://url.spec.whatwg.org/ - ver esta ejemplo . aplicación A Python se puede encontrar en w3lib (esta es la biblioteca Scrapy está utilizando); ver w3lib.url.safe_url_string :

from w3lib.url import safe_url_string
url = safe_url_string(u'http://example.org/Ñöñ-ÅŞÇİİ/', encoding="<page encoding>")

Una forma sencilla de comprobar si un URL escapar de aplicación es incorrecto / incompleto es comprobar si proporciona página de codificación argumento o no.

Para aquellos que no dependen estrictamente en urllib, una alternativa práctica es href="http://docs.python-requests.org/en/master/" solicitudes , que manijas del iris "fuera de la caja".

Por ejemplo, con http://bücher.ch:

>>> import requests
>>> r = requests.get(u'http://b\u00DCcher.ch')
>>> r.status_code
200

Basado en respuesta @darkfeline:

from urllib.parse import urlsplit, urlunsplit, quote

def iri2uri(iri):
    """
    Convert an IRI to a URI (Python 3).
    """
    uri = ''
    if isinstance(iri, str):
        (scheme, netloc, path, query, fragment) = urlsplit(iri)
        scheme = quote(scheme)
        netloc = netloc.encode('idna').decode('utf-8')
        path = quote(path)
        query = quote(query)
        fragment = quote(fragment)
        uri = urlunsplit((scheme, netloc, path, query, fragment))

    return uri

funciona! Finalmente

No podía evitar de este extraño personajes, pero al final he llegado a través de él.

import urllib.request
import os


url = "http://www.fourtourismblog.it/le-nuove-tendenze-del-marketing-tenere-docchio/"
with urllib.request.urlopen(url) as file:
    html = file.read()
with open("marketingturismo.html", "w", encoding='utf-8') as file:
    file.write(str(html.decode('utf-8')))
os.system("marketingturismo.html")
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top