【问题标题】:How to unquote a urlencoded unicode string in python?如何在python中取消引用urlencoded unicode字符串?
【发布时间】:2010-09-22 23:04:37
【问题描述】:

我有一个像“Tanım”这样的 unicode 字符串,它以某种方式编码为“Tan%u0131m”。如何将此编码字符串转换回原始 unicode。 显然 urllib.unquote 不支持 unicode。

【问题讨论】:

    标签: python unicode character-encoding urllib w3c


    【解决方案1】:

    %uXXXX 是一个被 w3c 拒绝的non-standard encoding scheme,尽管一个实现仍然存在于 JavaScript 领域。

    更常见的技术似乎是对字符串进行 UTF-8 编码,然后使用 %XX 对结果字节进行 % 转义。 urllib.unquote 支持此方案:

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

    不幸的是,如果你真的需要支持 %uXXXX,你可能不得不推出自己的解码器。否则,可能更可取的是简单地对您的 unicode 进行 UTF-8 编码,然后对结果字节进行 % 转义。

    一个更完整的例子:

    >>> 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'
    • 有趣的是 URI 是百分比编码的字节字符串,而不是字符串。
    • @jamtoday 没必要,在 Python 2.7.5+ 中你可以使用urllib2.unquote 试试print(dir(urllib2))
    • urllib.unquote(url.encode('utf-8')) 代替我工作
    • unquote(urlencode()) 这样的做法是不好的做法吗?
    【解决方案2】:
    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,不幸的是,它是 rapidly approaching its end-of-life。要使这个 Python 2 和 3 兼容(try: unichrexcept NameError: unichr = chr)并不难纠正,但这个版本不处理代理对。 %hhhh 转义格式的目的是编码 UTF-16 代码点,因此对于非 BMP 序列(例如大量表情符号),除了 a UCS-2 Python 2 构建。
    【解决方案3】:

    如果你绝对必须拥有它,它会这样做(我真的同意“非标准”的呼声):

    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' 应该的。只是提醒你为什么除非你真的需要它,否则你可能不想编写解码器。
    • 我完全同意。这就是为什么我真的不热衷于提供一个实际的解决方案。这些事情从未如此简单。不过,OP 可能已经绝望了,我认为这补充了您的出色答案。
    • 这仅适用于 Python 2,不幸的是,它是 rapidly approaching its end-of-lifeunicode_escape 的使用使 Python 3 的使用更难更正(您需要先编码为 utf-8),但此版本不处理代理对。 %hhhh 转义格式的目的是编码 UTF-16 代码点,因此对于非 BMP 序列(例如大量表情符号),除了 a UCS-2 Python 2 构建。
    【解决方案4】:

    上述版本中存在一个错误,当字符串中同时存在 ascii 编码和 unicode 编码字符时,有时会出现异常。我认为它特别适用于除了 unicode 之外还有 '\xab' 等高 128 范围内的字符。

    例如。 "%5B%AB%u03E1%BB%5D" 会导致此错误。

    我发现如果你先做 unicode,问题就消失了:

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

    【讨论】:

    • \xab 不是字符而是字节。实际上,您的示例“字符串”同时包含字节和字符,这在我所知道的任何语言中作为单个字符串都是无效的。
    • "%5B%AB%u03E1%BB%5D" 解码为什么? 0x5B 0xAB 和 0xBB 0x5D 几乎不是有效的 UTF-8 序列。
    • @wberry:我见过将一些 ASCII 码点(例如空格)编码为 %hh 序列的真实案例(某处的 Java 库),以及将超过 0x7F 的任何内容编码为 %uhhhh 序列。糟糕,但可以解析。
    【解决方案5】:

    您有一个使用 non-standard encoding scheme 的 URL,被标准机构拒绝,但仍由某些编码器生成。 Python urllib.parse.unquote() 函数无法处理这些。

    幸运的是,创建自己的解码器并不难。 %uhhhh 条目在这里是 UTF-16 代码点,因此我们需要考虑 surrogate pairs。我还看到混入了%hh 代码点,以增加混淆。

    考虑到这一点,这里有一个可在 Python 2 和 Python 3 中工作的解码器,前提是您在 Python 3 中传入 str 对象(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)
    

    该函数深受current standard-library implementation 的启发。

    演示:

    >>> 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 上测试)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-17
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多