Python urljoin 不删除多余的点
Python urljoin not removing superflous dots
我正在使用 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 提供给网络浏览器,它会很好地纠正它,但如果我尝试使用 Python 的 urlopen() 它会抛出异常(urllib2.HTTPError:HTTP 错误 400:错误请求)。
这是预期的行为吗?是否有其他一些 Python 函数可以正确删除我应该使用的这些点,或者这是一个错误?
我认为你应该使用绝对 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 中规范化 url 的方法。示例:
urljoin('http://www.example.com/foo/bar/../../baz/bux/', '.')
# result: 'http://www.example.com/baz/bux/'
我觉得无效的url处理(太多..
)只能处理"manually",像这样:
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)
这将从 url 中删除所有 ..
,甚至是无效的。示例:
"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
这是一个小错误!根据 RFC 3986,那些多余的 ..
片段应该被删除。没有 Python 我能找到的标准库函数确实能正确完成工作。
遗憾的是,现有答案在几个方面略有不正确。 URL 分辨率比人们想象的要复杂一些。
除了那里提到的urljoin
的缺点(加入绝对路径时不解析URLs,不处理过多的..
s) , 加入 URL 和 .
将删除最后一段。例如,urljoin('http://example.com/dir/./wrong/../file.txt', '.')
将导致 'http://example.com/dir/'
,删除文件,因此您必须将其重新添加。另外,urljoin('http://example.com/dir/..', '.')
导致 'http://example.com/dir/'
,这很简单你的情况不对。
不仅如此,提供的remove_extra_dots
函数居然还有bug。如果 URL 以尾随点段结尾(上一段中的最后一个错误使它不可能出现,但如果以某种方式修复),则尾随斜线 不会 添加。考虑 remove_extra_dots('http://example.com/..')
。这应该会导致 'http://example.com/'
,但实际上会导致 'http://example.com'
(请注意缺少的斜线)。这是一个很小的差异,但是很多网站都会在接收到缺失的斜杠时进行重定向,因此您可能会得到意想不到的结果。
以下函数完全解析 URLs - 即 .
s 和 ..
s - 遵循 RFC 3986。无需依赖 urljoin
,太!
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)
您可以在完成 URL 后调用它(在您的情况下,加入后),如下所示。
>>> resolve_url("http://example.com/dir/../../thing/.")
'http://example.com/thing/'
有关works/doesn无效内容的更多信息,请参阅a similar answer I wrote on the subject。
我正在使用 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 提供给网络浏览器,它会很好地纠正它,但如果我尝试使用 Python 的 urlopen() 它会抛出异常(urllib2.HTTPError:HTTP 错误 400:错误请求)。
这是预期的行为吗?是否有其他一些 Python 函数可以正确删除我应该使用的这些点,或者这是一个错误?
我认为你应该使用绝对 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 中规范化 url 的方法。示例:
urljoin('http://www.example.com/foo/bar/../../baz/bux/', '.')
# result: 'http://www.example.com/baz/bux/'
我觉得无效的url处理(太多..
)只能处理"manually",像这样:
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)
这将从 url 中删除所有 ..
,甚至是无效的。示例:
"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
这是一个小错误!根据 RFC 3986,那些多余的 ..
片段应该被删除。没有 Python 我能找到的标准库函数确实能正确完成工作。
遗憾的是,现有答案在几个方面略有不正确。 URL 分辨率比人们想象的要复杂一些。
除了那里提到的urljoin
的缺点(加入绝对路径时不解析URLs,不处理过多的..
s) , 加入 URL 和 .
将删除最后一段。例如,urljoin('http://example.com/dir/./wrong/../file.txt', '.')
将导致 'http://example.com/dir/'
,删除文件,因此您必须将其重新添加。另外,urljoin('http://example.com/dir/..', '.')
导致 'http://example.com/dir/'
,这很简单你的情况不对。
不仅如此,提供的remove_extra_dots
函数居然还有bug。如果 URL 以尾随点段结尾(上一段中的最后一个错误使它不可能出现,但如果以某种方式修复),则尾随斜线 不会 添加。考虑 remove_extra_dots('http://example.com/..')
。这应该会导致 'http://example.com/'
,但实际上会导致 'http://example.com'
(请注意缺少的斜线)。这是一个很小的差异,但是很多网站都会在接收到缺失的斜杠时进行重定向,因此您可能会得到意想不到的结果。
以下函数完全解析 URLs - 即 .
s 和 ..
s - 遵循 RFC 3986。无需依赖 urljoin
,太!
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)
您可以在完成 URL 后调用它(在您的情况下,加入后),如下所示。
>>> resolve_url("http://example.com/dir/../../thing/.")
'http://example.com/thing/'
有关works/doesn无效内容的更多信息,请参阅a similar answer I wrote on the subject。