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