Question

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

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 pas la chance de le changer.

Quelle est la façon d'accéder à une ressource pointée par une URL contenant des caractères non-ascii en utilisant Python?

modifier En d'autres termes, peut / comment urlopen ouvrir une URL comme:

http://example.org/Ñöñ-ÅŞÇİİ/
Était-ce utile?

La solution

Strictement parlant URIs ne peuvent pas contenir des caractères non-ASCII; ce que vous avez il y a une IRI .

Pour convertir un IRI à un URI ASCII:

  • caractères non-ASCII dans la partie de nom d'hôte de l'adresse doivent être codées en utilisant le Punycode à base de algorithme IDNA;

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

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, il est pas tout à fait assez bon dans le cas général, car urlparse ne se fend pas loin préfixe de user:pass@ ou suffixe :port sur le nom d'hôte. Seule la partie du nom d'hôte doit être IDNA codé. Il est plus facile à coder en utilisant urllib.quote normale et .encode('idna') au moment où vous construisez une URL que d'avoir à tirer un IRI à part.)

Autres conseils

Python 3 a des bibliothèques pour gérer cette situation. Utilisation urllib.parse.urlsplit de diviser l'URL dans ses composants, et urllib.parse.quote à bien citation / échapper aux caractères unicode et urllib.parse.urlunsplit s'y joindre ensemble.

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

Dans python3, utilisez la fonction de urllib.parse.quote sur la chaîne non-ascii:

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

Encode le unicode en UTF-8, puis URL-encoder.

Utilisation de la méthode iri2uri de httplib2. Il fait la même chose que par Bobin (est-il / elle l'auteur de cela?)

Il est plus complexe que la réponse du @ acceptée bobince suggère:

  • Netloc doit être codé en utilisant IDNA;
  • chemin d'URL non-ascii doit être codé en UTF-8 pour cent, puis-échappé;
  • paramètres de requête non-ascii doit être codé pour l'encodage d'une URL de page a été extraite de (ou aux usages du serveur d'encodage), puis pour cent-échappé.

Voici comment tous les navigateurs fonctionnent; il est précisé dans https://url.spec.whatwg.org/ - voir cette exemple . Une implémentation Python se trouve dans w3lib (ce qui est la bibliothè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 est incorrect échapper la mise en œuvre / incomplète est de vérifier si elle fournit des arguments page encoding 'ou non.

Pour ceux qui ne dépendent pas strictement urllib, une solution pratique , qui poignées "IRIs de la boîte".

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

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

Sur la base de réponse @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

fonctionne! enfin

Je ne pouvais pas éviter de ce étranges personnages, mais à la fin je viens à travers elle.

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")
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top