Python - Jak sprawdzić poprawność adresu URL w Pythonie? (Źle sformułowane lub nie)


116

Mam urlod użytkownika i muszę odpowiedzieć pobranym kodem HTML.

Jak mogę sprawdzić, czy adres URL jest nieprawidłowy, czy nie?

Na przykład :

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

Jak możemy to osiągnąć?



1
Po prostu spróbuj go przeczytać, jeśli na przykład httplib rzuci wyjątek, będziesz wiedział, że był nieprawidłowy. Nie wszystkie poprawnie sformułowane adresy URL są prawidłowe !
carlpett


10
url='http://google' nie jest zniekształcony. Schemat + nazwa hosta są zawsze prawidłowe.
Viktor Joras

Odpowiedzi:


90

wyrażenie regularne sprawdzania poprawności adresu URL django ( źródło ):

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

ciekawostka ... czy dodałeś ftp? A może mam starą wersję django?
Ruggero Turra

2
@ yugal-jindle sitedomain nie jest prawidłowym adresem URL. muzeum jest spowodowane tym, że .museum jest domeną najwyższego poziomu (definiuje je ICANN [1]), a nie domeną lokalną. [1] icann.org
glarrain

1
Ten wydaje się nie działać z adresami URL w stylu nazwa użytkownika: hasło@example.com
Adam Baxter


2
To nie zadziała w przypadku adresów URL IPv6, które mają postaćhttp://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine

124

Właściwie myślę, że to najlepszy sposób.

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

Jeśli ustawisz verify_existsna True, faktycznie zweryfikuje, czy adres URL istnieje, w przeciwnym razie po prostu sprawdzi, czy został poprawnie utworzony.

edit: ach tak, to pytanie jest powtórzeniem tego: Jak mogę sprawdzić, czy istnieje adres URL z walidatorami Django?


46
Ale to zadziała tylko w środowisku django, a nie inaczej.
Yugal Jindle

19
verify_existsjest przestarzałe. -1
g33kz0r

2
Dodaj: z django.conf importuj ustawienia settings.configure (DEBUG = False) i usuń plik verify_exists, aby działała z django 1.5
Dukeatcoding

1
@YugalJindle Dobrze, ale usunięcie go z Django jest prawie trywialne: D. Więc używam tej metody
swdev

7
Uwaga, z django> = 1.5 verify_existsjuż nie ma. Również zamiast valzmiennej możesz ją nazwać jakURLValidator()('http://www.google.com')
luckydonald

122

Skorzystaj z pakietu walidatorów :

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

Zainstaluj go z PyPI za pomocą pip ( pip install validators).


5
Wystąpi błąd dla adresów URL plików. Na przykład „file: ///users/file.txt”
Devavrata

2
Niepowodzenie w przypadku adresów URL hosta lokalnego validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom

5
@Lal Zada, zanim zgłosisz coś takiego, włóż trochę wysiłku i sprawdź kod, regexp jest właściwie całkiem niezłe: validators.readthedocs.io/en/latest/_modules/validators/ ...
Drachenfels

1
Walidacja fn pakietu ma wiele arbitralnych ograniczeń, więc sugerowanie go jako ogólnego rozwiązania jest okropną radą.
ivan_pozdeev

2
@ivan_pozdeev: jeśli to straszne, zaproponuj lepsze rozwiązanie
Jabba

62

Wersja True or False, oparta na odpowiedzi @DMfll:

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

Daje:

True
False
False
False

8
Nie wiedziałem, że możesz przetestować instrukcję if z listą elementów innych niż None. To jest pomocne. Również +1 za użycie wbudowanego modułu
Marc Maxmeister

9
To pozwala na wszystko. Wraca Truepo łańcuch fakelub nawet po pusty łańcuch. Nigdy nie będzie żadnych błędów, ponieważ te atrybuty są zawsze obecne, a lista zawsze będzie miała wartość logiczną True, ponieważ zawiera te atrybuty. Nawet jeśli wszystkie atrybuty mają wartość Brak, lista nadal nie będzie pusta. Potrzebujesz weryfikacji atrybutów, ponieważ wszystko przebiega tak, jak masz teraz.
zondo

3
Listy fałszywych obiektów są oceniane jako Prawda: wyświetla print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")„Jestem prawdziwy”. kiedy go uruchomię. [result.scheme, result.netloc, result.path]zawsze ocenia do True. print("I am True") if [] else print("I am False.")wyświetla "Nie jestem fałszywy". więc puste listy są fałszywe. Zawartość tablicy wymaga oceny za pomocą czegoś podobnego do allfunkcji.
dmmfll

3
Nie jestem pewien, dlaczego potrzebujesz takiej ścieżki. Należy usunąć result.pathz testu.
Jerinaw

1
To mi wystarczy, dzięki. Właśnie dodałem prostą weryfikację dla scheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin

20

Obecnie używam następujących, w oparciu o odpowiedź Padama:

$ python --version
Python 3.6.5

A tak to wygląda:

from urllib.parse import urlparse

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

Po prostu użyj is_url("http://www.asdf.com") .

Mam nadzieję, że to pomoże!


Nie powiedzie się, jeśli nazwa domeny zaczyna się od myślnika, co jest nieprawidłowe. tools.ietf.org/html/rfc952
Björn Lindqvist

1
Dobrze jest podzielić komponenty tylko w szczególnym przypadku, gdy wiadomo, że identyfikator URI NIE jest nieprawidłowo sformułowany. Jak już wcześniej odpowiedziałem na inną podobną odpowiedź, potwierdza to źle sformułowane identyfikatory URI, takie jak https://https://https://www.foo.bar.
ingyhere

9

Uwaga - lepl nie jest już obsługiwany, przepraszam (możesz go używać i myślę, że poniższy kod działa, ale nie otrzyma aktualizacji).

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html definiuje, jak to zrobić (dla adresów URL http i e-maili). Zaimplementowałem jego zalecenia w Pythonie przy użyciu lepl (biblioteki parsera). zobacz http://acooke.org/lepl/rfc3696.html

używać:

> easy_install lepl
...
> python
...
>>> from lepl.apps.rfc3696 import HttpUrl
>>> validator = HttpUrl()
>>> validator('google')
False
>>> validator('http://google')
False
>>> validator('http://google.com')
True

2
Zgrabnie, ale co z FTP lub HTTPS?
Adam Parkin

6
nie rozwidliłeś kodu i nie zaimplementowałeś ich? to jest open source.
Andrew Cooke

1
lepl jest teraz przerwane przez autora acooke.org/lepl/discontinued.html EDIT: heh, po prostu sobie sprawę, że to autor
Emmett Butler

1
uwaga: lepl.apps.rfc3696 nie działa w Pythonie 3.7.4
Sheile

9

Wylądowałem na tej stronie, próbując znaleźć rozsądny sposób weryfikacji ciągów znaków jako „prawidłowych” adresów URL. Udostępniam tutaj moje rozwiązanie za pomocą python3. Nie są wymagane żadne dodatkowe biblioteki.

Zobacz https://docs.python.org/2/library/urlparse.html jeśli używasz python2.

Zobacz https://docs.python.org/3.0/library/urllib.parse.html, jeśli używasz python3 tak jak ja.

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 (schemat = '', netloc = '', ścieżka = 'dkakasdkjdjakdjadjfalskdjfalk', params = '', zapytanie = '', fragment = '')

ParseResult (schemat = 'https', netloc = 'stackoverflow.com', path = '', params = '', query = '', fragment = '')

Ciąg „dkakasdkjdjakdjadjfalskdjfalk” nie ma schematu ani funkcji netloc.

https://stackoverflow.com ” to prawdopodobnie prawidłowy adres URL.

Oto bardziej zwięzła funkcja:

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

4

EDYTOWAĆ

Jak wskazał @Kwame, poniższy kod sprawdza poprawność adresu URL, nawet jeśli nie ma .comlub .coitp.

wskazany również przez @Blaise, adresy URL takie jak https://www.google to prawidłowy adres URL i musisz osobno sprawdzić DNS, aby sprawdzić, czy rozwiązuje się, czy nie.

To jest proste i działa:

min_attrZawiera więc podstawowy zestaw ciągów, które muszą być obecne, aby określić ważność adresu URL, tj. http://Część igoogle.com część.

urlparse.scheme sklepy http:// i

urlparse.netloc przechowywać nazwę domeny 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()zwraca prawdę, jeśli wszystkie zmienne wewnątrz niej zwracają prawdę. Więc jeśli result.schemei result.netlocjest obecne, tj. Ma jakąś wartość, to adres URL jest prawidłowy i dlatego zwraca True.


Och, niezły chwyt… Chyba muszę cofnąć kod. Wolisz, czy są jakieś inne opcje oprócz wyrażenia regularnego.
Padam Sethia,

https://www.googleto prawidłowy adres URL. Może to nie rozwiązać, ale jeśli Ci na tym zależy, musisz sprawdzić DNS.
Blaise,

jaskółki wyjątki
ivan_pozdeev

2

Sprawdź poprawność adresu URL za pomocą urllibi regex podobnego do Django

Wyrażenie regularne sprawdzania poprawności adresu URL w Django było w rzeczywistości całkiem dobre, ale musiałem go trochę poprawić w moim przypadku użycia. Zapraszam do dostosowania go do swojego!

Python 3.7.0

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

Wyjaśnienie

  • Kod weryfikuje tylko schemei netlocczęść podanego adresu URL. (Aby zrobić to poprawnie, rozdzielam adres URL urllib.parse.urlparse()na dwie części, które są następnie dopasowywane do odpowiednich wyrażeń regularnych).
  • netlocCzęść zatrzymuje się przed pierwszym pojawieniem się cięciem /, więc portliczba nadal częścią netloc, na przykład:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • Sprawdzane są również adresy IPv4

Obsługa IPv6

Jeśli chcesz, aby walidator adresów URL działał również z adresami IPv6, wykonaj następujące czynności:

Przykłady

Oto kilka przykładów wyrażenia regularnego dla netloc(aka domain) części w akcji:


2

Wszystkie powyższe rozwiązania uznają ciąg, taki jak „ http://www.google.com/path,www.yahoo.com/path ”, za prawidłowy. To rozwiązanie zawsze działa tak, jak powinno

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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.