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

14
我想要在读取数据之前检查URL是否有效。 我使用了urlparse包中的urlparse函数:
if not bool(urlparse.urlparse(url).netloc):
 # do something like: open and read using urllin2

然而,我发现有些有效的URL被认为是损坏的,例如:

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

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

有没有更好的方法来检查URL是否有效?


在不带http://的URL前添加它。 - xfx
@xfx 但是我有很多链接,我不知道它们是否以http://开头,也不知道它们是否是有效的URL。我想编写一个函数,可以避免这些错误并告诉我它们的状态。 - Ziva
如果您无论如何都要使用urllib2打开它,那么您不能先打开它并检查返回代码是否等于200吗? - Dunno
@Ziva 在这种情况下,我认为正则表达式是最好的方法。 - xfx
如果主要问题是http://,则if(url [: 7]!= 'http://'):...url ='http://' + url - flau
使用try/except是最好的选择。 - Padraic Cunningham
4个回答

14

TL;DR: 实际上你不能。每个给出的答案都遗漏了一种或多种情况。

  1. 字符串是 google.com(无效,因为没有方案,即使浏览器默认假定为 http)。Urlparse 将会缺少方案和 netloc。因此,对于此情况,all([result.scheme, result.netloc, result.path]) 似乎有效。
  2. 字符串是http://google(无效,因为缺少 .com)。Urlparse 将仅缺少路径。同样,all([result.scheme, result.netloc, result.path]) 似乎适用于此情况。
  3. 字符串是http://google.com/(正确)。Urlparse 将填充方案、netloc 和路径。因此,对于此情况,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, "/"))),在第 2 种情况中仍将产生误报。

也许可以使用更复杂的方法:

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是否正确。还有更多情况需要考虑。请参阅https://en.wikipedia.org/wiki/URL以思考更多情况。


2
urljoin和urlparse最终会调用urlsplit,如果它认为netloc中有括号(IPv6),则可能会引发ValueError,因此也需要进行异常处理。 - digenishjkl

13
你可以检查url是否有方案:
>>> url = "no.scheme.com/math/12345.png"
>>> parsed_url = urlparse.urlparse(url)
>>> bool(parsed_url.scheme)
False

如果是这种情况,您可以替换方案并获得一个真正有效的URL:

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

+1 对于使用替换元组的技巧我觉得非常优雅(而且我之前不知道)。唯一的问题是返回的 URL 在方案后包含三个斜杠,因为没有方案的 URL 被解释为 path 而不是 netloc。对我来说,一个简单的 .replace('///', '//') 就可以解决这个问题。 - taffit
你错过了 import urlparse - alexey_efimov
@alexey_efimov,问题已经说了“我正在使用argparse包”。 - xbello
否则,您可以简单地使用 import urllib; urllib.parse.urlparse(url, scheme='http') 来获得相同的结果。 - vicke4

5
您可以尝试下面的函数,它会检查解析URL后得到的`scheme`、`netloc`和`path`变量。支持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

在一个有效的URL上失败。>>> url_validator("file:///some_file.txt") False - dgrogan
进行了一些小的更改,你可以再试一次 - abdullahselek

1
没有模式的URL实际上是无效的,你的浏览器只是足够聪明以建议使用http://作为模式。检查URL是否没有模式(not re.match(r'^[a-zA-Z]+://', url)),并在其前面添加http://可能是一个好的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接