Python - Come convalidare un URL in Python? (Malformato o no)


116

Ho urlda parte dell'utente e devo rispondere con l'HTML recuperato.

Come posso verificare se l'URL non è corretto o no?

Per esempio :

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

Come possiamo raggiungere questo risultato ?



1
Prova a leggerlo, se per esempio httplib genera un'eccezione, allora saprai che non era valido. Non tutti gli URL ben formati sono validi !
carlpett


10
url='http://google' non è malformato. Schema + hostname è sempre valido.
Viktor Joras

Risposte:


90

regex di convalida dell'URL di django ( origine ):

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

una curiosità ... hai aggiunto il ftp? O ho una vecchia versione di django?
Ruggero Turra

2
@ yugal-jindle sitedomain non è un URL valido. museum è perché .museum è un dominio di primo livello (ICANN [1] li definisce) e non un sito. [1] icann.org
glarrain

1
Questo non sembra funzionare con il nome utente: URL in stile password@example.com
Adam Baxter


2
Questo non funzionerà per gli URL IPv6, che hanno il formatohttp://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine

124

In realtà, penso che questo sia il modo migliore.

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

Se imposti verify_existssu True, verificherà effettivamente che l'URL esista, altrimenti controllerà solo se è formato correttamente.

modifica: ah sì, questa domanda è un duplicato di questa: come posso verificare se esiste un URL con i validatori di Django?


46
Ma questo funzionerà solo nell'ambiente Django, non altrimenti.
Yugal Jindle

19
verify_existsè deprecato. -1
g33kz0r

2
Aggiungi: da django.conf importa le impostazioni settings.configure (DEBUG = False) e rimuovi la verifica_exists per mantenerla funzionante con django 1.5
Dukeatcoding

1
@YugalJindle Corretto, ma toglierlo a Django è quasi banale: D. Quindi, utilizzo questo metodo
swdev

7
Nota, con django> = 1.5 non c'è verify_existspiù. Inoltre, al posto della valvariabile puoi chiamarla comeURLValidator()('http://www.google.com')
luckydonald

122

Usa il pacchetto validatori :

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

Installalo da PyPI con pip ( pip install validators).


5
Genererà un errore per gli URL dei file. Come "file: ///users/file.txt"
Devavrata

2
Errore per gli URL localhost validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom

5
@Lal Zada, prima di richiedere qualcosa del genere, fai un po 'di sforzo e controlla il codice, regexp è abbastanza buono in realtà: validators.readthedocs.io/en/latest/_modules/validators/…
Drachenfels

1
La fn di convalida del pacchetto ha molte limitazioni arbitrarie, quindi è un pessimo consiglio suggerirlo come soluzione generale.
ivan_pozdeev

2
@ivan_pozdeev: se è terribile, suggerisci una soluzione migliore
Jabba

62

Una versione True o False, basata sulla risposta @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))

dà:

True
False
False
False

8
Non sapevo che potessi testare un'istruzione if con un elenco di elementi diversi da None. È utile. Inoltre +1 per l'utilizzo di un modulo integrato
Marc Maxmeister

9
Questo permette tutto. Restituisce Trueper la stringa fakeo anche per una stringa vuota. Non ci saranno mai errori perché questi attributi sono sempre presenti e l'elenco avrà sempre un valore booleano True perché contiene quegli attributi. Anche se tutti gli attributi sono Nessuno, l'elenco non sarà ancora vuoto. Hai bisogno di una convalida degli attributi perché tutto procede come lo hai ora.
zondo

3
Gli elenchi di oggetti falsi restituiscono True: print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")stampa "Sono vero". quando lo eseguo. [result.scheme, result.netloc, result.path]valuta sempre a True. print("I am True") if [] else print("I am False.")stampa "Sono falso". quindi gli elenchi vuoti sono falsi. Il contenuto dell'array deve essere valutato con qualcosa di simile alla allfunzione.
dmmfll

3
Non sono sicuro del motivo per cui avresti bisogno di un percorso come quello. Dovresti rimuovere result.pathdal test.
Jerinaw

1
Questo è abbastanza buono per me, grazie. Ho appena aggiunto una semplice convalida per scheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin,

20

Al giorno d'oggi, utilizzo quanto segue, sulla base della risposta di Padam:

$ python --version
Python 3.6.5

Ed è così che appare:

from urllib.parse import urlparse

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

Basta usare is_url("http://www.asdf.com") .

Spero che sia d'aiuto!


Non riesce nel caso in cui il nome di dominio inizi con un trattino, che non è valido. tools.ietf.org/html/rfc952
Björn Lindqvist

1
Questo è utile solo per suddividere i componenti nel caso speciale in cui si sa che l'URI NON è mal formato. Come ho risposto in precedenza all'altra risposta simile, questo convalida gli URI non validi, come https://https://https://www.foo.bar.
ingyhere

9

Nota - lepl non è più supportato, mi dispiace (puoi usarlo e penso che il codice seguente funzioni, ma non riceverà aggiornamenti).

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html definisce come farlo (per URL http ed email). ho implementato i suoi consigli in Python usando lepl (una libreria parser). vedere http://acooke.org/lepl/rfc3696.html

usare:

> 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
Bello, ma per quanto riguarda FTP o HTTPS?
Adam Parkin

6
non hai biforcato il codice e li hai implementati? è open source.
andrew cooke

1
lepl è ora interrotto dall'autore acooke.org/lepl/discontinued.html EDIT: heh, mi sono appena reso conto che sei l'autore
Emmett Butler

1
nota: lepl.apps.rfc3696 non funziona in Python 3.7.4
Sheile

9

Sono arrivato su questa pagina cercando di trovare un modo sano per convalidare le stringhe come URL "validi". Condivido qui la mia soluzione usando python3. Non sono necessarie librerie aggiuntive.

Vedi https://docs.python.org/2/library/urlparse.html se stai usando python2.

Vedi https://docs.python.org/3.0/library/urllib.parse.html se stai usando python3 come me.

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 = '', percorso = 'dkakasdkjdjakdjadjfalskdjfalk', params = '', query = '', fragment = '')

ParseResult (schema = 'https', netloc = 'stackoverflow.com', percorso = '', params = '', query = '', fragment = '')

La stringa "dkakasdkjdjakdjadjfalskdjfalk" non ha schema o netloc.

" https://stackoverflow.com " è probabilmente un URL valido.

Ecco una funzione più concisa:

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

MODIFICARE

Come sottolineato da @Kwame, il codice seguente convalida l'URL anche se .como .coecc non sono presenti.

sottolineato anche da @Blaise, URL come https://www.google sono un URL valido e devi eseguire un controllo DNS per verificare se si risolve o meno, separatamente.

Questo è semplice e funziona:

Quindi min_attrcontiene l'insieme di stringhe di base che deve essere presente per definire la validità di un URL, ovvero http://parte e google.comparte.

urlparse.schemenegozi http://e

urlparse.netloc memorizzare il nome di dominio 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()restituisce true se tutte le variabili al suo interno restituiscono true. Quindi, se result.schemee result.netlocè presente, cioè ha un valore, l'URL è valido e quindi restituisce True.


Oh, bella presa .. immagino di dover riprendere il mio codice. Cosa preferisci, ci sono altre opzioni tranne regex.
Padam Sethia

https://www.googleè un URL valido. Potrebbe non risolversi effettivamente, ma se ti interessa è necessario eseguire un controllo DNS.
Blaise

rondini eccezioni
ivan_pozdeev

2

Convalida l'URL con urllibe regex simile a Django

La regex di convalida dell'URL di Django era in realtà piuttosto buona, ma avevo bisogno di modificarla un po 'per il mio caso d'uso. Sentiti libero di adattarlo al tuo!

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

Spiegazione

  • Il codice convalida solo schemee netlocparte di un determinato URL. (Per farlo correttamente, ho diviso l'URL con urllib.parse.urlparse()nelle due parti corrispondenti che vengono quindi abbinate ai termini regex corrispondenti.)
  • La netlocparte si ferma prima della prima occorrenza di una barra /, quindi i portnumeri fanno ancora parte del netloc, ad esempio:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • Vengono convalidati anche gli indirizzi IPv4

Supporto IPv6

Se desideri che lo strumento di convalida URL funzioni anche con gli indirizzi IPv6, procedi come segue:

Esempi

Ecco alcuni esempi di regex per la parte netloc(aka domain) in azione:


1

Tutte le soluzioni precedenti riconoscono una stringa come " http://www.google.com/path,www.yahoo.com/path " come valida. Questa soluzione funziona sempre come dovrebbe

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.