Comment chercher une URL non-ascii avec Python urlopen?
-
10-10-2019 - |
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/Ñöñ-ÅŞÇİİ/
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.
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")