Как убрать кавычки из строки Unicode с URL-кодом в Python?

У меня есть строка Unicode, такая как «Tanım», которая каким-то образом закодирована как «Tan% u0131m». Как я могу преобразовать эту закодированную строку обратно в исходный Unicode. Судя по всему urllib.unquote не поддерживает юникод.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
52
0
70 783
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

def unquote(text):
    def unicode_unquoter(match):
        return unichr(int(match.group(1),16))
    return re.sub(r'%u([0-9a-fA-F]{4})',unicode_unquoter,text)

К сожалению, это работает только для Python 2, то есть быстро приближается к концу срока службы. Нетрудно исправить это, чтобы сделать эти Python 2 и 3 совместимыми (try: unichr, except NameError: unichr = chr), но эта версия не обрабатывает суррогатные пары. Целью escape-формата %hhhh было кодирование кодовых точек UTF-16, поэтому для последовательностей, отличных от BMP (например, большого количества эмодзи), вы получите недопустимую строку для чего-либо, кроме сборки Python 2 UCS-2.

Martijn Pieters 07.03.2019 18:25
Ответ принят как подходящий

% uXXXX - это нестандартная схема кодирования, который был отклонен w3c, несмотря на то, что реализация продолжает существовать в стране JavaScript.

Более распространенный метод, по-видимому, заключается в кодировании строки UTF-8 и последующем экранировании результирующих байтов с помощью% XX. Эта схема поддерживается urllib.unquote:

>>> urllib2.unquote("%0a")
'\n'

К сожалению, если вы действительно используете необходимость для поддержки% uXXXX, вам, вероятно, придется использовать собственный декодер. В противном случае, вероятно, будет гораздо предпочтительнее просто кодировать ваш Unicode UTF-8, а затем использовать% escape для результирующих байтов.

Более полный пример:

>>> u"Tanım"
u'Tan\u0131m'
>>> url = urllib.quote(u"Tanım".encode('utf8'))
>>> urllib.unquote(url).decode('utf8')
u'Tan\u0131m'

urllib2.unquote должно быть urllib.unquote.

jamtoday 07.09.2009 04:30

Интересно, что URI представляет собой закодированную в процентах байтовую строку, а не символьную строку.

wberry 20.09.2011 22:13

@jamtoday не обязательно, в Python 2.7.5+ вы можете использовать urllib2.unquote, просто попробуйте print(dir(urllib2))

Francisco Costa 21.02.2014 22:49

urllib.unquote (url.encode ('utf-8')) вместо этого работал у меня

Emily 25.01.2017 13:23

это плохая практика делать что-то вроде unquote(urlencode())?

Akin Hwan 01.08.2019 18:10

Это сработает, если вам это абсолютно необходимо (я действительно согласен с криками «нестандартность»):

from urllib import unquote

def unquote_u(source):
    result = unquote(source)
    if '%u' in result:
        result = result.replace('%u','\\u').decode('unicode_escape')
    return result

print unquote_u('Tan%u0131m')

> Tanım

Слегка патологический случай, но: unquote_u ('Tan% 25u0131m') -> u'Tan \ u0131m ', а не' Tan% u0131 ', как должно. Просто напоминание о том, почему вы, вероятно, не хотите писать декодер, если он вам действительно не нужен.

Aaron Maenpaa 19.11.2008 02:44

Я полностью согласен. Вот почему я действительно не хотел предлагать реальное решение. Эти вещи никогда не были такими простыми. Однако офицер полиции мог быть в отчаянии, и я думаю, что это дополняет ваш отличный ответ.

Ali Afshar 19.11.2008 02:48

К сожалению, это работает только для Python 2, то есть быстро приближается к концу срока службы. Использование unicode_escape немного усложняет корректировку использования Python 3 (вам нужно сначала кодировать в utf-8), но эта версия не обрабатывает суррогатные пары. Целью escape-формата %hhhh было кодирование кодовых точек UTF-16, поэтому для последовательностей, отличных от BMP (например, большого количества эмодзи), вы получите недопустимую строку для чего-либо, кроме сборки Python 2 UCS-2.

Martijn Pieters 07.03.2019 18:26

в приведенной выше версии есть ошибка, которая иногда пугает, когда в строке есть как символы в кодировке ascii, так и символы в кодировке unicode. Я думаю, что это особенно, когда есть символы из верхнего диапазона 128, такие как '\ xab', в дополнение к юникоду.

например. "% 5B% AB% u03E1% BB% 5D" вызывает эту ошибку.

Я обнаружил, что если вы сначала использовали только юникод, проблема исчезла:

def unquote_u(source):
  result = source
  if '%u' in result:
    result = result.replace('%u','\\u').decode('unicode_escape')
  result = unquote(result)
  return result

\ xab - это не символ, а байт. Фактически, ваш пример «строка» содержит как байты, так и символы, что недопустимо как отдельная строка на любом языке, который я знаю.

wberry 20.09.2011 22:05

Что будет декодировать "%5B%AB%u03E1%BB%5D"? 0x5B 0xAB и 0xBB 0x5D вряд ли являются допустимыми последовательностями UTF-8.

Martijn Pieters 07.03.2019 18:28

@wberry: Я видел реальные случаи (где-то в библиотеке Java), которые кодируют некоторые кодовые точки ASCII, такие как пробелы для последовательностей %hh и все, что больше 0x7F для последовательностей %uhhhh. Ужасно, но разборчиво.

Martijn Pieters 07.03.2019 18:29

У вас есть URL-адрес с использованием нестандартная схема кодирования, отклоненный органами по стандартизации, но все еще создаваемый некоторыми кодировщиками. Функция Python urllib.parse.unquote() не может справиться с этим.

К счастью, создать собственный декодер не так уж и сложно. Записи %uhhhh здесь предназначены для кодовых точек UTF-16, поэтому нам нужно принять во внимание суррогатные пары. Я также видел смешанные кодовые точки %hh для дополнительной путаницы.

Имея это в виду, вот декодер, который работает как в Python 2, так и в Python 3, при условии, что вы передаете объект str в Python 3 (Python 2 заботится меньше):

try:
    # Python 3
    from urllib.parse import unquote
    unichr = chr
except ImportError:
    # Python 2
    from urllib import unquote

def unquote_unicode(string, _cache = {}):
    string = unquote(string)  # handle two-digit %hh components first
    parts = string.split(u'%u')
    if len(parts) == 1:
        return parts
    r = [parts[0]]
    append = r.append
    for part in parts[1:]:
        try:
            digits = part[:4].lower()
            if len(digits) < 4:
                raise ValueError
            ch = _cache.get(digits)
            if ch is None:
                ch = _cache[digits] = unichr(int(digits, 16))
            if (
                not r[-1] and
                u'\uDC00' <= ch <= u'\uDFFF' and
                u'\uD800' <= r[-2] <= u'\uDBFF'
            ):
                # UTF-16 surrogate pair, replace with single non-BMP codepoint
                r[-2] = (r[-2] + ch).encode(
                    'utf-16', 'surrogatepass').decode('utf-16')
            else:
                append(ch)
            append(part[4:])
        except ValueError:
            append(u'%u')
            append(part)
    return u''.join(r)

Функция во многом вдохновлена ​​текущая реализация стандартной библиотеки.

Демо:

>>> print(unquote_unicode('Tan%u0131m'))
Tanım
>>> print(unquote_unicode('%u05D0%u05D9%u05DA%20%u05DE%u05DE%u05D9%u05E8%u05D9%u05DD%20%u05D0%u05EA%20%u05D4%u05D8%u05E7%u05E1%u05D8%20%u05D4%u05D6%u05D4'))
איך ממירים את הטקסט הזה
>>> print(unquote_unicode('%ud83c%udfd6'))  # surrogate pair
?
>>> print(unquote_unicode('%ufoobar%u666'))  # incomplete
%ufoobar%u666

Функция работает на Python 2 (проверено на 2.4 - 2.7) и Python 3 (проверено на 3.3 - 3.8).

Другие вопросы по теме