python unicode urllib2 non-ascii-characters urlopen

python urldecode



¿Cómo recuperar una url no ascii con Python urlopen? (8)

Necesito obtener datos de una URL con caracteres que no sean ascii, pero urllib2.urlopen se niega a abrir el recurso y plantea:

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

Sé que la URL no cumple con los estándares pero no tengo la oportunidad de cambiarla.

¿Cuál es la forma de acceder a un recurso apuntado por una URL que contiene caracteres que no son ASCII utilizando Python?

editar: En otras palabras, can / how urlopen abre una URL como:

http://example.org/Ñöñ-ÅŞÇİİ/


Basado en @darkfeline respuesta:

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


Codifique el unicode a UTF-8, luego URL-encode.


En python3, use la función urllib.parse.quote en la cadena que no es ascii:

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


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

  • netloc debe codificarse usando IDNA;
  • la ruta URL no ascii debe estar codificada en UTF-8 y luego en porcentaje de escape;
  • los parámetros de consulta no ascii deben codificarse para la codificación de una URL de página de la que se extrajo (o para el servidor de codificación), y luego se escapó porcentualmente.

Así es como funcionan todos los navegadores; está especificado en https://url.spec.whatwg.org/ - vea este example . Se puede encontrar una implementación de Python en w3lib (esta es la biblioteca que usa Scrapy); vea w3lib.url.safe_url_string :

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

Una manera fácil de comprobar si una URL que escapa de la implementación es incorrecta / incompleta es verificar si proporciona un argumento de "codificación de página" o no.


Estrictamente hablando, los URI no pueden contener caracteres que no sean ASCII; lo que tienes allí es un IRI .

Para convertir un IRI a un URI ASCII simple:

  • los caracteres no ASCII en la parte del nombre de host de la dirección deben codificarse utilizando el algoritmo IDNA basado en Punycode ;

  • caracteres no ASCII en la ruta, y la mayoría de las otras partes de la dirección deben codificarse usando UTF-8 y% codificación, según la respuesta de Ignacio.

Asi 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 lo suficientemente bueno en el caso general porque urlparse no divide a ningún user:pass@ prefix o :port sufijo de :port en el nombre de host. Solo la parte de nombre de host debe ser codificada por IDNA. Es más fácil codificar usando normal urllib.quote y .encode(''idna'') en el momento de construir una URL que tener que separar un IRI).


Para aquellos que no dependen estrictamente de urllib, una alternativa práctica son las requests , que manejan IRI "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


Python 3 tiene bibliotecas para manejar esta situación. Utilice urllib.parse.urlsplit para dividir la URL en sus componentes, y urllib.parse.quote para presupuestar / escapar correctamente los caracteres Unicode y urllib.parse.urlunsplit para unirlos nuevamente.

>>> 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


Use el método httplib2 de httplib2 . Hace lo mismo que por bobin (¿es él / ella el autor de eso?)