URL 编码产生两个不同的结果?只有一个有效

URL Encoding yields two different results? Only one works

我正在编写 Python 脚本来获取韩语词汇发音。我有一个 URL 准备就绪,当我在 Safari 中打开 URL 时,它会从服务器检索预期的 JSON。

当我使用requests获取JSON时,调用失败,没有找到结果。

使用 Charles,在我将 URL 粘贴到 Safari 并按回车键后,我可以看到 URL 和我的原始查询(一个韩文单词)被 URL 编码。例如,URL 字符串中的 소식 实例在输出时变为 %EC%86%8C%EC%8B%9D

然而,当我用 requests 发出同样的请求时,这个词被编码为 %E1%84%89%E1%85%A9%E1%84%89%E1%85%B5%E1%86%A8。两种编码都可以解码回原始单词 소식(使用网络应用程序确认)。前者编码被服务器接受,后者不接受

为什么我得到的编码与 requests 不同?

编辑

查询字符串作为 소식

进入脚本

query = sys.argv[1] sys.stderr.write(query) -> 소식

将查询插入 URL 字符串会产生 ...json/word/소식... 打印时。

通过 Charles 现在看起来像这样 /json/word/%E1%84%89%E1%85%A9%E1%84%89%E1%85%B5%E1%86%A8/。一切都是默认的,没有指定编码。

这些都是 "same" 输入文本的有效 url 编码:

>>> from urllib.parse import unquote
>>> ulong = unquote('%E1%84%89%E1%85%A9%E1%84%89%E1%85%B5%E1%86%A8')
>>> ushort = unquote('%EC%86%8C%EC%8B%9D')
>>> ulong
'소식'
>>> ushort
'소식'

字符串实际上并不相等,但是它们在 unicode 中有不同的形式:

>>> from unicodedata import name
>>> [name(x) for x in ulong]
['HANGUL CHOSEONG SIOS',
 'HANGUL JUNGSEONG O',
 'HANGUL CHOSEONG SIOS',
 'HANGUL JUNGSEONG I',
 'HANGUL JONGSEONG KIYEOK']
>>> [name(x) for x in ushort]
['HANGUL SYLLABLE SO', 'HANGUL SYLLABLE SIG']

我不懂韩语,但看起来长串是由组合字符组成的(你也可以看到拉丁字符和重音符类似的东西)。如果我执行形式的规范分解和组合,我得到相等性:

>>> from unicodedata import normalize
>>> normalize('NFC', ulong) == ushort
True

因此,要么您使用的是不同的输入文本,但恰好看起来相同(即使 repr 也不足以看出差异,您必须检查代码点)或您使用的其中一种方法正在使用 - 可能是浏览器 - 正在执行 normalization/transformation。

由于文本的短格式适用于服务器,我建议您将脚本的输入规范化为 NFC 格式。