urllib.quote() 抛出 KeyError

42 投票
3 回答
21342 浏览
提问于 2025-04-17 17:19

为了对URI进行编码,我使用了 urllib.quote("schönefeld"),但是当字符串中有一些非ASCII字符时,它会抛出错误。

KeyError: u'\xe9'
Code: return ''.join(map(quoter, s))

我的输入字符串有 köln, brønshøj, schönefeld 等等。

我在Windows上尝试打印语句(使用python2.7和pyscripter IDE),但在Linux上却出现了异常(我觉得平台无关紧要)。

这是我正在尝试的:

from commands import getstatusoutput
queryParams = "schönefeld";
cmdString = "http://baseurl" + quote(queryParams)
print getstatusoutput(cmdString)

探讨问题的原因:urllib.quote() 中,实际上异常是在 return ''.join(map(quoter, s)) 这一行抛出的。

urllib中的代码是:

def quote(s, safe='/'):
    if not s:
        if s is None:
            raise TypeError('None object cannot be quoted')
        return s
     cachekey = (safe, always_safe)
     try:
         (quoter, safe) = _safe_quoters[cachekey]
     except KeyError:
         safe_map = _safe_map.copy()
         safe_map.update([(c, c) for c in safe])
         quoter = safe_map.__getitem__
         safe = always_safe + safe
         _safe_quoters[cachekey] = (quoter, safe)
      if not s.rstrip(safe):
         return s
      return ''.join(map(quoter, s))

异常的原因在于 ''.join(map(quoter, s)),对于s中的每个元素,都会调用quoter函数,最后将列表用''连接起来并返回。

对于非ASCII字符 è,它的对应键是 %E8,这个键在 _safe_map 变量中存在。但是当我调用 quote('è') 时,它会查找键 \xe8。所以这个键不存在,因此抛出了异常。

所以,我在调用 ''.join(map(quoter, s)) 之前,修改了 s = [el.upper().replace("\\X","%") for el in s],并放在了try-except块中。现在它运行得很好。

但我在想,我这样做是否是正确的方法,或者会不会引发其他问题?而且我有200多个Linux实例,要在所有实例中部署这个修复非常困难。

3 个回答

1

我遇到的错误和@underscore的一模一样,但我的情况是,map(quoter,s)在找一个叫u'\xe9'的键,而这个键在<_safe_map>里找不到。不过\xe9是存在的,所以我通过把u'\xe9'替换成\xe9来解决了这个问题。

另外,return语句不应该放在try/except里面吗?我也需要改这个,才能完全解决问题。

2

我只是把字符串转换成了unicode格式,这样就解决了问题。

下面是代码片段:

try:
    unicode(mystring, "ascii")
except UnicodeError:
    mystring = unicode(mystring, "utf-8")
else:
    pass

关于解决方案的详细描述可以在这里找到:http://effbot.org/pyfaq/what-does-unicodeerror-ascii-decoding-encoding-error-ordinal-not-in-range-128-mean.htm

66

你想要把Unicode数据转成可以在网址中安全使用的格式,所以你需要想办法把它变成安全的字节。

首先,把这个字符串编码成字节。通常我们会用UTF-8这种编码方式:

>>> import urllib
>>> urllib.quote(u'sch\xe9nefeld')
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py:1268: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  return ''.join(map(quoter, s))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 1268, in quote
    return ''.join(map(quoter, s))
KeyError: u'\xe9'
>>> urllib.quote(u'sch\xe9nefeld'.encode('utf8'))
'sch%C3%A9nefeld'

不过,编码方式其实要看服务器能接受什么。最好是用发送原始数据时的编码方式。

撰写回答