Python-如何在python中验证网址?(格式是否正确)


115

url来自用户,我必须用获取的HTML进行回复。

如何检查URL格式是否正确?

例如 :

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

我们怎样才能做到这一点?



1
只是尝试读取它,例如,如果httplib引发异常,那么您将知道它无效。并非所有格式正确的网址都有效
2011年


10
url='http://google' 没有格式错误。架构+主机名始终有效。
维克多·乔拉斯

这回答了你的问题了吗?如何在Python中使用正则表达式验证URL?
AMC

Answers:


90

django url验证正则表达式():

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

好奇心...您添加了ftp吗?还是我有旧的Django版本?
Ruggero Turra

2
@ yugal-jindle sitedomain不是有效的网址。博物馆是因为.museum是顶级域名(ICANN [1]定义了它们),而不是站点域名。[1] icann.org
glarrain 2012年

1
这似乎不适用于username:password@example.com样式的网址
Adam Baxter 2015年


2
这不适用于IPv6网址,其格式为http://[2001:0DB8::3]:8080/index.php?valid=true#result
cimnine

124

实际上,我认为这是最好的方法。

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

如果设置verify_existsTrue,它将实际验证该URL是否存在,否则将仅检查其格式是否正确。

编辑:是的,这个问题是重复的:我如何检查Django的验证程序是否存在URL?


46
但这仅在django环境中有效,而在其他情况下则无效。
Yugal Jindle 2011年

19
verify_exists不推荐使用。-1
g33kz0r 2013年

2
添加:从django.conf导入设置settings.configure(DEBUG = False)并删除verify_exists以使其与django 1.5一起使用
Dukeatcoding

1
@YugalJindle正确,但是从Django中删除它几乎是微不足道的:D。因此,我使用这种方法
swdev

7
请注意,如果django> = 1.5,则verify_exists不再存在。此外,val也可以像变量一样调用它URLValidator()('http://www.google.com')
luckydonald 16/09/21

122

使用验证程序包:

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

使用pip()从PyPI安装它pip install validators


5
它将为文件URL引发错误。像“文件:///users/file.txt”
Devavrata

2
本地主机网址失败 validators.url("http://localhost:8080") ValidationFailure(func=url, args={'public': False, 'value': 'http://localhost:8080'})
Tom

5
@Lal扎达,你要求这样的事情之前,把一些精力和检查代码,正则表达式是相当不错的实际上是: validators.readthedocs.io/en/latest/_modules/validators/...
Drachenfels

1
程序包的验证fn有许多任意限制,因此建议将其作为一般解决方案是一个可怕的建议。
ivan_pozdeev,

2
@ivan_pozdeev:如果太糟糕了,那就建议一个更好的解决方案
Jabba

62

基于@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))

给出:

True
False
False
False

7
我不知道您可以使用非None元素列表测试if语句。有帮助 同时为使用内置模块+1
Marc Maxmeister

9
这样就可以了。它返回True该字符串fake,甚至返回一个空白字符串。永远不会有任何错误,因为这些属性始终存在,并且列表将始终具有布尔值True,因为它包含这些属性。即使所有属性均为“无”,列表仍将为非空。您需要对属性进行一些验证,因为所有事情都会像现在那样进行。
zondo

3
错误对象列表的评估结果为“真”:print("I am true") if [False, None, 0, '', [], {}] else print("I am false.")打印“我是真的”。当我运行它时。[result.scheme, result.netloc, result.path]总是计算为Trueprint("I am True") if [] else print("I am False.")打印“我是假的”。所以空列表是False。数组的内容需要使用类似all函数的评估。
dmmfll

3
不知道为什么需要这样的路径。您应该result.path从测试中删除。
Jerinaw

1
这对我来说已经足够了,谢谢。我刚刚添加了以下简单验证schemeif not all([result.scheme in ["file", "http", "https"], result.netloc, result.path]):
亚历山大·福汀

20

如今,我根据Padam的回答使用以下内容:

$ python --version
Python 3.6.5

这是它的外观:

from urllib.parse import urlparse

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

只需使用is_url("http://www.asdf.com")

希望能帮助到你!


如果域名以短划线开头(无效),则失败。tools.ietf.org/html/rfc952
比约恩·林德奎斯特(

这仅仅是良好的分裂部件的特殊情况下URI是已知存在格式错误。正如我之前对其他类似答案的回答一样,这可以验证格式错误的URI,例如https://https://https://www.foo.bar
ingyhere

9

注意 -lepl不再受支持,对不起(欢迎您使用它,我认为以下代码可以运行,但不会获得更新)。

rfc 3696 http://www.faqs.org/rfcs/rfc3696.html定义了如何执行此操作(针对http网址和电子邮件)。我使用lepl(解析器库)在python中实现了其建议。看到http://acooke.org/lepl/rfc3696.html

使用:

> 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
整洁,但是FTP或HTTPS呢?
亚当·帕金

6
您没有分叉代码并实现它们?它是开源的。
安德鲁·库克

1
lepl现在已被作者acooke.org/lepl/discontinued.html停产。编辑:嘿,刚刚意识到您作者
Emmett Butler

1
注意:lepl.apps.rfc3696在Python 3.7.4中
不起作用

9

我登陆此页面,试图找出一种合理的方法来将字符串验证为“有效” URL。我在这里分享我使用python3的解决方案。无需额外的库。

如果您使用的是python2,请参见https://docs.python.org/2/library/urlparse.html

如果您按原样使用python3,请参阅https://docs.python.org/3.0/library/urllib.parse.html

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 =“'')

'dkakasdkjdjakdjadjfalskdjfalk'字符串没有方案或netloc。

https://stackoverflow.com ”可能是有效的网址。

这是一个更简洁的功能:

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

编辑

正如@Kwame指出的那样,即使.comor .co等不存在,下面的代码也会验证网址。

@Blaise也指出,类似https://www.google的URL是有效的URL,您需要分别进行DNS检查以检查其是否解析。

这很简单并且有效:

因此min_attr包含定义URL有效性(即http://google.com一部分)所需出现的基本字符串集。

urlparse.scheme商店http://

urlparse.netloc 存储域名 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()如果其中所有变量都返回true,则返回true。因此,如果result.schemeresult.netloc存在(即具有某个值),则该URL有效,因此返回True


哦,好极了..我想我必须拿回我的代码。您喜欢什么,除了正则表达式外还有其他选择。
Padam Sethia

https://www.google是有效的网址。它可能无法真正解决,但是如果您担心,则需要进行DNS检查。
布莱斯

吞下例外
ivan_pozdeev,

2

使用urllib和类似Django的正则表达式验证URL

Django URL验证正则表达式实际上非常好,但是我需要针对我的用例进行一些调整。 随时适应您的需求!

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

说明

  • 该代码仅验证给定URL 的schemenetloc部分。(为了正确执行此操作,我将URL分为urllib.parse.urlparse()两个相应的部分,然后与相应的正则表达式匹配。)
  • netloc零件在第一次出现斜线之前停止/,因此port数字仍然是的一部分netloc,例如:

    https://www.google.com:80/search?q=python
    ^^^^^   ^^^^^^^^^^^^^^^^^
      |             |      
      |             +-- netloc (aka "domain" in my code)
      +-- scheme
  • IPv4地址也经过验证

IPv6支持

如果您希望URL验证程序也可以使用IPv6地址,请执行以下操作:

例子

以下是实际netloc(aka domain)部分的正则表达式示例:


1

以上所有解决方案都将“ http://www.google.com/path,www.yahoo.com/path ”之类的字符串识别为有效。此解决方案始终可以正常工作

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.