如何在python中规范化URL


73

我想知道我是否在python中标准化URL。

例如,如果我有一个网址字符串,例如:“ http://www.example.com/foo goo / bar.html”

我需要python中的库,该库会将多余的空间(或任何其他非标准化字符)转换为正确的URL。


还有一个更先进的最新答案在计算器上的位置:stackoverflow.com/questions/10584861/...
stuckintheshuck

2
我认为这不是更好...
亚伦·霍尔

Answers:


69

看一下这个模块:werkzeug.utils。(现在在werkzeug.urls

您要查找的函数称为“ url_fix”,其工作方式如下:

>>> from werkzeug.urls import url_fix
>>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'

它在Werkzeug中的实现如下:

import urllib
import urlparse

def url_fix(s, charset='utf-8'):
    """Sometimes you get an URL by a user that just isn't a real
    URL because it contains unsafe characters like ' ' and so on.  This
    function can fix some of the problems in a similar way browsers
    handle data entered by the user:

    >>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)')
    'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'

    :param charset: The target charset for the URL if the url was
                    given as unicode string.
    """
    if isinstance(s, unicode):
        s = s.encode(charset, 'ignore')
    scheme, netloc, path, qs, anchor = urlparse.urlsplit(s)
    path = urllib.quote(path, '/%')
    qs = urllib.quote_plus(qs, ':&=')
    return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))

尽管这是来自http rfc2616的更准确的解决方案,但我认为这太过分了,还是我错过了一些事情?
FlorianBösch08年

1
是。您可能错过了这个问题。他有一个来自用户输入的URL,并希望将其正确转换为真实URL。(又称:执行Firefox位置栏的作用)
Armin Ronacher,

3
url_fix现在位于werkzeug.urls
sebpiq 2012年

@ArminRonacher此功能很好,但不幸的是,它不执行基于语法的完整标准化,也就是说,不进行大小写标准化+百分比编码标准化+路径段标准化,也不执行RFC 3986中定义的基于方案的标准化。 Python库(标准与否)可以做到吗?我不敢相信Python没有这样的基本标准功能。
Maggyero '19

58

该问题在Python 2.7中的实际修复

正确的解决方案是:

 # percent encode url, fixing lame server errors for e.g, like space
 # within url paths.
 fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]")

有关更多信息,请参见Issue918368:“ urllib无法更正服务器返回的URL”


4
出色的答案,简洁而有用。由于此更改位于urllib内部,因此希望执行相同操作的代码应使用上面的参数进行import urllib调用urllib.quote()
奎因·泰勒

这把字母ä否决了,但我投了赞成票,因为它很简单,并且不再需要其他输入。
mlissner

24

采用 urllib.quoteurllib.quote_plus

urllib文档中

quote(string [,safe])

使用“%xx”转义符替换字符串中的特殊字符。字母,数字和字符“ _.-”都不会被引用。可选的safe参数指定不应加引号的其他字符-其默认值为'/'。

示例:quote('/~connolly/')yields '/%7econnolly/'

quote_plus(字符串[,安全])

像quote()一样,但是也用加号代替空格,这是引用HTML表单值所必需的。除非原始字符串中包含加号,否则它们将被转义。它也没有安全的默认值'/'。

编辑:在整个URL上使用urllib.quote或urllib.quote_plus会使其混乱,如@@ΤZΩΤZΙΟΥ指出:

>>> quoted_url = urllib.quote('http://www.example.com/foo goo/bar.html')
>>> quoted_url
'http%3A//www.example.com/foo%20goo/bar.html'
>>> urllib2.urlopen(quoted_url)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\python25\lib\urllib2.py", line 124, in urlopen
    return _opener.open(url, data)
  File "c:\python25\lib\urllib2.py", line 373, in open
    protocol = req.get_type()
  File "c:\python25\lib\urllib2.py", line 244, in get_type
    raise ValueError, "unknown url type: %s" % self.__original
ValueError: unknown url type: http%3A//www.example.com/foo%20goo/bar.html

@ΤζΩΤΙΙΙΟΥ提供了使用urlparse.urlparse和urlparse.urlunparse解析URL并仅对路径进行编码的功能。这可能对您更有用,尽管如果您是从已知的协议和主机构建URL但带有可疑路径的,则可能也可以避免urlparse并引用URL的可疑部分,并与已知的安全零件。


2
那么,给定问题的示例url,urllib.quote返回什么?
tzot

1
垃圾。为什么一个明显错误的答案被接受为解决方案?
Armin Ronacher

@ΤζΩΤžΙΟΥ:很好。在@Armin Ronacher上致辞:可能是因为应答者和接受者都不知道问题所在-并非所有问题对所有人都是显而易见的。
布莱尔·康拉德

建议的编辑:“…并且仅编码主机名”→“…并且仅引用路径”
tzot

当然,@ΤZnΩΤZΙΟΥ。谢谢!有时我不知道我离开大脑的位置。
布莱尔·康拉德

13

因为此页面是Google在该主题上搜索的最佳结果,所以我认为值得一提的是使用Python对URL规范化所做的一些工作,这些工作超出了对数字字符的黑字编码。例如,处理默认端口,字符大小写,缺少尾部斜杠等。

在开发Atom联合格式时,有人在讨论如何将URL规范化为规范格式。这在文章PaceCanonicalIds中有记录在Atom / Pie Wiki上的进行了记录。那篇文章提供了一些很好的测试案例。

我相信,这次讨论的结果是Mark Nottingham的urlnorm.py库,我在几个项目中都用到了很好的结果。但是,该脚本不适用于此问题中给出的URL。因此,一个更好的选择可能是Sam Ruby的urlnorm.py版本,该版本可以处理该URL以及Atom Wiki中的所有上述测试用例。


10

y3

from urllib.parse import urlparse, urlunparse, quote
def myquote(url):
    parts = urlparse(url)
    return urlunparse(parts._replace(path=quote(parts.path)))

>>> myquote('https://www.example.com/~user/with space/index.html?a=1&b=2')
'https://www.example.com/~user/with%20space/index.html?a=1&b=2'

y2

import urlparse, urllib
def myquote(url):
    parts = urlparse.urlparse(url)
    return urlparse.urlunparse(parts[:2] + (urllib.quote(parts[2]),) + parts[3:])

>>> myquote('https://www.example.com/~user/with space/index.html?a=1&b=2')
'https://www.example.com/%7Euser/with%20space/index.html?a=1&b=2'

这仅引用路径组件。


2
那只是引用所有字符。那对他没有帮助。
Armin Ronacher

在此示例中,它还会引用':'字符(并非全部)。感谢您的评论。
tzot



1

我遇到这样的问题:只需要引用空格。

fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]") 确实有帮助,但这太复杂了。

因此,我使用了一种简单的方法:url = url.replace(' ', '%20'),这不是完美的方法,但这是最简单的方法,适用于这种情况。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.