使用 Python 进行 URL 编码/解码

49 投票
3 回答
84547 浏览
提问于 2025-04-16 03:16

我在用Python编码、存储和解码参数时遇到了一些困惑。以下是我的步骤:

1) 我使用谷歌工具包里的 gtm_stringByEscapingForURLArgument 方法,把一个NSString转换成适合放进HTTP参数里的格式。

2) 在我的服务器(用Python写的)上,我把这些字符串参数存储成像这样 u'1234567890-/:;()$&@".,?!\'[]{}#%^*+=_\\|~<>\u20ac\xa3\xa5\u2022.,?!' 的形式(注意这些是iPhone键盘在“123”和“#+=”视图下的标准键,里面的 \u\x 字符是一些货币符号,比如英镑、日元等)。

3) 我对存储的值调用 urllib.quote(myString,''),大概是想把它们进行百分号编码,以便传输到客户端,这样客户端就可以解码了。

结果是,当我尝试记录百分号编码的结果时,出现了异常。我是不是漏掉了什么重要的步骤,导致无法正确转换包含 \u\x 格式的存储值,以便通过HTTP发送呢?

更新: 下面标记为答案的建议对我有效。我提供一些更新来回应下面的评论,以便更完整。

我收到的异常提到了 \u20ac 的问题。我不知道这是否是特定于这个字符的问题,还是因为它是字符串中的第一个unicode字符。

那个 \u20ac 字符是表示“欧元”符号的unicode。我发现如果不使用urllib2的 quote 方法,我会遇到问题。

3 个回答

2

你在使用标准库的时候运气不好,urllib.quote 这个函数对 Unicode 字符串不太管用。如果你在用 Django 框架,可以试试 django.utils.http.urlquote,这个函数对 Unicode 字符串处理得很好。

4

我想支持pycruft的观点。网络协议经过几十年的发展,处理各种约定的方式可能会让人感到麻烦。现在,URL实际上是针对字节(八位组)而不是字符进行明确定义的。由于历史原因,URL是一个你只能假设,但不能强制或安全地期待某种编码存在的地方。不过,这里有一个约定,就是优先使用latin-1和utf-8编码,而不是其他编码。曾经一度看起来像是'unicode百分号转义'会成为未来的主流,但它们并没有流行起来。

在这个领域,准确区分unicode对象和八位组str(在Python 3.0之前;这在Python 3.0及之后又让人困惑,因为那时str是unicode对象,而bytes/bytearray是字节对象)是非常重要的。不幸的是,根据我的经验,在Python 2.x中,干净地分开这两个概念是相当困难的。

更进一步,当你想接收第三方的HTTP请求时,你不能完全依赖于URL是以百分号转义的utf-8编码字节发送的:有时可能会出现%uxxxx的转义,而且至少在Firefox 2.x中,URL会尽可能使用latin-1编码,而只有在必要时才使用utf-8。

71

把“原始”的unicode进行url编码其实没什么意义。你需要先用 .encode("utf8") 进行编码,这样你就有了一个明确的字节编码,然后再用 .quote() 进行处理。

输出的结果可能看起来不太好,但它应该是正确的uri编码。

>>> s = u'1234567890-/:;()$&@".,?!\'[]{}#%^*+=_\|~<>\u20ac\xa3\xa5\u2022.,?!\''
>>> urllib2.quote(s.encode("utf8"))
'1234567890-/%3A%3B%28%29%24%26%40%22.%2C%3F%21%27%5B%5D%7B%7D%23%25%5E%2A%2B%3D_%5C%7C%7E%3C%3E%E2%82%AC%C2%A3%C2%A5%E2%80%A2.%2C%3F%21%27'

记住,如果你在调试或者其他情况下想要正确打印出来,你需要同时使用 unquote()decode()

>>> print urllib2.unquote(urllib2.quote(s.encode("utf8")))
1234567890-/:;()$&@".,?!'[]{}#%^*+=_\|~<>€£¥•.,?!'
>>> # oops, nasty  means we've got a utf8 byte stream being treated as an ascii stream
>>> print urllib2.unquote(urllib2.quote(s.encode("utf8"))).decode("utf8")
1234567890-/:;()$&@".,?!'[]{}#%^*+=_\|~<>€£¥•.,?!'

实际上,这就是在另一个回答中提到的 django函数 的作用。

这些函数 django.utils.http.urlquote() 和 django.utils.http.urlquote_plus() 是 Python标准库中的 urllib.quote() 和 urllib.quote_plus() 的 版本,能够处理非ASCII字符。 (数据在编码之前会被转换为UTF-8。)

如果你还要进行其他的引用或编码,记得小心不要搞混了。

撰写回答