Python - Wie überprüfe ich eine URL in Python? (Missgebildet oder nicht)

114

Ich habe urlvom Benutzer und ich muss mit dem abgerufenen HTML antworten.

Wie kann ich überprüfen, ob die URL fehlerhaft ist oder nicht?

Zum Beispiel :

url='google'  // Malformed
url='google.com'  // Malformed
url='http://google.com'  // Valid
url='http://google'   // Malformed

Wie können wir das erreichen?

Yugal Jindle
quelle
1
Versuchen Sie einfach, es zu lesen. Wenn beispielsweise httplib eine Ausnahme auslöst, wissen Sie, dass es ungültig war. Nicht alle wohlgeformten URLs sind gültig !
Carlpett
1
Dies wird Ihnen helfen: stackoverflow.com/questions/827557/…
DhruvPathak
10
url='http://google' ist nicht missgebildet. Schema + Hostname ist immer gültig.
Viktor Joras

Antworten:

90

Django URL Validierung Regex ( Quelle ):

import re
regex = re.compile(
        r'^(?:http|ftp)s?://' # http:// or https://
        r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
        r'localhost|' #localhost...
        r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
        r'(?::\d+)?' # optional port
        r'(?:/?|[/?]\S+)$', re.IGNORECASE)

print(re.match(regex, "http://www.example.com") is not None) # True
print(re.match(regex, "example.com") is not None)            # False
cetver
quelle
eine Neugier ... hast du das hinzugefügt ftp? Oder habe ich eine alte Django-Version?
Ruggero Turra
2
@ yugal-jindle sitedomain ist keine gültige URL. Museum ist, weil .museum eine Top-Level-Domain ist (ICANN [1] definiert sie) und keine Sitedomain. [1] icann.org
Glarrain
1
Dieser scheint nicht mit Benutzernamen zu funktionieren : [email protected] style URLs
Adam Baxter
2
Dies wird nicht für IPv6 - URLs arbeiten, die die Form habenhttp://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine
123

Eigentlich denke ich, dass dies der beste Weg ist.

from django.core.validators import URLValidator
from django.core.exceptions import ValidationError

val = URLValidator(verify_exists=False)
try:
    val('http://www.google.com')
except ValidationError, e:
    print e

Wenn Sie setzen verify_existsauf True, wird es tatsächlich überprüfen, ob die URL existiert, sonst wird es nur überprüfen , ob es richtig gebildet wird .

edit: ah yeah, diese Frage ist ein Duplikat davon: Wie kann ich überprüfen, ob eine URL mit Djangos Validatoren existiert?

Drekembe
quelle
46
Dies funktioniert aber nur in der Django-Umgebung, sonst nicht.
Yugal Jindle
19
verify_existsist veraltet. -1
g33kz0r
2
Fügen Sie hinzu: aus django.conf import settings settings.configure (DEBUG = False) und entfernen Sie die verify_exists, damit es mit django 1.5
Dukeatcoding
1
@YugalJindle Richtig, aber das Entfernen von Django ist fast trivial: D. Also, ich benutze diese Methode
swdev
7
Beachten Sie, dass es mit django> = 1.5 keine verify_existsmehr gibt. Auch anstelle der valVariablen können Sie es wieURLValidator()('http://www.google.com')
Luckydonald
121

Verwenden Sie das Validators- Paket:

>>> import validators
>>> validators.url("http://google.com")
True
>>> validators.url("http://google")
ValidationFailure(func=url, args={'value': 'http://google', 'require_tld': True})
>>> if not validators.url("http://google"):
...     print "not valid"
... 
not valid
>>>

Installieren Sie es von PyPI mit pip ( pip install validators).

Jabba
quelle
5
Es wird ein Fehler für Datei-URLs ausgegeben. Wie "file: ///users/file.txt"
Devavrata
2
Fehlgeschlagen für lokale Host-URLs validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom
5
@Lal Zada, bevor Sie so etwas behaupten, geben Sie sich etwas Mühe und überprüfen Sie den Code, Regexp ist eigentlich ganz gut: validators.readthedocs.io/en/latest/_modules/validators/…
Drachenfels
1
Das Validierungs-fn des Pakets weist viele willkürliche Einschränkungen auf, daher ist es ein schrecklicher Rat, es als allgemeine Lösung vorzuschlagen.
ivan_pozdeev
2
@ivan_pozdeev: Wenn es schrecklich ist, dann schlagen Sie eine bessere Lösung vor
Jabba
61

Eine True- oder False-Version, basierend auf der @ DMfll-Antwort:

try:
    # python2
    from urlparse import urlparse
except:
    # python3
    from urllib.parse import urlparse

a = 'http://www.cwi.nl:80/%7Eguido/Python.html'
b = '/data/Python.html'
c = 532
d = u'dkakasdkjdjakdjadjfalskdjfalk'

def uri_validator(x):
    try:
        result = urlparse(x)
        return all([result.scheme, result.netloc, result.path])
    except:
        return False

print(uri_validator(a))
print(uri_validator(b))
print(uri_validator(c))
print(uri_validator(d))

Gibt:

True
False
False
False
Alemol
quelle
7
Ich wusste nicht, dass Sie eine if-Anweisung mit einer Liste von Nicht-None-Elementen testen können. Das ist hilfreich. Auch +1 für die Verwendung eines eingebauten Moduls
Marc Maxmeister
9
Das erlaubt alles. Es wird Truefür die Zeichenfolge fakeoder sogar für eine leere Zeichenfolge zurückgegeben. Es wird niemals Fehler geben, da diese Attribute immer vorhanden sind und die Liste immer den booleschen Wert True hat, da sie diese Attribute enthält. Selbst wenn alle Attribute Keine sind, ist die Liste nicht leer. Sie müssen die Attribute überprüfen, da alles so verläuft, wie Sie es jetzt haben.
Zondo
3
Listen falscher Objekte werden als wahr ausgewertet: print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")Gibt "Ich bin wahr" aus. wenn ich es laufen lasse. [result.scheme, result.netloc, result.path]bewertet immer zu True. print("I am True") if [] else print("I am False.")druckt "Ich bin falsch." Leere Listen sind also falsch. Der Inhalt des Arrays muss mit so etwas wie der allFunktion ausgewertet werden.
dmmfll
3
Ich bin mir nicht sicher, warum Sie einen solchen Pfad benötigen würden. Sie sollten result.pathaus dem Test entfernen .
Jerinaw
1
Das ist gut genug für mich, danke. Ich habe gerade eine einfache Validierung hinzugefügt für scheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin
20

Heutzutage verwende ich Folgendes, basierend auf der Antwort des Padam:

$ python --version
Python 3.6.5

Und so sieht es aus:

from urllib.parse import urlparse

def is_url(url):
  try:
    result = urlparse(url)
    return all([result.scheme, result.netloc])
  except ValueError:
    return False

Einfach benutzen is_url("http://www.asdf.com").

Ich hoffe es hilft!

Jonaprieto
quelle
Dies schlägt fehl, wenn der Domainname mit einem Bindestrich beginnt, der nicht gültig ist. tools.ietf.org/html/rfc952
Björn Lindqvist
Dies ist nur dann gut, wenn Komponenten in dem speziellen Fall aufgeteilt werden, in dem der URI bekanntermaßen NICHT fehlerhaft ist. Wie ich zuvor auf die andere ähnliche Antwort geantwortet habe, werden dadurch fehlerhafte URIs wie z https://https://https://www.foo.bar.
Ingyhere
9

note - lepl wird leider nicht mehr unterstützt (Sie können es gerne verwenden, und ich denke, der folgende Code funktioniert, aber er wird keine Updates erhalten).

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html definiert, wie dies getan wird (für http-URLs und E-Mails). Ich habe seine Empfehlungen in Python mit Lepl (einer Parser-Bibliothek) implementiert. Siehe http://acooke.org/lepl/rfc3696.html

benutzen:

> easy_install lepl
...
> python
...
>>> from lepl.apps.rfc3696 import HttpUrl
>>> validator = HttpUrl()
>>> validator('google')
False
>>> validator('http://google')
False
>>> validator('http://google.com')
True
Andrew Cooke
quelle
2
Ordentlich, aber was ist mit FTP oder HTTPS?
Adam Parkin
6
Sie haben den Code nicht gegabelt und implementiert? Es ist Open Source.
Andrew Cooke
1
LEPL wird nun vom Autor eingestellt acooke.org/lepl/discontinued.html EDIT: heh, nur realisiert , dass Sie sind der Autor
Emmett Butler
1
Hinweis: lepl.apps.rfc3696 funktioniert nicht in Python 3.7.4
Sheile
9

Ich bin auf dieser Seite gelandet und habe versucht, einen vernünftigen Weg zu finden, um Zeichenfolgen als "gültige" URLs zu validieren. Ich teile hier meine Lösung mit Python3. Keine zusätzlichen Bibliotheken erforderlich.

Siehe https://docs.python.org/2/library/urlparse.html, wenn Sie python2 verwenden.

Siehe https://docs.python.org/3.0/library/urllib.parse.html, wenn Sie python3 so verwenden, wie ich es bin.

import urllib
from pprint import pprint

invalid_url = 'dkakasdkjdjakdjadjfalskdjfalk'
valid_url = 'https://stackoverflow.com'
tokens = [urllib.parse.urlparse(url) for url in (invalid_url, valid_url)]

for token in tokens:
    pprint(token)

min_attributes = ('scheme', 'netloc')  # add attrs to your liking
for token in tokens:
    if not all([getattr(token, attr) for attr in min_attributes]):
        error = "'{url}' string has no scheme or netloc.".format(url=token.geturl())
        print(error)
    else:
        print("'{url}' is probably a valid url.".format(url=token.geturl()))

ParseResult (Schema = '', Netloc = '', Pfad = 'dkakasdkjdjakdjadjfalskdjfalk', params = '', query = '', fragment = '')

ParseResult (Schema = 'https', Netloc = 'Stackoverflow.com', Pfad = '', Parameter = '', Abfrage = '', Fragment = '')

Die Zeichenfolge 'dkakasdkjdjakdjadjfalskdjfalk' hat kein Schema oder Netloc.

' https://stackoverflow.com ' ist wahrscheinlich eine gültige URL.

Hier ist eine präzisere Funktion:

from urllib.parse import urlparse

min_attributes = ('scheme', 'netloc')


def is_valid(url, qualifying=min_attributes):
    tokens = urlparse(url)
    return all([getattr(tokens, qualifying_attr)
                for qualifying_attr in qualifying])
dmmfll
quelle
4

BEARBEITEN

Wie von @Kwame hervorgehoben, überprüft der folgende Code die URL auch dann, wenn das .comoder .cousw. nicht vorhanden sind.

@Blaise weist auch darauf hin, dass URLs wie https://www.google eine gültige URL sind und Sie eine DNS-Prüfung durchführen müssen, um zu überprüfen, ob sie separat aufgelöst wird oder nicht.

Das ist einfach und funktioniert:

So min_attrenthält die Basis - Set von Strings , die Bedürfnisse vorhanden sein , um die Gültigkeit einer URL, also zu definieren http://Teil und google.comTeil.

urlparse.schemeGeschäfte http://und

urlparse.netloc Speichern Sie den Domainnamen google.com

from urlparse import urlparse
def url_check(url):

    min_attr = ('scheme' , 'netloc')
    try:
        result = urlparse(url)
        if all([result.scheme, result.netloc]):
            return True
        else:
            return False
    except:
        return False

all()Gibt true zurück, wenn alle darin enthaltenen Variablen true zurückgeben. Wenn also result.schemeund result.netlocvorhanden ist, dh einen Wert hat, ist die URL gültig und wird daher zurückgegeben True.

Padam Sethia
quelle
Oh, netter Fang. Ich denke, ich muss meinen Code zurücknehmen. Was bevorzugen Sie, gibt es andere Optionen als Regex.
Padam Sethia
https://www.googleist eine gültige URL. Möglicherweise wird es nicht wirklich aufgelöst, aber wenn Sie sich darum kümmern, müssen Sie eine DNS-Überprüfung durchführen.
Blaise
schluckt Ausnahmen
ivan_pozdeev
2

Überprüfen Sie die URL mit urllibund Django-ähnlichen Regex

Der Regex für die Django-URL-Validierung war eigentlich ziemlich gut, aber ich musste ihn für meinen Anwendungsfall ein wenig optimieren. Fühlen Sie sich frei, es an Ihre anzupassen!

Python 3.7

import re
import urllib

# Check https://regex101.com/r/A326u1/5 for reference
DOMAIN_FORMAT = re.compile(
    r"(?:^(\w{1,255}):(.{1,255})@|^)" # http basic authentication [optional]
    r"(?:(?:(?=\S{0,253}(?:$|:))" # check full domain length to be less than or equal to 253 (starting after http basic auth, stopping before port)
    r"((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+" # check for at least one subdomain (maximum length per subdomain: 63 characters), dashes in between allowed
    r"(?:[a-z0-9]{1,63})))" # check for top level domain, no dashes allowed
    r"|localhost)" # accept also "localhost" only
    r"(:\d{1,5})?", # port [optional]
    re.IGNORECASE
)
SCHEME_FORMAT = re.compile(
    r"^(http|hxxp|ftp|fxp)s?$", # scheme: http(s) or ftp(s)
    re.IGNORECASE
)

def validate_url(url: str):
    url = url.strip()

    if not url:
        raise Exception("No URL specified")

    if len(url) > 2048:
        raise Exception("URL exceeds its maximum length of 2048 characters (given length={})".format(len(url)))

    result = urllib.parse.urlparse(url)
    scheme = result.scheme
    domain = result.netloc

    if not scheme:
        raise Exception("No URL scheme specified")

    if not re.fullmatch(SCHEME_FORMAT, scheme):
        raise Exception("URL scheme must either be http(s) or ftp(s) (given scheme={})".format(scheme))

    if not domain:
        raise Exception("No URL domain specified")

    if not re.fullmatch(DOMAIN_FORMAT, domain):
        raise Exception("URL domain malformed (domain={})".format(domain))

    return url

Erläuterung

  • Der Code überprüft nur den schemeund einen netlocTeil einer bestimmten URL. (Um dies richtig zu machen, urllib.parse.urlparse()teile ich die URL mit in die beiden entsprechenden Teile, die dann mit den entsprechenden Regex-Begriffen abgeglichen werden.)
  • Der netlocTeil stoppt vor dem ersten Auftreten eines Schrägstrichs /, sodass portZahlen immer noch Teil von netlocz.

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • IPv4-Adressen werden ebenfalls überprüft

IPv6-Unterstützung

Wenn der URL-Validator auch mit IPv6-Adressen funktionieren soll, gehen Sie wie folgt vor:

  • Fügen Sie is_valid_ipv6(ip)aus Markus Jarderots Antwort hinzu , die einen wirklich guten IPv6-Validator-Regex hat
  • and not is_valid_ipv6(domain)Zum letzten hinzufügenif

Beispiele

Hier sind einige Beispiele für den regulären Ausdruck für den netloc(aka domain) Teil in Aktion:

winklerrr
quelle
1

Alle oben genannten Lösungen erkennen eine Zeichenfolge wie " http://www.google.com/path,www.yahoo.com/path " als gültig an. Diese Lösung funktioniert immer so, wie sie sollte

import re

# URL-link validation
ip_middle_octet = u"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5]))"
ip_last_octet = u"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))"

URL_PATTERN = re.compile(
                        u"^"
                        # protocol identifier
                        u"(?:(?:https?|ftp|rtsp|rtp|mmp)://)"
                        # user:pass authentication
                        u"(?:\S+(?::\S*)?@)?"
                        u"(?:"
                        u"(?P<private_ip>"
                        # IP address exclusion
                        # private & local networks
                        u"(?:localhost)|"
                        u"(?:(?:10|127)" + ip_middle_octet + u"{2}" + ip_last_octet + u")|"
                        u"(?:(?:169\.254|192\.168)" + ip_middle_octet + ip_last_octet + u")|"
                        u"(?:172\.(?:1[6-9]|2\d|3[0-1])" + ip_middle_octet + ip_last_octet + u"))"
                        u"|"
                        # IP address dotted notation octets
                        # excludes loopback network 0.0.0.0
                        # excludes reserved space >= 224.0.0.0
                        # excludes network & broadcast addresses
                        # (first & last IP address of each class)
                        u"(?P<public_ip>"
                        u"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])"
                        u"" + ip_middle_octet + u"{2}"
                        u"" + ip_last_octet + u")"
                        u"|"
                        # host name
                        u"(?:(?:[a-z\u00a1-\uffff0-9_-]-?)*[a-z\u00a1-\uffff0-9_-]+)"
                        # domain name
                        u"(?:\.(?:[a-z\u00a1-\uffff0-9_-]-?)*[a-z\u00a1-\uffff0-9_-]+)*"
                        # TLD identifier
                        u"(?:\.(?:[a-z\u00a1-\uffff]{2,}))"
                        u")"
                        # port number
                        u"(?::\d{2,5})?"
                        # resource path
                        u"(?:/\S*)?"
                        # query string
                        u"(?:\?\S*)?"
                        u"$",
                        re.UNICODE | re.IGNORECASE
                       )
def url_validate(url):   
    """ URL string validation
    """                                                                                                                                                      
    return re.compile(URL_PATTERN).match(url)
Сергей Дорофий
quelle