如何在 Python 中进行 URL 编码(urllib.parse 完全指南)
Python 的 URL 编码使用 urllib.parse.quote() 对字符串进行百分号编码,使用 urllib.parse.urlencode() 将字典编码为查询字符串。本指南通过实用示例讲解 quote()、unquote()、urlencode() 和 parse_qs()。
使用 quote() 进行 URL 编码
urllib.parse.quote() 函数是 Python 中对字符串进行百分号编码的主要工具。它会把在 URL 中使用不安全的字符转换为对应的百分号编码形式。默认情况下,它将正斜杠(/)视为安全字符,但你可以自定义这一行为。
from urllib.parse import quote
# 基本编码
print(quote('hello world'))
# 输出: hello%20world
# 编码特殊字符
print(quote('price=10&qty=2'))
# 输出: price%3D10%26qty%3D2
# 默认情况下 / 不会被编码
print(quote('path/to/file'))
# 输出: path/to/file
# 若要同时编码斜杠,设置 safe=''
print(quote('path/to/file', safe=''))
# 输出: path%2Fto%2Ffile
# 编码 Unicode 字符
print(quote('cafe'))
# 输出: caf%C3%A9
# 指定额外的安全字符
print(quote('key=value&foo=bar', safe='=&'))
# 输出: key=value&foo=bar
safe 参数是控制哪些字符会被编码的关键。默认情况下 safe='/'。如果你希望编码除字母数字字符和 _.-~ 之外的所有字符,可以设置 safe=''。这等价于 JavaScript 中的 encodeURIComponent()。
此外还有 quote_plus(),它的用法与 quote() 类似,但会把空格编码为 + 而不是 %20。这正是 HTML 表单数据(application/x-www-form-urlencoded)所使用的格式。
from urllib.parse import quote_plus
print(quote_plus('hello world'))
# 输出: hello+world
print(quote_plus('key=value&name=John Doe'))
# 输出: key%3Dvalue%26name%3DJohn+Doe
使用 unquote() 进行 URL 解码
urllib.parse.unquote() 函数用于逆转百分号编码,将 %XX 序列还原为其原始字符。此外还有 unquote_plus(),它会额外把 + 号转换为空格。
from urllib.parse import unquote, unquote_plus
# 基本解码
print(unquote('hello%20world'))
# 输出: hello world
print(unquote('caf%C3%A9'))
# 输出: cafe(带重音符号)
# unquote 不会把 + 转换为空格
print(unquote('hello+world'))
# 输出: hello+world
# unquote_plus 会把 + 转换为空格
print(unquote_plus('hello+world'))
# 输出: hello world
# 解码完整 URL
url = 'https://example.com/search?q=C%2B%2B%20programming'
print(unquote(url))
# 输出: https://example.com/search?q=C++ programming
解码表单数据时,请始终使用 unquote_plus(),因为 HTML 表单会把空格编码为 +。对于空格被编码为 %20 的常规 URL 解码,则使用 unquote()。
使用 urlencode() 编码查询字符串
urllib.parse.urlencode() 函数接受一个字典或元组列表,并将其转换为格式正确的查询字符串。这是 Python 中构建查询字符串最便捷的方式。
from urllib.parse import urlencode
# 字典转查询字符串
params = {
'q': 'python programming',
'page': 1,
'lang': 'en'
}
print(urlencode(params))
# 输出: q=python+programming&page=1&lang=en
# 元组列表(保留顺序,允许重复键)
params = [
('tag', 'python'),
('tag', 'web'),
('sort', 'date')
]
print(urlencode(params))
# 输出: tag=python&tag=web&sort=date
# 对列表值使用 doseq=True
params = {
'tag': ['python', 'web', 'api'],
'sort': 'date'
}
print(urlencode(params, doseq=True))
# 输出: tag=python&tag=web&tag=api&sort=date
# 使用 quote_via 控制空格的编码方式
from urllib.parse import quote
params = {'q': 'hello world'}
print(urlencode(params, quote_via=quote))
# 输出: q=hello%20world (使用 %20 而不是 +)
默认情况下,urlencode() 内部使用 quote_plus(),这意味着空格会变成 +。如果你需要用 %20 表示空格,可以像上面那样传入 quote_via=quote。
使用 parse_qs() 解析查询字符串
urllib.parse.parse_qs() 函数可以把查询字符串解析回字典。字典中的每个值都是一个列表,因为查询参数可能有多个值。此外还有 parse_qsl(),它返回的是元组列表。
from urllib.parse import parse_qs, parse_qsl
# 将查询字符串解析为字典
qs = 'q=python+programming&page=1&lang=en'
result = parse_qs(qs)
print(result)
# 输出: {'q': ['python programming'], 'page': ['1'], 'lang': ['en']}
# 注意: 值始终是列表
print(result['q'][0]) # 'python programming'
# 处理同一个键的多个值
qs = 'tag=python&tag=web&tag=api'
result = parse_qs(qs)
print(result)
# 输出: {'tag': ['python', 'web', 'api']}
# parse_qsl 返回元组列表
result = parse_qsl(qs)
print(result)
# 输出: [('tag', 'python'), ('tag', 'web'), ('tag', 'api')]
# 保留空值(默认情况下会被忽略)
qs = 'name=John&email=&age=30'
print(parse_qs(qs, keep_blank_values=True))
# 输出: {'name': ['John'], 'email': [''], 'age': ['30']}
使用 urlparse 编码完整 URL
在处理完整 URL 时,Python 的 urlparse() 和 urlunparse() 函数可以让你安全地分解和重建 URL。当你需要修改 URL 的特定部分而又不破坏其整体结构时,这尤其有用。
from urllib.parse import urlparse, urlunparse, urlencode, quote
# 将 URL 解析为各个组成部分
url = 'https://example.com/search?q=hello&page=1#results'
parsed = urlparse(url)
print(parsed.scheme) # 'https'
print(parsed.netloc) # 'example.com'
print(parsed.path) # '/search'
print(parsed.query) # 'q=hello&page=1'
print(parsed.fragment) # 'results'
# 从各个组成部分构建 URL
from urllib.parse import ParseResult
new_url = urlunparse(ParseResult(
scheme='https',
netloc='api.example.com',
path='/v2/search',
params='',
query=urlencode({'q': 'python & java', 'limit': 10}),
fragment=''
))
print(new_url)
# 输出: https://api.example.com/v2/search?q=python+%26+java&limit=10
# 安全地添加包含特殊字符的路径段
base = 'https://example.com/files/'
filename = 'my report (final).pdf'
safe_url = base + quote(filename, safe='')
print(safe_url)
# 输出: https://example.com/files/my%20report%20%28final%29.pdf
对于现代 Python 代码,可以考虑使用 requests 库,当你以字典形式传入参数时,它会自动处理 URL 编码。httpx 库同样提供了类似的自动编码能力。
import requests
# requests 会自动处理编码
response = requests.get(
'https://api.example.com/search',
params={
'q': 'python & java',
'page': 1,
'sort': 'relevance'
}
)
print(response.url)
# https://api.example.com/search?q=python+%26+java&page=1&sort=relevance