用 Python 标准库追加 URL 查询参数:保留重复键和片段
调用搜索接口时,一个参数有时会出现多次,比如 tag=python&tag=cloud。如果直接把 ?tag=... 拼到已有 URL 后面,原地址可能已经有查询参数,也可能带有 #section 片段;手工拼接容易把参数放错位置,或漏掉空值与编码。
下面用 Python 标准库 urllib.parse 做一个小函数:保留原查询参数的顺序、重复键和空值,再追加新参数,同时保留 URL 片段。
完整示例
保存为 url_query.py:
"""Append query parameters while keeping existing repeated keys and fragments."""
from __future__ import annotations
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
def append_query(url: str, params: dict[str, str | list[str]]) -> str:
parts = urlsplit(url)
existing = parse_qsl(parts.query, keep_blank_values=True)
query = "&".join(
piece
for piece in (urlencode(existing), urlencode(params, doseq=True))
if piece
)
return urlunsplit(parts._replace(query=query))
if __name__ == "__main__":
source = "https://example.com/search?tag=old&empty=#section"
result = append_query(source, {"tag": ["python", "cloud"], "q": "a b"})
print(result)
print(parse_qsl(urlsplit(result).query, keep_blank_values=True))
运行结果:
https://example.com/search?tag=old&empty=&tag=python&tag=cloud&q=a+b#section
[('tag', 'old'), ('empty', ''), ('tag', 'python'), ('tag', 'cloud'), ('q', 'a b')]
输出中的 tag 出现三次:原地址的一次和新加入的两次。empty= 没丢,#section 仍在末尾。q 的空格编码为 +,这是 urlencode() 默认采用的表单式查询编码;再用 parse_qsl() 读取时会还原为空格。
关键步骤
先拆分 URL。 urlsplit() 把路径、查询串和片段分开,避免把新参数接到 #section 后面。片段只在浏览器等客户端内部使用,不属于发给服务器的查询参数。
保留原查询参数。 parse_qsl(..., keep_blank_values=True) 返回有序的键值对列表,所以重复键与空值都能留下。如果改用 parse_qs() 再随意转成普通字典,就需要额外处理多值列表和顺序。
展开新参数。 urlencode(params, doseq=True) 会把 {"tag": ["python", "cloud"]} 编成 tag=python&tag=cloud。省略 doseq=True 时,列表会被当成一个值编码,通常不是接口所需的重复键格式。普通字符串仍作为单个值处理。
最后把已有参数与新增参数的编码结果用 & 连接,并用 urlunsplit() 重组 URL。代码在任一部分为空时不会插入多余的 &。
使用边界
此函数追加参数;如果接口要求“替换同名参数”,应先从 existing 中删去该键,再编码新值。原查询串会经过解析和重新编码,因而 %20 可能变为 +,虽然一般查询解析结果相同,但不适合必须逐字保留原始查询串的签名 URL。对已签名的 URL 追加或重排参数也可能让签名失效,应按该接口的签名规则重新签名。
示例只处理字符串和字符串列表。若要传入数字、布尔值或 None,先明确接口要求的文本格式,再转换为字符串;不要让 None 意外变成字面量 None。
- 点赞
- 收藏
- 关注作者
评论(0)