Comment récupérer une url non-ascii avec urlopen Python?

je dois récupérer des données à partir d'une URL avec des caractères non-ascii mais urllib2.urlopen refuse d'ouvrir la ressource et soulève:

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

je sais que L'URL n'est pas conforme aux normes mais je n'ai aucune chance de la changer.

comment accéder à une ressource pointée par une URL contenant des caractères non-ascii en utilisant Python?

edit: en d'autres termes, peut / Comment urlopen ouvrir une URL comme:

http://example.org/Ñöñ-ÅŞÇİİ/
31
demandé sur omat 2010-12-08 19:06:33

8 réponses

à proprement parler, les URIs ne peuvent pas contenir de caractères non ASCII; ce que vous avez là est un IRI.

pour convertir un IRI en un simple URI ASCII:

  • les caractères non-ASCII dans la partie nom d'hôte de l'adresse doivent être encodés en utilisant le Punycodebase de IDNA algorithme;

  • caractères non-ASCII dans le chemin, et la plupart des autres parties de l'adresse doivent être encodées en UTF-8 et %-encoding, selon la réponse D'Ignacio.

Donc:

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'

(techniquement ce n'est pas encore assez bon dans le cas général parce que urlparse ne pas couper tout de suite user:pass@ préfixe ou :port suffixe sur le nom d'hôte. Seule la partie nom d'hôte doit être codée IDNA. Il est plus facile de coder en utilisant la normale urllib.quote et .encode('idna') au moment où vous construisez une URL, vous n'avez pas à séparer une IRI.)

47
répondu bobince 2010-12-08 19:23:12

Python 3 a des bibliothèques pour gérer cette situation. Utiliser urllib.parse.urlsplit pour diviser L'URL en ses composants, et urllib.parse.quote pour citer correctement/échapper aux caractères unicode et urllib.parse.urlunsplit pour le rejoindre.

>>> 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
15
répondu darkfeline 2013-08-16 08:56:41

En python3, utilisez le urllib.parse.quote function sur la non-ascii string:

>>> from urllib.request import urlopen                                                                                                                                                            
>>> from urllib.parse import quote                                                                                                                                                                
>>> chinese_wikipedia = 'http://zh.wikipedia.org/wiki/Wikipedia:' + quote('首页')
>>> urlopen(chinese_wikipedia)
12
répondu Perry 2015-03-24 11:32:16

encoder le unicode à UTF-8, puis URL-Encoder.

5
répondu Ignacio Vazquez-Abrams 2010-12-08 16:07:37

Utiliser iri2uri méthode httplib2. Il fait la même chose que par bobin (il/elle est l'auteur de qui?)

4
répondu eviltnan 2012-02-28 13:22:00

C'est plus complexe que la réponse acceptée de @bobince suggère:

  • netloc doit être encodé en utilisant IDNA;
  • le chemin D'URL non-ascii doit être encodé en UTF-8 puis en pourcentage;
  • les paramètres de requête non ascii doivent être encodés à l'encodage D'une page dont L'URL a été extraite (ou aux usages du serveur d'encodage), puis échappés en pourcentage.

C'est ainsi que tous les navigateurs fonctionnent; il est spécifié dans https://url.spec.whatwg.org/ - voir cette exemple. Une implémentation Python peut être trouvée dans w3lib (C'est la bibliothèque que Scrapy utilise); voir w3lib.URL.safe_url_string:

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

un moyen facile de vérifier si une URL échappant l'implémentation est incorrecte/incomplète est de vérifier si elle fournit l'argument 'page encoding' ou non.

2
répondu Mikhail Korobov 2018-09-11 12:12:43

pour ceux qui ne dépendent pas strictement d'urllib, une alternative pratique est demande, qui manipule L'IRIs "hors de la boîte".

Par exemple,http://bücher.ch:

>>> import requests
>>> r = requests.get(u'http://b\u00DCcher.ch')
>>> r.status_code
200
1
répondu h7r 2016-05-22 15:39:33

D'après la réponse de @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
1
répondu Ukr 2017-02-17 23:15:35