我使用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函数可以正确地删除我应该使用的这些点,或者这是一个错误?
答案 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。