如何使用 `urlparse` 检查 URL 是否有效?

14 投票
4 回答
32255 浏览
提问于 2025-04-18 16:57

我想在打开一个网址读取数据之前,先检查一下这个网址是否有效。

我之前使用的是来自urlparse这个包里的urlparse函数:

if not bool(urlparse.urlparse(url).netloc):
 # do something like: open and read using urllin2

但是,我发现有些有效的网址却被当作无效的,比如:

url = upload.wikimedia.org/math/8/8/d/88d27d47cea8c88adf93b1881eda318d.png

这个网址是有效的(我可以用我的浏览器打开它)。

有没有更好的方法来检查网址是否有效呢?

4 个回答

1

没有协议的链接其实是不合法的,你的浏览器很聪明,会自动帮你加上http://这个协议。一个好的做法是先检查一下链接是否有协议(可以用not re.match(r'^[a-zA-Z]+://', url)这个方法),如果没有,就在前面加上http://

5

你可以试试下面这个函数,它会检查在解析网址后得到的 schemenetlocpath 变量。这个函数支持 Python 2 和 3。

try:
    # python 3
    from urllib.parse import urlparse
except ImportError:
    from urlparse import urlparse

def url_validator(url):
    try:
        result = urlparse(url)
        components = [result.scheme, result.path]
        if result.netloc != "":
            components.append(result.netloc)
        return all(components)
    except:
        return False
13

你可以检查一下这个网址是否有协议:

>>> url = "no.scheme.com/math/12345.png"
>>> parsed_url = urlparse.urlparse(url)
>>> bool(parsed_url.scheme)
False

如果有的话,你可以把这个协议替换掉,这样就能得到一个真正有效的网址:

>>> parsed_url.geturl()
"no.scheme.com/math/12345.png"
>>> parsed_url = parsed_url._replace(**{"scheme": "http"})
>>> parsed_url.geturl()
'http:///no.scheme.com/math/12345.png'
14

总结一下:其实你是无法做到的。之前给出的每个答案都漏掉了一个或多个情况。

  1. 字符串是 google.com(无效,因为没有协议,虽然浏览器默认会假设是http)。用Urlparse解析时会缺少协议和网络位置。所以 all([result.scheme, result.netloc, result.path]) 在这种情况下似乎能正常工作。
  2. 字符串是 http://google(无效,因为缺少.com)。用Urlparse解析时只会缺少路径。同样, all([result.scheme, result.netloc, result.path]) 似乎能捕捉到这个情况。
  3. 字符串是 http://google.com/(正确)。用Urlparse解析时会填充协议、网络位置和路径。所以在这种情况下 all([result.scheme, result.netloc, result.path]) 工作得很好。
  4. 字符串是 http://google.com(正确)。用Urlparse解析时只会缺少路径。所以在这种情况下 all([result.scheme, result.netloc, result.path]) 似乎会给出错误的结果

从以上情况可以看出,最接近解决方案的是 all([result.scheme, result.netloc, result.path])。但这只在URL包含路径的情况下有效(即使那个路径是/)。

即使你试图强制添加路径(比如 urlparse(urljoin(your_url, "/"))),在第二种情况下你仍然会得到错误的结果。

也许可以考虑更复杂的方式,比如

final_url = urlparse(urljoin(your_url, "/"))
is_correct = (all([final_url.scheme, final_url.netloc, final_url.path]) 
              and len(final_url.netloc.split(".")) > 1)

也许你还想跳过协议检查,如果没有协议就假设是http。但即便如此,这也只能解决到一定程度。虽然它覆盖了上述情况,但并没有完全涵盖URL包含IP而不是主机名的情况。对于这种情况,你需要验证这个IP是否是正确的IP。而且还有更多的场景。可以参考 https://en.wikipedia.org/wiki/URL 来思考更多的情况。

撰写回答