规范化/规范化URL?

规范化/规范化URL?,url,python-3.x,normalization,Url,Python 3.x,Normalization,我正在搜索一个库函数来规范Python中的URL,即删除路径中的“/”或“./”部分,或添加默认端口或转义特殊字符等。结果应该是一个字符串,该字符串对于指向同一网页的两个URL是唯一的。例如http://google.com和http://google.com:80/a/../应返回相同的结果 我更喜欢Python3,并且已经浏览了urllib模块。它提供了拆分URL的功能,但没有提供规范化URL的功能。java有 URI .RealMeSeq()/Case>函数,它执行类似的操作(虽然它不考虑

我正在搜索一个库函数来规范Python中的URL,即删除路径中的“/”或“./”部分,或添加默认端口或转义特殊字符等。结果应该是一个字符串,该字符串对于指向同一网页的两个URL是唯一的。例如
http://google.com
http://google.com:80/a/../
应返回相同的结果

我更喜欢Python3,并且已经浏览了
urllib
模块。它提供了拆分URL的功能,但没有提供规范化URL的功能。java有<代码> URI .RealMeSeq()/Case>函数,它执行类似的操作(虽然它不考虑默认端口80等于给定端口),但有类似这样的东西吗?Python?< /P> < P>
In [1]: from urllib.parse import urljoin

In [2]: urljoin('http://example.com/a/b/c/../', '.')
Out[2]: 'http://example.com/a/b/'
灵感来源于对问题的回答。它不会规范化端口,但启动一个函数应该很简单。

在之后,我编写了一个方法,适用于web中常见的大多数情况

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

这就是我所用的,到目前为止一直有效。你可以从pip那里得到URLMORM

请注意,我对查询参数进行了排序。我发现这很重要

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, ''))
旧(不赞成的)答案 [不再维护]模块规范化多个斜杠、
组件,而不会弄乱
http://
中的双斜杠

一旦您执行了
pip安装urltools
(由于作者重命名了repo,这不再有效),用法如下:

print urltools.normalize('http://example.com:80/a////b/../c')
>>> 'http://example.com/a/c'
虽然模块不再是pip可安装的,但它是可重复使用的

Python3的最新答案 Python 3考虑从模块中使用。

从urllib.parse导入urljoin
urljoin('https://stackoverflow.com/questions/10584861/“,”../dinsdale')
#Out[17]:'https://stackoverflow.com/questions/dinsdale'

现在有一个专门针对这个问题的库

它不仅仅是按照文档规范化路径:

URI规范化函数:

  • 注意IDN域
  • 始终以小写字符提供URI方案
  • 始终以小写字符提供主机(如果有)
  • 仅在必要时执行百分比编码
  • 百分比编码时始终使用大写A-to-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/
    

    我在上面使用了@Antony的答案并使用了库,但它有一个目前尚未修复的错误:当发送URL时没有方案,is会意外地将其设置为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
    

    我没有
    urllib.parse
    ,但我有
    urlprase
    urllib.parse
    是Python 3的位置-最初的问题是关于Py 3的。这在任何不以“/”结尾的地方都会非常失败,删除无效的父目录您需要替换
    split[2]。split(“”)[0]
    带有
    urllib.parse.quote(split[2])
    ——在某些情况下,URL中有空格是完全正常的,事实上是必需的。另外,urlnorm是py2konly,因此,在一些不寻常的情况下,您将丢弃片段,它实际上可能是必需的URL组件。是的,有非零数量的网页,
    blah.com/#wat
    blah.com/
    是完全不同的页面。它通常是用javascript完成的,是一个巨大的PITA,但它是存在的。@FakeName写道:“URL中有空格是完全正常的,事实上是必需的。”。不,那是绝对错误的。URL中不允许使用空格。阅读规范:一些浏览器错误地显示空格,但事实上它们是百分比编码的http://google.com/与
    http://google.com:80/a/../
    。也就是说,如果
    /a
    不存在,则第二条路径将失败。通过“规范化”它,您将丢失这种特殊情况,并在开始使用无效URI时使用有效URI…这是不正确的,无论如何在浏览器中都不正确。即使a不存在,您也可以编写,它将转到。这是因为浏览器在发送到服务器之前进行初始解析。在服务器端,不同的服务器以不同的方式运行。https呢?很好!唯一不能正常工作的是,当不提供协议时,它返回HTTPS而不是HTTP。示例网站转到“https:////example.com“@GilCohen,https是目前推荐的做法,因此这种行为可能是故意的。例如,请参阅和。理论上您可能是正确的,但在实践中,当协议没有规定时,浏览器会先尝试HTTP。urltools似乎已经离开了地球。没有github,没有pypi,任何地方都没有缓存副本。如果有人知道发生了什么,请告诉我。@GaneshKathiresan:看来作者决定重新命名回购协议;更新