Python - Cách xác thực url trong python? (Không đúng định dạng hoặc không)


116

Tôi có urltừ người dùng và tôi phải trả lời bằng HTML đã tìm nạp.

Làm cách nào để kiểm tra xem URL có bị sai hay không?

Ví dụ :

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

Làm thế nào chúng ta có thể đạt được điều này?



1
Chỉ cần cố gắng đọc nó, nếu chẳng hạn như httplib ném ra một ngoại lệ, thì bạn sẽ biết nó không hợp lệ. Không phải tất cả các url được định dạng tốt đều hợp lệ !
carlpett

1
điều này sẽ giúp bạn: stackoverflow.com/questions/827557/…
DhruvPathak

10
url='http://google' không bị dị dạng. Lược đồ + tên máy luôn hợp lệ.
Viktor Joras

Điều này có trả lời câu hỏi của bạn không? Làm cách nào để xác thực URL bằng một biểu thức chính quy trong Python?
AMC

Câu trả lời:


90

xác thực url django regex ( nguồn ):

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

một sự tò mò ... bạn đã thêm ftp? Hay tôi có một phiên bản django cũ?
Ruggero Turra

2
@ yugal-jindle sitedomain không phải là url hợp lệ. bảo tàng là bởi vì .museum là miền cấp cao nhất (ICANN [1] định nghĩa chúng), chứ không phải là một sitedomain. [1] icann.org
glarrain 10/10/12

1
Cái này dường như không làm việc với username: password@example.com URL phong cách
Adam Baxter


2
Điều này sẽ không hoạt động đối với các url IPv6, có dạnghttp://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine

124

Thực ra, tôi nghĩ đây là cách tốt nhất.

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

Nếu bạn đặt verify_existsthành True, nó sẽ thực sự xác minh rằng URL tồn tại, nếu không, nó sẽ chỉ kiểm tra xem nó có được định dạng chính xác hay không.

chỉnh sửa: à vâng, câu hỏi này trùng lặp với câu này: Làm cách nào để kiểm tra xem URL có tồn tại với trình xác thực của Django không?


46
Nhưng điều này sẽ chỉ hoạt động trong môi trường django chứ không phải cách khác.
Yugal Jindle

19
verify_existskhông được dùng nữa. -1
g33kz0r

2
Địa chỉ: từ django.conf thiết lập nhập khẩu settings.configure (DEBUG = False) và loại bỏ các verify_exists để giữ cho nó làm việc với django 1,5
Dukeatcoding

1
@YugalJindle Đúng, nhưng tước nó khỏi Django thì gần như tầm thường: D. Vì vậy, tôi sử dụng phương pháp này
swdev

7
Lưu ý, với django> = 1.5 thì không verify_existscòn nữa. Ngoài ra thay vì valbiến bạn có thể gọi nó như thếURLValidator()('http://www.google.com')
luckydonald

122

Sử dụng gói trình xác thực :

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

Cài đặt nó từ PyPI với pip ( pip install validators).


5
Nó sẽ tạo ra lỗi cho các url của tệp. Giống như "tệp: ///users/file.txt"
Devavrata

2
Không tìm được url máy chủ cục bộ validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom

5
@Lal Zada, trước khi bạn yêu cầu một cái gì đó như thế này, đặt một số nỗ lực và kiểm tra mã, regexp khá tốt thực sự: validators.readthedocs.io/en/latest/_modules/validators/...
Drachenfels

1
Việc xác thực fn của gói có nhiều hạn chế tùy ý, vì vậy lời khuyên khủng khiếp là bạn nên đề xuất nó như một giải pháp chung.
ivan_pozdeev

2
@ivan_pozdeev: nếu nó khủng khiếp, thì hãy đề xuất một giải pháp tốt hơn
Jabba

62

Phiên bản Đúng hay Sai, dựa trên câu trả lời @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))

Cung cấp:

True
False
False
False

8
Tôi không biết bạn có thể kiểm tra câu lệnh if với danh sách các phần tử không phải Không có. Thật là hữu ích. Đồng thời +1 để sử dụng mô-đun tích hợp
Marc Maxmeister

9
Điều này cho phép mọi thứ. Nó trả về Truecho chuỗi fakehoặc thậm chí cho một chuỗi trống. Sẽ không bao giờ có bất kỳ lỗi nào vì các thuộc tính đó luôn ở đó và danh sách sẽ luôn có giá trị boolean là True vì nó chứa các thuộc tính đó. Ngay cả khi tất cả các thuộc tính là Không, danh sách vẫn sẽ không trống. Bạn cần một số xác nhận của các thuộc tính vì mọi thứ trôi qua theo cách bạn có hiện tại.
zondo

3
Danh sách các đối tượng sai được đánh giá thành Đúng: print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")in ra "Tôi đúng". khi tôi chạy nó. [result.scheme, result.netloc, result.path]luôn luôn đánh giá True. print("I am True") if [] else print("I am False.")in "Tôi sai." vì vậy danh sách trống là Sai. Nội dung của mảng cần được đánh giá với một cái gì đó như allhàm.
dmmfll

3
Không chắc tại sao bạn lại yêu cầu một đường dẫn như vậy. Bạn nên loại bỏ result.pathkhỏi bài kiểm tra.
Jerinaw

1
Điều này là đủ tốt cho tôi, cảm ơn. Tôi vừa thêm một xác thực đơn giản cho scheme: if not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
Alexander Fortin

20

Ngày nay, tôi sử dụng những điều sau đây, dựa trên câu trả lời của Padam:

$ python --version
Python 3.6.5

Và đây là cách nó trông:

from urllib.parse import urlparse

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

Chỉ cần sử dụng is_url("http://www.asdf.com").

Hy vọng nó giúp!


Nó không thành công trong trường hợp tên miền bắt đầu bằng dấu gạch ngang, không hợp lệ. tools.ietf.org/html/rfc952
Björn Lindqvist

1
Điều này chỉ tốt để chia nhỏ các thành phần trong trường hợp đặc biệt mà URI được biết là KHÔNG bị sai định dạng. Như tôi đã trả lời trước đó cho câu trả lời tương tự khác, điều này xác thực các URI không đúng định dạng, như https://https://https://www.foo.bar.
ingyhere

9

lưu ý - lepl không còn được hỗ trợ, xin lỗi (bạn có thể sử dụng nó và tôi nghĩ mã bên dưới hoạt động, nhưng nó sẽ không nhận được bản cập nhật).

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html xác định cách thực hiện việc này (cho url http và email). tôi đã triển khai các đề xuất của nó trong python bằng cách sử dụng lepl (thư viện phân tích cú pháp). xem http://acooke.org/lepl/rfc3696.html

sử dụng:

> 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
Gọn gàng, nhưng còn FTP hoặc HTTPS thì sao?
Adam Parkin

6
bạn chưa chia mã và triển khai chúng? nó là mã nguồn mở.
andrew cooke

1
lepl hiện đang ngưng bởi tác giả acooke.org/lepl/discontinued.html EDIT: heh, chỉ nhận ra rằng bạn tác giả
Emmett Butler

1
lưu ý: lepl.apps.rfc3696 không hoạt động trong Python
3.7.4

9

Tôi đã truy cập trang này để cố gắng tìm ra một cách lành mạnh để xác nhận các chuỗi là url "hợp lệ". Tôi chia sẻ ở đây giải pháp của tôi bằng cách sử dụng python3. Không cần thêm thư viện.

Xem https://docs.python.org/2/library/urlparse.html nếu bạn đang sử dụng python2.

Xem https://docs.python.org/3.0/library/urllib.parse.html nếu bạn đang sử dụng python3 như tôi.

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 = '', segment = '')

ParseResult (Scheme = 'https', netloc = 'stackoverflow.com', path = '', params = '', query = '', segment = '')

Chuỗi 'dkakasdkjdjakdjadjfalskdjfalk' không có lược đồ hoặc netloc.

' https://stackoverflow.com ' có thể là một url hợp lệ.

Đây là một hàm ngắn gọn hơn:

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

BIÊN TẬP

Như đã chỉ ra bởi @Kwame, đoạn mã dưới đây xác thực url ngay cả khi .comhoặc .cov.v. không có.

cũng được @Blaise chỉ ra, các URL như https://www.google là một URL hợp lệ và bạn cần thực hiện kiểm tra DNS để kiểm tra xem nó có giải quyết được hay không.

Điều này đơn giản và hoạt động:

Vì vậy, min_attrchứa tập hợp các chuỗi cơ bản cần phải có để xác định tính hợp lệ của một URL, tức là http://một phần và google.commột phần.

urlparse.schemecửa hàng http://

urlparse.netloc lưu trữ tên miền 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()trả về true nếu tất cả các biến bên trong nó trả về true. Vì vậy, nếu result.schemeresult.netlochiện tại tức là có một số giá trị thì URL hợp lệ và do đó trả về True.


Ồ, bắt rất hay .. Tôi đoán tôi phải lấy lại mã của mình. Bạn thích gì hơn, có bất kỳ lựa chọn nào khác ngoại trừ regex.
Padam Sethia

https://www.googlelà một URL hợp lệ. Nó có thể không thực sự giải quyết được, nhưng nếu bạn quan tâm, bạn cần phải kiểm tra DNS.
Blaise

nuốt ngoại lệ
ivan_pozdeev

2

Xác thực URL với urllibvà regex giống Django

Regex xác thực URL Django thực sự khá tốt nhưng tôi cần phải điều chỉnh nó một chút cho trường hợp sử dụng của mình. Hãy điều chỉnh nó cho phù hợp với bạn!

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

Giải trình

  • Mã chỉ xác thực phần schemenetloccủa một URL nhất định. (Để làm điều này đúng cách, tôi chia URL urllib.parse.urlparse()thành hai phần theo các phần sau đó được đối sánh với các điều khoản regex tương ứng.)
  • Phần netlocdừng trước lần xuất hiện đầu tiên của dấu gạch chéo /, vì vậy các portsố vẫn là một phần của netloc, ví dụ:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • Địa chỉ IPv4 cũng được xác thực

Hỗ trợ IPv6

Nếu bạn muốn trình xác thực URL cũng hoạt động với địa chỉ IPv6, hãy làm như sau:

Ví dụ

Dưới đây là một số ví dụ về regex cho phần netloc(hay còn gọi là domain) đang hoạt động:


1

Tất cả các giải pháp trên đều công nhận chuỗi như " http://www.google.com.vn/path,www.yahoo.com/path " là hợp lệ. Giải pháp này luôn hoạt động như bình thường

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.