python url unquote с последующим декодированием unicode

у меня есть строка unicode, как '%C3%A7%C3%B6asd+fjkls%25asd' и я хочу декодировать эту строку.
Я использовал urllib.unquote_plus(str) но оно работает неправильно.

  • ожидается : çöasd+fjkls%asd
  • результат : çöasd fjkls%asd

двойной код utf-8 символов (%C3%A7 и %C3%B6) декодируются неправильно.
Моя версия python-2.7 под дистрибутивом linux. Каков наилучший способ получить ожидаемый результат?

6 ответов


у вас есть 3 или 4 или 5 задач ... но!--6--> и unicodedata.name() ваши друзья; они однозначно показывают вам именно то, что у вас есть, без путаницы, порожденной людьми с различными консольными кодировками, сообщающими результаты print fubar.

Summary: либо (a) вы начинаете с объекта unicode и применяете к нему функцию unquote, либо (b) вы начинаете с объекта str, и ваша консольная кодировка не является UTF-8.

если, как вы говорите, вы начинаете с объектом unicode:

>>> s0 = u'%C3%A7%C3%B6asd+fjkls%25asd'
>>> print repr(s0)
u'%C3%A7%C3%B6asd+fjkls%25asd'

это случайная бессмыслица. Если вы применяете urllibX.unquote_YYYY() к нему вы получаете еще один бессмысленный объект unicode (u'\xc3\xa7\xc3\xb6asd+fjkls%asd') что вызвало бы ваши показанные симптомы при печати. Вы должны немедленно преобразовать исходный объект unicode в объект str:

>>> s1 = s0.encode('ascii')
>>> print repr(s1)
'%C3%A7%C3%B6asd+fjkls%25asd'

тогда вы должны отменить кавычки:

>>> import urllib2
>>> s2 = urllib2.unquote(s1)
>>> print repr(s2)
'\xc3\xa7\xc3\xb6asd+fjkls%asd'

глядя на первые 4 байта этого, он закодирован в UTF-8. Если вы это сделаете print s2, это будет выглядеть нормально, если ваша консоль ожидая UTF-8, но если он ожидает ISO-8859-1 (он же latin1), вы увидите свой симптоматический мусор (первый символ будет a-tilde). Давайте припаркуем эту мысль на мгновение и преобразуем ее в объект Unicode:

>>> s3 = s2.decode('utf8')
>>> print repr(s3)
u'\xe7\xf6asd+fjkls%asd'

и осмотрите его, чтобы увидеть, что у нас есть:

>>> import unicodedata
>>> for c in s3[:6]:
...     print repr(c), unicodedata.name(c)
...
u'\xe7' LATIN SMALL LETTER C WITH CEDILLA
u'\xf6' LATIN SMALL LETTER O WITH DIAERESIS
u'a' LATIN SMALL LETTER A
u's' LATIN SMALL LETTER S
u'd' LATIN SMALL LETTER D
u'+' PLUS SIGN

похоже, что вы сказали, что ожидали. Теперь мы подошли к вопросу отображения на консоли. Примечание: не волнуйтесь, когда вы видите "cp850"; я делаю это переносимо и просто так получилось это делается в командной строке в Windows.

>>> import sys
>>> sys.stdout.encoding
'cp850'
>>> print s3
çöasd+fjkls%asd

Примечание: объект unicode был явно закодирован с помощью sys.стандартный вывод.кодирование. К счастью, все символы Unicode в s3 представимы в этой кодировке (и cp1252 и latin1).


используя unquote или unquote_plus даст вам строку байта. Если вам нужна строка Unicode, вам нужно декодировать строку байтов в unicode:

>>> print(urllib.unquote_plus('%C3%A7%C3%B6asd+fjkls%25asd').decode('utf8'))
çöasd fjkls%asd
>>> 

по сравнению с:

>>> print(urllib.unquote_plus('%C3%A7%C3%B6asd+fjkls%25asd'))
çöasd fjkls%asd
>>> 

обратите внимание, что входная строка должна быть строкой байта: если вы передаете unicode в unquote/unquote_plus тогда вы получите немного беспорядок. Если это так, то сначала Закодируйте его:

>>> print(urllib.unquote_plus(u'%C3%A7%C3%B6asd+fjkls%25asd'.encode('ascii')).decode('utf8'))
çöasd fjkls%asd

попробовать urllib2 еще раз:

print urllib2.unquote('%C3%A7%C3%B6asd+fjkls%25asd')

'%C3%A7%C3%B6asd+fjkls%25asd' - это не строка Юникода.

это строка в кодировке url. Использовать urllib2.вместо этого unquote ().


у вас есть двойная проблема: ваша строка кодируется в юникоде и содержит urlencoded caracter. Некоторые матчи. Вы можете нормализовать строку в ascci, чтобы убедиться, что она не будет интерпретирована неправильно:

>>> s = '%C3%A7%C3%B6asd+fjkls%25asd' # ascii string
>>> print urllib2.unquote(s) # works as expected
çöasd+fjkls%asd
>>> s = u'%C3%A7%C3%B6asd+fjkls%25asd' # unicode string
>>> print urllib2.unquote(s) # decode stuff that it shouldn't
çöasd+fjkls%asd
>>> print urllib2.unquote(s.encode('ascii')) # encode the unicode string to ascii: works!
çöasd+fjkls%asd

вы используете unquote_plus метод, который принимает space учитывать и преобразовывать в +. Просто используйте unquote метод, и вы должны быть в порядке.

>>> import urllib
>>> print urllib.unquote('%C3%A7%C3%B6asd+fjkls%25asd')
çöasd+fjkls%asd
>>> print urllib.unquote_plus('%C3%A7%C3%B6asd+fjkls%25asd')
çöasd fjkls%asd