Python 2.6.5中有没有unicode友好的urllib.quote和urllib.unquote替代方案?

42 投票
4 回答
23339 浏览
提问于 2025-04-16 15:09

在Python 2.6.5中,urllib.quoteurllib.unquote这两个函数处理Unicode字符时出现了问题。具体情况是这样的:

In [5]: print urllib.unquote(urllib.quote(u'Cataño'))
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)

/home/kkinder/<ipython console> in <module>()

/usr/lib/python2.6/urllib.pyc in quote(s, safe)
   1222             safe_map[c] = (c in safe) and c or ('%%%02X' % i)
   1223         _safemaps[cachekey] = safe_map
-> 1224     res = map(safe_map.__getitem__, s)
   1225     return ''.join(res)
   1226 

KeyError: u'\xc3'

把值编码成UTF8格式也不奏效:

In [6]: print urllib.unquote(urllib.quote(u'Cataño'.encode('utf8')))
Cataño

这个问题被认为是一个bug,并且已经有了修复方案,但我的Python版本并没有这个修复。

我希望能找到类似于urllib.quote和urllib.unquote的功能,但能正确处理Unicode变量,这样这段代码就能正常工作:

decode_url(encode_url(u'Cataño')) == u'Cataño'

有没有什么推荐的解决办法?

4 个回答

2

我遇到了同样的问题,所以我用了一个辅助函数来处理非ASCII字符,还有一个叫做urllib.urlencode的函数(这个函数包含了quote和unquote的功能):

def utf8_urlencode(params):
    import urllib as u
    # problem: u.urlencode(params.items()) is not unicode-safe. Must encode all params strings as utf8 first.
    # UTF-8 encodes all the keys and values in params dictionary
    for k,v in params.items():
        # TRY urllib.unquote_plus(artist.encode('utf-8')).decode('utf-8')
        if type(v) in (int, long, float):
            params[k] = v
        else:
            try:
                params[k.encode('utf-8')] = v.encode('utf-8')
            except Exception as e:
                logging.warning( '**ERROR utf8_urlencode ERROR** %s' % e )
    return u.urlencode(params.items()).decode('utf-8')

这个内容是从 使用Python进行Unicode URL编码/解码 这篇文章里借鉴过来的。

5

“把值编码成UTF8也不行”...你代码的结果是一个str对象,猜测起来看起来像是输入的内容用UTF-8编码过。你需要解码一下,或者说明一下“没用”是什么意思——你到底期待什么呢?

注意:为了避免我们猜测你终端的编码方式和数据类型,建议用print repr(whatever)来代替print whatever

>>> # Python 2.6.6
... from urllib import quote, unquote
>>> s = u"Cata\xf1o"
>>> q = quote(s.encode('utf8'))
>>> u = unquote(q).decode('utf8')
>>> for x in (s, q, u):
...     print repr(x)
...
u'Cata\xf1o'
'Cata%C3%B1o'
u'Cata\xf1o'
>>>

做个对比:

>>> # Python 3.2
... from urllib.parse import quote, unquote
>>> s = "Cata\xf1o"
>>> q = quote(s)
>>> u = unquote(q)
>>> for x in (s, q, u):
...     print(ascii(x))
...
'Cata\xf1o'
'Cata%C3%B1o'
'Cata\xf1o'
>>>
47

Python的urllib.quote和urllib.unquote对Unicode的处理不太正确

urllib根本不处理Unicode。根据定义,URL中不应该包含非ASCII字符。当你使用urllib时,应该只用字节字符串。如果你想让这些字符串表示Unicode字符,你需要手动进行编码和解码。

国际化资源标识符(IRI)可以包含非ASCII字符,并将它们编码为UTF-8序列,但目前Python并没有一个叫irilib的库。

将值编码为UTF8也不奏效:

In [6]: print urllib.unquote(urllib.quote(u'Cataño'.encode('utf8')))
Cataño

哦,现在你是在控制台输入Unicode,并且在控制台使用print输出Unicode。这通常不太可靠,尤其是在Windows系统中,特别是你在使用IPython控制台的时候。

你可以用反斜杠序列的方式输入,这样你能更清楚地看到urllib确实是可以工作的:

>>> u'Cata\u00F1o'.encode('utf-8')
'Cata\xC3\xB1o'
>>> urllib.quote(_)
'Cata%C3%B1o'

>>> urllib.unquote(_)
'Cata\xC3\xB1o'
>>> _.decode('utf-8')
u'Cata\xF1o'

撰写回答