Python urljoin不删除超级圆点

时间:2015-01-14 18:57:16

标签: python url

我使用urljoin获取页面链接的绝对URL。在大多数情况下,它在解决相关链接等方面做得很好,但我注意到在某些情况下它不会删除超级圆点。例如:

>>> urljoin("http://x.com","http://x.com/../../X",False)
'http://x.com/../../X'
>>> urljoin("http://x.com","http://x.com/./../X",False)
'http://x.com/./../X'

如果我将这样的URL提供给Web浏览器,它可以很好地纠正它,但是如果我尝试使用Python' urlopen()它会抛出异常(urllib2.HTTPError:HTTP Error 400:Bad Request)。

这是预期的行为吗?是否有其他一些Python函数可以正确地删除我应该使用的这些点,或者这是一个错误?

2 个答案:

答案 0 :(得分:2)

我认为你应该使用绝对base和相对url 如果你这样称呼它,它会删除点:

urljoin("http://x.com/a/b/page.html","../../index.html",False)
# result: 'http://x.com/index.html'

urljoin("http://x.com/a/b/page.html","./index.html",False)
# result: 'http://x.com/a/b/index.html'

我找到了一种在this answer中规范化网址的方法。例如:

urljoin('http://www.example.com/foo/bar/../../baz/bux/', '.')
# result: 'http://www.example.com/baz/bux/'

我认为无效的网址处理(太多..)只能“手动”处理,如下所示:

def remove_extra_dots(url):
    parsed = list(urlparse(url))
    dirs = []
    for name in parsed[2].split("/"):
        if name == "..":
            if len(dirs) > 1:
                dirs.pop()
        else:
            dirs.append(name)
    parsed[2] = "/".join(dirs)
    return urlunparse(parsed)

这将从网址中删除所有..,即使是无效的网址也是如此。例子:

"http://x.com/a/b/c/../../X"  #->  http://x.com/a/X
"http://x.com/a/b/../../X"    #->  http://x.com/X
"http://x.com/../../X"        #->  http://x.com/X

答案 1 :(得分:1)

这有点像虫子!根据{{​​3}},应删除那些多余的..段。没有Python标准库函数我能够找到真正正确的工作。

遗憾的是,现有的答案在某些方面略有不正确。 URL解析比人们预期的要复杂得多。

除了那里提到的urljoin的缺点(它在加入绝对路径时不解析URL,并且它不处理过多的..),使用{{加入URL 1}}将删除最后一个段。例如,.将导致urljoin('http://example.com/dir/./wrong/../file.txt', '.'),删除文件,因此您必须将其重新添加。此外,'http://example.com/dir/'会生成urljoin('http://example.com/dir/..', '.'),这只是简单的你的情况不对。

不仅如此,提供的'http://example.com/dir/'函数实际上还有一个错误。如果URL以尾随点段结尾(前一段中的最后一个bug无法呈现,但如果以某种方式修复),则不会添加尾部斜杠 。考虑remove_extra_dots。这应该会产生remove_extra_dots('http://example.com/..'),但实际上会产生'http://example.com/'(请注意缺少的斜杠)。这是一个小差异,但很多网站在接收丢失的斜杠时会重定向,因此您可能会得到意想不到的结果。

以下功能完全解析了RFC 3986之后的网址 - 即'http://example.com'. s。无需依赖..

urljoin

然后,当您拥有完整的URL(在您的情况下,加入后)后,您可以调用它,如下所示。

try:
    # Python 3
    from urllib.parse import urlsplit, urlunsplit
except ImportError:
    # Python 2
    from urlparse import urlsplit, urlunsplit

def resolve_url(url):
    parts = list(urlsplit(url))
    segments = parts[2].split('/')
    segments = [segment + '/' for segment in segments[:-1]] + [segments[-1]]
    resolved = []
    for segment in segments:
        if segment in ('../', '..'):
            if resolved[1:]:
                resolved.pop()
        elif segment not in ('./', '.'):
            resolved.append(segment)
    parts[2] = ''.join(resolved)
    return urlunsplit(parts)

有关哪些有效/无效的详细信息,请参阅RFC 3986