如何在python中取消引用urlencoded unicode字符串?
-
08-07-2019 - |
题
我有一个像<!>一样的unicode字符串; Tan <!>#305; m <!> quot;编码为<!>“Tan%u0131m <!>”;不知何故。如何将此编码的字符串转换回原始的unicode。 显然urllib.unquote不支持unicode。
解决方案
%uXXXX是非标准编码方案 w3c拒绝了,尽管实现仍然继续存在于JavaScript领域。
更常见的技术似乎是UTF-8编码字符串,然后%%使用%XX转义结果字节。 urllib.unquote支持此方案:
>>> urllib2.unquote("%0a")
'\n'
不幸的是,如果你真的需要来支持%uXXXX,你可能需要推出自己的解码器。否则,简单地UTF-8编码你的unicode然后%转义结果字节可能更为可取。
更完整的例子:
>>> u"Tanım"
u'Tan\u0131m'
>>> url = urllib.quote(u"Tanım".encode('utf8'))
>>> urllib.unquote(url).decode('utf8')
u'Tan\u0131m'
其他提示
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)
如果您必须这样做(我真的同意<!>“非标准<!>”的呼声),这将会这样做:)
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
上述版本中存在一个错误,当字符串中存在ascii编码字符和unicode编码字符时,它有时会出现问题。我认为它特别适用于除了unicode之外还有来自上层128范围的字符,如'\ xab'。
例如。 <!> QUOT;%5B%AB%u03E1%BB%5D QUOT <!>;导致此错误。
我发现如果你先做了unicode,问题就消失了:
def unquote_u(source):
result = source
if '%u' in result:
result = result.replace('%u','\\u').decode('unicode_escape')
result = unquote(result)
return result
您有使用非标准编码方案的网址,被标准机构拒绝但仍由某些编码器生产。 Python urllib.parse.unquote()
函数无法处理这些。
幸运的是,创建自己的解码器并不难。 %uhhhh
条目在这里是 UTF-16 代码点,因此我们需要代理对。我也看到了%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)
该功能受到的启发当前的标准库实施。
演示:
>>> 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上测试)。