规范化/规范化 URL?
Posted
技术标签:
【中文标题】规范化/规范化 URL?【英文标题】:Canonicalize / normalize a URL? 【发布时间】:2012-05-22 00:39:59 【问题描述】:我正在寻找一个库函数来规范化 Python 中的 URL,即删除路径中的“./”或“../”部分,或者添加默认端口或转义特殊字符等。结果应该是指向同一网页的两个 URL 唯一的字符串。例如http://google.com
和http://google.com:80/a/../
应该返回相同的结果。
我更喜欢 Python 3,并且已经浏览过 urllib
模块。它提供了拆分 URL 的功能,但没有将它们规范化。 Java 有 URI.normalize()
函数做类似的事情(虽然它不认为默认端口 80 等于没有给定端口),但是有没有类似的东西是 python?
【问题讨论】:
附带说明,http://google.com/
等资源与 http://google.com:80/a/../
不同。也就是说,如果/a
不存在,那么第二条路径将失败。通过“规范化”它,你会失去那个特殊情况,当你从一个无效的 URI 开始时,你会得到一个有效的 URI...
这是不正确的,无论如何在浏览器中都不正确。即使a不存在,你也可以写google.com:80/a/..,它会转到google.com:80。这是因为浏览器在发送到服务器之前会进行初始解析。在服务器端,不同的服务器有不同的行为方式。
【参考方案1】:
这个怎么样:
In [1]: from urllib.parse import urljoin
In [2]: urljoin('http://example.com/a/b/c/../', '.')
Out[2]: 'http://example.com/a/b/'
灵感来自对this question 的回答。它不会对端口进行规范化,但创建一个可以做到这一点的函数应该很简单。
【讨论】:
我没有urllib.parse
,但我有urlparse
。
urllib.parse
是 Python 3 的位置 - 最初的问题是关于 Py 3。
这在任何不以'/'结尾的东西上都非常失败【参考方案2】:
这是我使用的,到目前为止它已经奏效了。你可以从 pip 获取 urlnorm。
请注意,我对查询参数进行了排序。我发现这是必不可少的。
from urlparse import urlsplit, urlunsplit, parse_qsl
from urllib import urlencode
import urlnorm
def canonizeurl(url):
split = urlsplit(urlnorm.norm(url))
path = split[2].split(' ')[0]
while path.startswith('/..'):
path = path[3:]
while path.endswith('%20'):
path = path[:-3]
qs = urlencode(sorted(parse_qsl(split.query)))
return urlunsplit((split.scheme, split.netloc, path, qs, ''))
【讨论】:
不错,删除无效的父目录 您需要将split[2].split(' ')[0]
替换为urllib.parse.quote(split[2])
- 在某些情况下,URL 中有空格是完全正常的,实际上是必需的。此外,urlnorm 仅适用于 py2k
另外,在一些不寻常的情况下,您将丢弃实际上可能是必需的 URL 组件的片段。是的,blah.com/#wat
与blah.com/
完全不同的网页数量非零。它通常使用 javascript 完成,是一个巨大的 PITA,但它存在。
@FakeName 写道:“在 URL 中有空格是完全正常的,实际上是必需的”。不,那是绝对不正确的。 URL 中不允许有空格。阅读规范:tools.ietf.org/html/rfc2396 一些浏览器会误导性地显示空格,但实际上它们是百分比编码的。【参考方案3】:
旧(已弃用)答案
[不再维护]urltools 模块规范了多个斜杠,.
和 ..
组件,而不会弄乱 http://
中的双斜杠。
一旦你这样做了 (这不再起作用,因为作者重命名了repo)用法如下:pip install urltools
print urltools.normalize('http://example.com:80/a////b/../c')
>>> 'http://example.com/a/c'
虽然该模块不再可通过 pip 安装,但它是 a single file,因此您可以重复使用它的一部分。
Python3 的更新答案
对于 Python3,请考虑使用 urllib.urlparse
模块中的 urljoin
。
from urllib.parse import urljoin
urljoin('https://***.com/questions/10584861/', '../dinsdale')
# Out[17]: 'https://***.com/questions/dinsdale'
【讨论】:
urltools 似乎已经离开了这个星球。没有 github,没有 pypi,没有任何地方的缓存副本。如果有人知道发生了什么,请告诉我。 @GaneshKathiresan:看起来作者决定重命名 repo;更新【参考方案4】:现在有一个专门解决这个问题的库url-normalize
它不仅仅按照文档规范化路径:
URI归一化函数:
注意 IDN 域。 始终以小写字符提供 URI 方案。 始终以小写字符提供主机(如果有)。 仅在必要时执行百分比编码。 在进行百分比编码时,始终使用大写的 A 到 F 字符。 防止点段出现在非相对 URI 路径中。 对于定义默认权限的方案,如果需要默认权限,请使用空权限。 对于将空路径定义为等效于“/”路径的方案,请使用“/”。 对于定义端口的方案,如果需要默认端口,请使用空端口 URI 的所有部分都必须是来自 Unicode 字符串的 utf-8 编码 NFC
这是一个例子:
from url_normalize import url_normalize
url = 'http://google.com:80/a/../'
print(url_normalize(url))
这给出了:
http://google.com/
【讨论】:
不错!唯一不能正常工作的是当不提供协议时,它返回 HTTPS 而不是 HTTP。 example.com 变成“https:////example.com” @GilCohen ,如今,https 是推荐的做法,因此这种行为可能是故意的。例如,请参阅 W3C statement 和 US government policy。 理论上你可能是正确的,但实际上当协议没有说明时,浏览器会先尝试HTTP。【参考方案5】:按照good start,我编写了一个适合大多数网络常见案例的方法。
def urlnorm(base, link=''):
'''Normalizes an URL or a link relative to a base url. URLs that point to the same resource will return the same string.'''
new = urlparse(urljoin(base, url).lower())
return urlunsplit((
new.scheme,
(new.port == None) and (new.hostname + ":80") or new.netloc,
new.path,
new.query,
''))
【讨论】:
那么 https 呢?【参考方案6】:我在上面使用了@Antony 的答案并使用了url-normalize 库,但它有一个当前未修复的错误:当在没有方案的情况下发送 URL 时,不小心将其设置为 HTTPS。我编写了一个函数,通过将其设置为 HTTP 来包装和修复它:
from url_normalize import url_normalize
from urllib.parse import urlparse
def parse_url(url):
return_val = url_normalize(url)
wrong_default_prefix = "https://"
new_default_prefix = "http://"
# If the URL came with no scheme and the normalize function mistakenly
# set it to the HTTPS protocol, then fix it and set it to HTTP
if urlparse(url).scheme.strip() == '' and return_val.startswith(wrong_default_prefix):
return_val = new_default_prefix + return_val[len(wrong_default_prefix):]
return return_val
【讨论】:
该库的作者是否说这是一个错误?对我来说它看起来是故意的,因为为了安全起见,https 应该比 http 更受欢迎。 嗯,不应该,因为这不是浏览器的行为方式。以上是关于规范化/规范化 URL?的主要内容,如果未能解决你的问题,请参考以下文章