Wie kann ich eine URL in Python normalisieren?

73

Ich würde gerne wissen, ob ich eine URL in Python normalisiere.

Zum Beispiel, wenn ich eine URL-Zeichenfolge wie " http://www.example.com/foo goo / bar.html" habe.

Ich benötige eine Bibliothek in Python, die den zusätzlichen Speicherplatz (oder ein anderes nicht normalisiertes Zeichen) in eine richtige URL umwandelt.

Tom Feiner
quelle
Es gibt eine aktuellere Antwort auf StackOverflow hier: stackoverflow.com/questions/10584861/…
Stuckintheshuck
2
Ich denke nicht, dass das besser ist ...
Aaron Hall

Antworten:

69

Schauen Sie sich dieses Modul an: werkzeug.utils . (jetzt in werkzeug.urls)

Die gesuchte Funktion heißt "url_fix" und funktioniert folgendermaßen:

>>> from werkzeug.urls import url_fix
>>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'

Es ist in Werkzeug wie folgt implementiert:

import urllib
import urlparse

def url_fix(s, charset='utf-8'):
    """Sometimes you get an URL by a user that just isn't a real
    URL because it contains unsafe characters like ' ' and so on.  This
    function can fix some of the problems in a similar way browsers
    handle data entered by the user:

    >>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
    'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'

    :param charset: The target charset for the URL if the url was
                    given as unicode string.
    """
    if isinstance(s, unicode):
        s = s.encode(charset, 'ignore')
    scheme, netloc, path, qs, anchor = urlparse.urlsplit(s)
    path = urllib.quote(path, '/%')
    qs = urllib.quote_plus(qs, ':&=')
    return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
Armin Ronacher
quelle
Während dies von einem http rfc2616 wahrscheinlich die genauere Lösung ist, denke ich, dass es übertrieben ist, oder vermisse ich etwas?
Florian Bösch
1
Ja. Sie haben die Frage wahrscheinlich verpasst. Er hat eine URL aus Benutzereingaben und möchte diese ordnungsgemäß in eine echte URL konvertieren. (Aka: Mach was die Firefox Location Bar macht)
Armin Ronacher
3
url_fixbefindet sich jetzt amwerkzeug.urls
seppiq
@ArminRonacher Diese Funktion ist sehr gut , aber leider ist es nicht vollständig durchführt Syntax-basierte Normalisierung , dh Fall Normalisierung + Prozent kodierenden Normalisierung + Pfadsegment Normalisierung sagen, noch Schema-basierte Normalisierung , gemäß der Definition in RFC 3986. Kennen Sie Python-Bibliothek (Standard oder nicht), die das kann? Ich kann nicht glauben, dass Python keine so grundlegende Standardfunktion hat.
Maggyero
58

Echte Lösung in Python 2.7 für dieses Problem

Die richtige Lösung war:

 # percent encode url, fixing lame server errors for e.g, like space
 # within url paths.
 fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]")

Weitere Informationen finden Sie unter Issue918368: "urllib korrigiert die vom Server zurückgegebenen URLs nicht."

Oleg Sacharow
quelle
4
Hervorragende Antwort, prägnant und hilfreich. Da diese Änderung in urllib enthalten war, sollte Code, der dasselbe tun möchte, mit den obigen Parametern import urllibaufgerufen urllib.quote()werden.
Quinn Taylor
Dieser Balken auf dem Buchstaben ä, aber ich gebe ihm meine Stimme, weil es einfach ist und keinen weiteren Import erfordert.
mlissner
24

benutze urllib.quoteoderurllib.quote_plus

Aus der urllib-Dokumentation :

Zitat (Zeichenfolge [, sicher])

Ersetzen Sie Sonderzeichen in der Zeichenfolge mit dem Escapezeichen "% xx". Buchstaben, Ziffern und die Zeichen "_.-" werden niemals in Anführungszeichen gesetzt. Der optionale Parameter safe gibt zusätzliche Zeichen an, die nicht in Anführungszeichen gesetzt werden sollen. Der Standardwert ist '/'.

Beispiel: quote('/~connolly/')Ausbeuten '/%7econnolly/'.

quote_plus (string [, safe])

Wie quote (), ersetzt aber auch Leerzeichen durch Pluszeichen, wie zum Zitieren von HTML-Formularwerten erforderlich. Pluszeichen in der Originalzeichenfolge werden maskiert, sofern sie nicht im Safe enthalten sind. Es gibt auch keinen sicheren Standardwert für '/'.

BEARBEITEN: Wenn Sie urllib.quote oder urllib.quote_plus für die gesamte URL verwenden, wird dies beschädigt, wie @ ΤΖΩΤΖΙΟΥ hervorhebt:

>>> quoted_url = urllib.quote('http://www.example.com/foo goo/bar.html')
>>> quoted_url
'http%3A//www.example.com/foo%20goo/bar.html'
>>> urllib2.urlopen(quoted_url)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\python25\lib\urllib2.py", line 124, in urlopen
    return _opener.open(url, data)
  File "c:\python25\lib\urllib2.py", line 373, in open
    protocol = req.get_type()
  File "c:\python25\lib\urllib2.py", line 244, in get_type
    raise ValueError, "unknown url type: %s" % self.__original
ValueError: unknown url type: http%3A//www.example.com/foo%20goo/bar.html

@ ΤΖΩΤΖΙΟΥ bietet eine Funktion, die urlparse.urlparse und urlparse.urlunparse verwendet , um die URL zu analysieren und nur den Pfad zu codieren. Dies kann für Sie nützlicher sein. Wenn Sie die URL jedoch aus einem bekannten Protokoll und Host mit einem verdächtigen Pfad erstellen, können Sie wahrscheinlich genauso gut URL-Analyse vermeiden und nur den verdächtigen Teil der URL zitieren, der mit verkettet ist bekannte sichere Teile.

Blair Conrad
quelle
2
Was gibt urllib.quote angesichts der Beispiel-URL der Frage zurück?
Zot
1
Müll. Warum wird eine offensichtlich falsche Antwort als Lösung akzeptiert?
Armin Ronacher
@ ΤΖΩΤΖΙΟΥ: ausgezeichneter Punkt. Adressiert @Armin Ronacher: Möglicherweise, weil der Antwortende und der Akzeptant sich des Problems nicht bewusst waren - nicht alle Probleme sind für alle offensichtlich.
Blair Conrad
Vorgeschlagene Bearbeitung: "... und nur den Hostnamen verschlüsseln" → "... und nur den Pfad
angeben
Natürlich @ ΤΖΩΤΖΙΟΥ. Vielen Dank! Manchmal weiß ich nicht, wo ich mein Gehirn verlasse.
Blair Conrad
13

Da diese Seite ein Top-Ergebnis für die Google-Suche zu diesem Thema ist, sollte ich einige Arbeiten erwähnen, die an der URL-Normalisierung mit Python durchgeführt wurden und über das Urlencodieren von Leerzeichen hinausgehen. Zum Beispiel Umgang mit Standardports, Groß- und Kleinschreibung, fehlenden Schrägstrichen usw.

Als das Atom-Syndication-Format entwickelt wurde, gab es einige Diskussionen darüber, wie URLs in ein kanonisches Format normalisiert werden können. Dies ist im Artikel PaceCanonicalIds im Atom / Pie-Wiki dokumentiert . Dieser Artikel enthält einige gute Testfälle.

Ich glaube, dass ein Ergebnis dieser Diskussion Mark nottinghams urlnorm.py- Bibliothek war, die ich mit guten Ergebnissen bei einigen Projekten verwendet habe. Dieses Skript funktioniert jedoch nicht mit der in dieser Frage angegebenen URL. Eine bessere Wahl könnte also Sam Rubys Version von urlnorm.py sein , die diese URL verarbeitet, sowie alle oben genannten Testfälle aus dem Atom-Wiki.

Cobra Libre
quelle
10

Py3

from urllib.parse import urlparse, urlunparse, quote
def myquote(url):
    parts = urlparse(url)
    return urlunparse(parts._replace(path=quote(parts.path)))

>>> myquote('https://www.example.com/~user/with space/index.html?a=1&b=2')
'https://www.example.com/~user/with%20space/index.html?a=1&b=2'

Py2

import urlparse, urllib
def myquote(url):
    parts = urlparse.urlparse(url)
    return urlparse.urlunparse(parts[:2] + (urllib.quote(parts[2]),) + parts[3:])

>>> myquote('https://www.example.com/~user/with space/index.html?a=1&b=2')
'https://www.example.com/%7Euser/with%20space/index.html?a=1&b=2'

Dies zitiert nur die Pfadkomponente.

tzot
quelle
2
Das zitiert einfach alle Zeichen. Das wird ihm nicht helfen.
Armin Ronacher
In diesem Beispiel würde auch das Zeichen ':' angegeben (nicht alle). Danke für den Kommentar.
tzot
1

Ich stoße auf ein solches Problem: Ich muss nur den Raum zitieren.

fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]") helfen, aber es ist zu kompliziert.

Also habe ich einen einfachen Weg benutzt: url = url.replace(' ', '%20')Es ist nicht perfekt, aber es ist der einfachste Weg und es funktioniert für diese Situation.

WKPlus
quelle