使用Python进行URL编码/解码

49
我试图在Python中编码、存储和解码参数,但在过程中迷失了方向。以下是我的步骤:
1)我使用Google Toolkit的gtm_stringByEscapingForURLArgument将NSString适当地转换为HTTP参数。
2)在我的服务器(Python)上,我将这些字符串参数存储为类似于u'1234567890-/:;()$&@".,?!\'[]{}#%^*+=_\\|~<>\u20ac\xa3\xa5\u2022.,?!\''的形式。(请注意,这些是iPhone键盘上“123”视图和“# +=”视图中的标准键,其中包含一些货币前缀,如英镑、日元等)
3)我对存储的值调用urllib.quote(myString,''),可能是为了将它们进行百分号转义,以便传输给客户端,以便客户端可以取消转义。
结果是,当我尝试记录%转义的结果时,我收到了一个异常。我是否忽略了某个关键步骤来应用于具有\u和\x格式的存储值,以便将其正确转换为通过http发送?
更新:下面标记为答案的建议对我起作用了。为了完整起见,我提供了一些更新以解决下面的评论。
我收到的异常引用了\u20ac的问题。我不知道这是否特别是它的问题,而不是它是字符串中的第一个unicode字符。
\u20ac字符是“欧元”符号的unicode。我基本上发现除非我使用urllib2的quote方法,否则我会遇到问题。

1
请提供异常详细信息和堆栈跟踪(如果可能)。 - Ihor Kaharlichenko
似乎你的字符串不是一个有效的Unicode字符串。我尝试简单地打印它,但对于\u20ac字符,它会给出编码错误。 - Ankit Jaiswal
3个回答

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)。

如果你要进一步使用引号或编码,请小心不要弄乱事情。


2
你用djang.utils.http.urlquote/unquote救了我的一天!非常感谢。 - Michael Weibel
在Python3中,quoteunquote似乎被隐藏在urllib.parse中,而不是在urilliburllib2中。 - jcoppens

4
我想附议Pycruft的评论。网络协议已经发展了几十年,处理各种约定集可能很麻烦。现在,URL仅针对字节(八位)而不是字符进行明确定义。作为一个历史巧合,URL是你只能假设但不能强制执行或安全地期望存在编码的地方之一。然而,这里有一个惯例,即优先选择Latin-1和UTF-8而不是其他编码。有一段时间,看起来“unicode percent escapes”会成为未来,但它们从未流行起来。
在这个领域,对于“unicode”对象和八位“str”(在Python<3.0中;令人困惑的是,在Python≥3.0中是“str”unicode对象和“bytes”/“bytearray”对象)之间的差异要严格挑剔非常重要。不幸的是,在我的经验中,由于许多原因,在Python 2.x中很难清晰地区分这两个概念。
更加偏离主题,当你想要接收第三方HTTP请求时,你不能完全依赖于以百分号转义、UTF-8编码的八位字节发送URL:其中可能会有偶尔出现的`%uxxxx`转义,至少Firefox 2.x曾经在可能的情况下将URL编码为Latin-1,并仅在必要时才使用UTF-8。

2

如果你使用stdlib,那么urllib.quote无法处理unicode编码。如果你使用django,可以使用django.utils.http.urlquote来正确处理unicode编码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接