在 Python 中,处理 URL 编码字符串中的空格时,需要区分 URL 编码的空格 和 实际的空格字符。

urllib.parse.unquote的作用:它只解码%20这种形式的 URL 编码,它不会把普通的空格字符()转换成 号,也不会把 号转换成空格。urllib.parse.unquote_plus的作用:这是处理表单数据(特别是application/x-www-form-urlencoded格式)的正确函数,它会:- 将
%20解码成空格。 - 将 号解码成空格。
- 将
- 最佳实践:
- 如果你在处理一个完整的 URL(从浏览器地址栏复制来的),使用
unquote。 - 如果你在处理一个来自 HTML 表单
POST请求的查询参数(request.body或request.get_data()),必须使用unquote_plus。
- 如果你在处理一个完整的 URL(从浏览器地址栏复制来的),使用
详细解释与代码示例
urllib.parse.unquote
这个函数是 URL 解码的标准实现,它遵循 RFC 3986 标准,在这个标准中,空格被编码为 %20。
行为:
- 将
%20转换为空格 。 - 对其他
%XX形式的编码进行解码。 - 对普通空格 和 号不做任何处理。
示例:
from urllib.parse import unquote
# 场景1: 标准URL编码,空格是 %20
url_encoded_with_percent20 = "Hello%20World%21"
decoded_unquote = unquote(url_encoded_with_percent20)
print(f"原始字符串: {url_encoded_with_percent20}")
print(f"unquote 解码后: {decoded_unquote}")
# 输出:
# 原始字符串: Hello%20World%21
# unquote 解码后: Hello World!
print("-" * 20)
# 场景2: 字符串中包含普通空格
url_with_space = "Hello World"
decoded_with_space = unquote(url_with_space)
print(f"原始字符串: {url_with_space}")
print(f"unquote 解码后: {decoded_with_space}")
# 输出:
# 原始字符串: Hello World
# unquote 解码后: Hello World (空格保持不变)
print("-" * 20)
# 场景3: 字符串中包含 + 号
url_with_plus = "Hello+World"
decoded_with_plus = unquote(url_with_plus)
print(f"原始字符串: {url_with_plus}")
print(f"unquote 解码后: {decoded_with_plus}")
# 输出:
# 原始字符串: Hello+World
# unquote 解码后: Hello+World (+号保持不变)
urllib.parse.unquote_plus
这个函数专门用于处理 application/x-www-form-urlencoded 这种常见的表单提交格式,在这种格式中,为了方便,空格经常被编码成 号。

行为:
- 将
%20转换为空格 。 - 将 号也转换为空格 。
- 对其他
%XX形式的编码进行解码。
示例:
from urllib.parse import unquote_plus
# 场景1: 标准URL编码,空格是 %20
url_encoded_with_percent20 = "Hello%20World%21"
decoded_plus_1 = unquote_plus(url_encoded_with_percent20)
print(f"原始字符串: {url_encoded_with_percent20}")
print(f"unquote_plus 解码后: {decoded_plus_1}")
# 输出:
# 原始字符串: Hello%20World%21
# unquote_plus 解码后: Hello World!
print("-" * 20)
# 场景2: 表单格式编码,空格是 +
url_encoded_with_plus = "name=John+Doe&message=Hello+World"
decoded_plus_2 = unquote_plus(url_encoded_with_plus)
print(f"原始字符串: {url_encoded_with_plus}")
print(f"unquote_plus 解码后: {decoded_plus_2}")
# 输出:
# 原始字符串: name=John+Doe&message=Hello+World
# unquote_plus 解码后: name=John Doe&message=Hello World
# 注意:+ 被正确地转换成了空格
print("-" * 20)
# 场景3: 混合了 %20 和 +
url_mixed = "search=python%20tutorial+for+beginners"
decoded_plus_3 = unquote_plus(url_mixed)
print(f"原始字符串: {url_mixed}")
print(f"unquote_plus 解码后: {decoded_plus_3}")
# 输出:
# 原始字符串: search=python%20tutorial+for+beginners
# unquote_plus 解码后: search=python tutorial for beginners
# %20 和 + 都被正确地转换成了空格
在 Web 框架中的实际应用
当你使用 Flask 或 Django 等 Web 框架时,这个问题会经常出现。
Flask 示例
假设你有一个前端表单,用户输入 "John Doe"。
HTML Form:
<form action="/submit" method="post"> <input type="text" name="username" value="John Doe"> <button type="submit">Submit</button> </form>
当这个表单被 POST 提交时,请求体中的数据会被编码为 application/x-www-form-urlencoded 格式,浏览器可能会发送 username=John+Doe 或 username=John%20Doe。
Flask 后端处理:
from flask import Flask, request
from urllib.parse import unquote, unquote_plus
app = Flask(__name__)
@app.route('/submit', methods=['POST'])
def submit():
# request.get_data() 获取原始的请求体
raw_body = request.get_data(as_text=True)
print(f"原始请求体: {raw_body}")
# --- 错误的做法 ---
# 如果使用 unquote,+ 号不会被解码,可能导致问题
# decoded_wrong = unquote(raw_body)
# username_wrong = decoded_wrong.split('=')[1]
# print(f"错误解码后的用户名: {username_wrong}") # 可能是 "John+Doe"
# --- 正确的做法 ---
# 对于 form data,应该使用 unquote_plus
decoded_correct = unquote_plus(raw_body)
username_correct = decoded_correct.split('=')[1]
print(f"正确解码后的用户名: {username_correct}")
return f"你好, {username_correct}!"
if __name__ == '__main__':
app.run(debug=True)
当你提交表单后,服务器日志会显示:
原始请求体: username=John+Doe
正确解码后的用户名: John Doe
如果你错误地使用了 unquote,你可能会得到 "John+Doe" 这样的结果,这不是你想要的。
| 函数 | 处理 %20 |
处理 | 主要用途 |
|---|---|---|---|
unquote |
转换成空格 | 不做处理 | 解码完整的 URL 字符串(如 URL 路径、查询参数中的值) |
unquote_plus |
转换成空格 | 转换成空格 | 解码表单数据(application/x-www-form-urlencoded) |
记住这个关键区别,你就能正确处理 URL 中的空格了。
