Frage

Ich muss die Daten von einer URL mit Nicht-ASCII-Zeichen holen, aber urllib2.urlopen weigert sich, die Ressource und Raises zu öffnen:

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

ich weiß, ist die URL nicht standardkonform, aber ich habe keine Chance, es zu ändern.

Was ist der Weg, um Zugriff auf eine Ressource durch eine URL Nicht-ASCII-Zeichen mit Python enthält, zeigte?

Bearbeiten Mit anderen Worten kann / wie urlopen eine URL geöffnet haben, wie:

http://example.org/Ñöñ-ÅŞÇİİ/
War es hilfreich?

Lösung

Genau genommen URIs kann kein Nicht-ASCII-Zeichen enthalten; was Sie haben, gibt es eine IRI .

Um eine IRI auf eine reine ASCII-URI konvertieren

  • Nicht-ASCII-Zeichen im Hostnamen Teil der Adresse haben codiert werden unter Verwendung des Punycode -basierte IDNA Algorithmus;

  • Nicht-ASCII-Zeichen in dem Weg, und die meisten anderen Teile der Adresse haben codiert werden unter Verwendung von UTF-8 und% -encoding, wie pro Ignacio Antwort.

So:

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'

(Technisch gesehen ist dies immer noch nicht gut genug im allgemeinen Fall, weil urlparse abgespalten keinen user:pass@ Präfix oder Suffix :port auf dem Hostnamen. Nur der Host-Name Teil sein sollte IDNA codiert. Es ist einfacher zu kodieren mit normalen urllib.quote und .encode('idna') zu der Zeit Sie eine URL ist die Konstruktion als zu haben, eine IRI auseinander zu ziehen.)

Andere Tipps

Python 3 hat Bibliotheken mit dieser Situation zu umgehen. Verwenden urllib.parse.urlsplit die URL in ihre Bestandteile aufzuspalten und urllib.parse.quote richtig Zitat / entkommen, die Unicode-Zeichen und urllib.parse.urlunsplit verbinden sie wieder zusammen.

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

In python3, verwenden Sie die urllib.parse.quote Funktion auf der Nicht-ASCII-Zeichenfolge:

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

Encode die unicode auf UTF-8, dann URL-kodieren.

Mit iri2uri Methode von httplib2. Es macht das Gleiche wie von bobin (ist er / sie der Autor das?)

Es ist komplexer als die akzeptierte @ bobince Antwort schlägt vor:

  • Netloc codiert werden sollte IDNA verwendet wird;
  • Nicht-ASCII-URL-Pfad zu UTF-8 codiert werden soll, und dann Prozent-escaped;
  • Nicht-ASCII-Abfrageparameter sollte auf die Codierung einer Seite URL codiert werden extrahiert aus (oder auf die Codierung Server Anwendungen), dann Prozent-escaped.

Dies ist, wie alle Browser arbeiten; es angegeben ist in https://url.spec.whatwg.org/ - siehe das Beispiel . Eine Python Umsetzung kann in w3lib gefunden werden (dies ist die Bibliothek Scrapy verwendet); finden Sie unter w3lib.url.safe_url_string :

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

Eine einfache Möglichkeit, zu überprüfen, ob eine URL zu entkommen Implementierung falsch ist / unvollständig ist zu prüfen, ob es "Seitencodierung Argument liefert oder nicht.

Für diejenigen, die nicht streng auf urllib je, eine praktische Alternative ist Anfragen , die Griff IRIs "out of the box".

Zum Beispiel mit http://bücher.ch:

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

Basierend auf @darkfeline Antwort:

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

funktioniert! schließlich

Ich konnte nicht von diesen seltsamen Zeichen vermeiden, aber am Ende komme ich durch sie.

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")
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top