Python - Bagaimana cara memvalidasi url dengan python? (Rusak atau tidak)

116

Saya dapatkan urldari pengguna dan saya harus membalas dengan HTML yang diambil.

Bagaimana cara memeriksa URL yang salah atau tidak?

Sebagai contoh :

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

Bagaimana kita bisa mencapai hal ini ?

Yugal Jindle
sumber
1
Coba saja untuk membacanya, jika misalnya httplib memberikan pengecualian, maka Anda akan tahu bahwa itu tidak valid. Tidak semua url yang berformat baik valid !
carlpett
1
ini akan membantu Anda: stackoverflow.com/questions/827557/…
DhruvPathak
10
url='http://google' tidak salah format. Skema + nama host selalu valid.
Viktor Joras

Jawaban:

90

regex validasi url django ( sumber ):

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
sumber
rasa ingin tahu ... apakah Anda menambahkan ftp? Atau apakah saya versi django lama?
Ruggero Turra
2
@ Yugal-jindle sitedomain tidak url yang valid. museum adalah karena .museum adalah domain level teratas (ICANN [1] mendefinisikannya), dan bukan domain situs. [1] icann.org
glarrain
1
Yang ini sepertinya tidak berfungsi dengan nama pengguna: URL gaya kata [email protected]
Adam Baxter
2
Ini tidak akan berfungsi untuk url IPv6, yang memiliki bentukhttp://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine
124

Sebenarnya, menurut saya ini adalah cara terbaik.

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

Jika Anda menyetel verify_existske True, itu akan benar-benar memverifikasi bahwa URL itu ada, jika tidak maka hanya akan memeriksa apakah itu dibentuk dengan benar.

edit: ah ya, pertanyaan ini adalah duplikat dari ini: Bagaimana saya dapat memeriksa apakah sebuah URL ada dengan validator Django?

Drekembe
sumber
46
Tetapi ini hanya akan bekerja di lingkungan django bukan sebaliknya.
Yugal Jindle
19
verify_existssudah ditinggalkan. -1
g33kz0r
2
Tambahkan: dari django.conf import settings settings.configure (DEBUG = False) dan hapus verifikasi_exists agar tetap bekerja dengan django 1.5
Dukeatcoding
1
@YugalJindle Benar, tetapi menghapusnya dari Django hampir sepele: D. Jadi, saya menggunakan metode ini
swdev
7
Catatan, dengan django> = 1.5 tidak ada verify_existslagi. Juga sebagai pengganti valvariabel Anda dapat menyebutnya sepertiURLValidator()('http://www.google.com')
luckydonald
122

Gunakan paket validator :

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

Instal dari PyPI dengan pip ( pip install validators).

Jabba
sumber
5
Ini akan memunculkan kesalahan untuk url file. Seperti "file: ///users/file.txt"
Devavrata
2
Gagal untuk url localhost validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom
5
@Lal Zada, sebelum Anda mengklaim sesuatu seperti ini, berusahalah dan periksa kodenya, regexp sebenarnya cukup bagus: validators.readthedocs.io/en/latest/_modules/validators/…
Drachenfels
1
Fn yang memvalidasi paket memiliki banyak batasan yang sewenang-wenang, jadi saran yang buruk adalah menyarankannya sebagai solusi umum.
ivan_pozdeev
2
@ivan_pozdeev: jika itu buruk, maka sarankan solusi yang lebih baik
Jabba
62

Versi Benar atau Salah, berdasarkan jawaban @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))

Memberikan:

True
False
False
False
alemol
sumber
8
Saya tidak tahu Anda dapat menguji pernyataan if dengan daftar elemen non-None. Itu sangat membantu. Juga +1 untuk menggunakan modul built-in
Marc Maxmeister
9
Ini memungkinkan segalanya. Ia mengembalikan Trueuntuk string fakeatau bahkan untuk string kosong. Tidak akan pernah ada kesalahan karena atribut tersebut selalu ada, dan daftar akan selalu memiliki nilai boolean True karena berisi atribut tersebut. Meskipun semua atributnya adalah None, daftar tersebut tetap tidak kosong. Anda memerlukan beberapa validasi atribut karena semuanya berjalan sesuai keinginan Anda sekarang.
zondo
3
Daftar objek palsu dievaluasi ke True: print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")mencetak "Saya benar." ketika saya menjalankannya. [result.scheme, result.netloc, result.path]selalu mengevaluasi ke True. print("I am True") if [] else print("I am False.")mencetak "Saya salah." jadi daftar kosong adalah False. Isi dari array membutuhkan evaluasi dengan sesuatu seperti allfungsinya.
dmmfll
3
Tidak yakin mengapa Anda membutuhkan jalan seperti itu. Anda harus menghapus result.pathdari tes.
Jerinaw
1
Ini cukup bagus untukku, terima kasih. Saya baru saja menambahkan validasi sederhana untuk scheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin
20

Saat ini, saya menggunakan yang berikut ini, berdasarkan jawaban Padam:

$ python --version
Python 3.6.5

Dan seperti inilah tampilannya:

from urllib.parse import urlparse

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

Gunakan saja is_url("http://www.asdf.com").

Semoga membantu!

jonaprieto
sumber
Gagal jika nama domain diawali dengan tanda hubung, yang tidak valid. tools.ietf.org/html/rfc952
Björn Lindqvist
1
Ini hanya bagus untuk memisahkan komponen dalam kasus khusus dimana URI diketahui TIDAK dalam format yang salah. Saat saya membalas sebelumnya untuk jawaban serupa lainnya, ini memvalidasi format URI yang salah, seperti https://https://https://www.foo.bar.
ingyhere
9

note - lepl tidak lagi didukung, maaf (Anda dipersilakan untuk menggunakannya, dan saya pikir kode di bawah ini berfungsi, tetapi tidak akan mendapatkan pembaruan).

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html menjelaskan cara melakukannya (untuk http url dan email). saya mengimplementasikan rekomendasinya dalam python menggunakan lepl (pustaka parser). lihat http://acooke.org/lepl/rfc3696.html

menggunakan:

> 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
sumber
2
Rapi, tapi bagaimana dengan FTP, atau HTTPS?
Adam Parkin
6
Anda belum mem-fork kode dan menerapkannya? itu open source.
andrew cooke
1
lepl sekarang dihentikan oleh penulis acooke.org/lepl/discontinued.html EDIT: heh, baru menyadari bahwa Anda adalah penulisnya
Emmett Butler
1
catatan: lepl.apps.rfc3696 tidak berfungsi dengan Python 3.7.4
Sheile
9

Saya membuka halaman ini mencoba mencari cara yang masuk akal untuk memvalidasi string sebagai url "valid". Saya bagikan di sini solusi saya menggunakan python3. Tidak diperlukan perpustakaan tambahan.

Lihat https://docs.python.org/2/library/urlparse.html jika Anda menggunakan python2.

Lihat https://docs.python.org/3.0/library/urllib.parse.html jika Anda menggunakan python3 seperti saya.

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 (scheme = '', netloc = '', path = 'dkakasdkjdjakdjadjfalskdjfalk', params = '', query = '', fragment = '')

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

String 'dkakasdkjdjakdjadjfalskdjfalk' tidak memiliki skema atau netloc.

' https://stackoverflow.com ' mungkin adalah url yang valid.

Berikut ini fungsi yang lebih ringkas:

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

EDIT

Seperti yang ditunjukkan oleh @Kwame, kode di bawah ini memvalidasi url meskipun .comor .coetc tidak ada.

juga ditunjukkan oleh @Blaise, URL seperti https://www.google adalah URL yang valid dan Anda perlu melakukan pemeriksaan DNS untuk memeriksa apakah itu menyelesaikan atau tidak, secara terpisah.

Ini sederhana dan berhasil:

Jadi min_attrberisi kumpulan string dasar yang perlu ada untuk menentukan validitas URL, yaitu http://bagian dan google.combagian.

urlparse.schemetoko http://dan

urlparse.netloc simpan nama domain 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()mengembalikan nilai benar jika semua variabel di dalamnya mengembalikan nilai benar. Jadi jika result.schemedan result.netlocada yaitu memiliki beberapa nilai maka URL tersebut valid dan karenanya kembali True.

Padam Sethia
sumber
Oh, tangkapan yang bagus .. Kurasa aku harus mengambil kembali kodeku. Apa yang Anda sukai, apakah ada opsi lain selain regex.
Padam Sethia
https://www.googleadalah URL yang valid. Ini mungkin tidak benar-benar menyelesaikan, tetapi jika Anda peduli tentang itu, Anda perlu melakukan pemeriksaan DNS.
Blaise
menelan pengecualian
ivan_pozdeev
2

Validasi URL dengan urllibdan ekspresi reguler seperti Django

Regex validasi URL Django sebenarnya cukup bagus tetapi saya perlu mengubahnya sedikit untuk kasus penggunaan saya. Jangan ragu untuk menyesuaikannya dengan milik Anda!

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

Penjelasan

  • Kode hanya memvalidasi schemedan netlocbagian dari URL tertentu. (Untuk melakukan ini dengan benar, saya membagi URL dengan urllib.parse.urlparse()di dua bagian sesuai yang kemudian dicocokkan dengan istilah regex yang sesuai.)
  • Bagian netlocberhenti sebelum kemunculan pertama garis miring /, jadi portangka masih menjadi bagian dari netloc, misalnya:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • Alamat IPv4 juga divalidasi

Dukungan IPv6

Jika Anda ingin validator URL juga berfungsi dengan alamat IPv6, lakukan hal berikut:

  • Tambahkan is_valid_ipv6(ip)dari jawaban Markus Jarderot , yang memiliki regex validator IPv6 yang sangat bagus
  • Tambahkan and not is_valid_ipv6(domain)ke yang terakhirif

Contoh

Berikut beberapa contoh regex untuk bagian netloc(alias domain) yang sedang bekerja:

winklerrr
sumber
2

Semua solusi di atas mengenali string seperti " http://www.google.com/path,www.yahoo.com/path " sebagai valid. Solusi ini selalu berfungsi sebagaimana mestinya

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)
Сергей Дорофий
sumber