web-dev-qa-db-fra.com

urllib.quote () lève KeyError

Pour encoder l'URI, j'ai utilisé urllib.quote("schönefeld") mais lorsque certains caractères non ascii existent dans la chaîne, il thorws

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

Mes chaînes d'entrée sont köln, brønshøj, schönefeld Etc.

Quand j'ai essayé d'imprimer des instructions dans Windows (en utilisant python2.7, IDE de psy). Mais sous Linux, cela soulève une exception (je suppose que la plate-forme n'a pas d'importance).

Voici ce que j'essaie:

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

Exploration de la raison du problème: dans urllib.quote(), en fait l'exception étant lancée sur return ''.join(map(quoter, s)).

Le code dans urllib est:

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

La raison de l'exception est dans ''.join(map(quoter, s)), pour chaque élément de s, la fonction quoter sera appelée et enfin la liste sera jointe par '' et retournée.

Pour les caractères non-ascii è, La clé équivalente sera %E8 Qui se présente dans la variable _safe_map. Mais lorsque j'appelle quote ('è'), il recherche la clé \xe8. Pour que la clé n'existe pas et exception levée.

Donc, je viens de modifier s = [el.upper().replace("\\X","%") for el in s] avant d'appeler ''.join(map(quoter, s)) dans le bloc try-except. Maintenant ça marche bien.

Mais je suis ennuyeux ce que j'ai fait est une approche correcte ou cela va créer un autre problème? Et j'ai également plus de 200 instances de Linux, ce qui est très difficile à déployer ce correctif dans toutes les instances.

40
Garfield

Vous essayez de citer des données Unicode, vous devez donc décider comment les transformer en octets sûrs pour les URL.

Encodez d'abord la chaîne en octets. UTF-8 est souvent utilisé:

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

Cependant, l'encodage dépend de ce que le serveur acceptera. Il est préférable de s'en tenir à l'encodage avec lequel le formulaire d'origine a été envoyé.

62
Martijn Pieters

En convertissant simplement la chaîne en unicode, j'ai résolu le problème.

voici l'extrait:

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

Une description détaillée de la solution peut être trouvée sur http://effbot.org/pyfaq/what-does-unicodeerror-ascii-decoding-encoding-error-ordinal-not-in-range-128-mean.htm

2
Garfield

J'ai eu exactement la même erreur que @underscore mais dans mon cas, le problème était que la carte (guillemet (s)) a essayé de rechercher la clé u'\xe9' qui n'était pas dans le _safe_map. Cependant \xe9 l'était, j'ai donc résolu le problème en remplaçant u'\xe9' par \xe9 dans s.

De plus, l'instruction return ne doit-elle pas se trouver dans le try/except? J'ai également dû changer cela pour résoudre complètement le problème.

1
Sebastien