一、什么是url编码(urlquote)
url中部分字符属于保留字符,不能直接放在查询参数里,例如 { } " : , 等。
url编码作用:将特殊字符转为 %十六进制 格式。
常见对照表:
"→%22{→%7b}→%7d:→%3a,→%2c%→%25重点!百分号本身编码后变成 %25,这就是双重编码的核心根源
python内置模块 urllib.parse.quote 用来做url编码,无需额外安装第三方包。
quote 关键参数:safe
quote(string, safe='/', encoding=none, errors=none)
safe:设置不需要编码的字符,默认是/,也就是斜杠不会被编码。safe='':强制所有字符全部编码,不保留任何原始字符,爬虫对接很多后端接口时必须用这个。
示例单层编码:
from urllib.parse import quote
import json
data = {
"articleid": "89c46c77f90e61ff7df01e3cc18eae9349f619f697ca4366e94fad724d913ee6",
"columnid": "268f86fdf61ac8614f09db38a2d0295253043b03e092c7ff48ab94290296125c"
}
json_str = json.dumps(data, separators=(',', ':'))
# 单层url编码
encode_1 = quote(json_str, safe='')
print(encode_1)
# 输出:%7b%22articleid%22%3a%2289c46c77f90e61ff7df01e3cc18eae9349f619f697ca4366e94fad724d913ee6%22%2c%22columnid%22%3a%22268f86fdf61ac8614f09db38a2d0295253043b03e092c7ff48ab94290296125c%22%7d
二、双重url编码是什么(本次业务场景)
很多老旧java/php后端、门户站点接口,会在服务端自动做一次url解码。
如果前端传递json字符串作为url参数:
- 前端把json编码一次 →
%7bxxx%7d - 浏览器/网关还会再自动编码一遍,把里面的
%编码成%25
最终就变成我们看到的:%257b%2522articleid%2522...%257d
本质:对已经编码后的字符串,再执行一次quote
# 双层url编码
encode_1 = quote(json_str, safe='')
encode_2 = quote(encode_1, safe='')
final_param = f"params={encode_2}"
print(final_param)
# params=%257b%2522articleid%2522%3a%252289c46c77f90e61ff7df01e3cc18eae9349f619f697ca4366e94fad724d913ee6%2522%2c%2522columnid%2522%3a%2522268f86fdf61ac8614f09db38a2d0295253043b03e092c7ff48ab94290296125c%2522%257d
完整链路:从原始url提取参数 + 双重编码
业务场景:从一个url链接中提取query参数,组装成json,再双重url编码拼成params=xxx
from urllib.parse import urlparse, parse_qs, quote, unquote
import json
url = '[https://www.chinaisa.org.cn/gxportal/xfgl/portal/content.html?articleid=89c46c77f90e61ff7df01e3cc18eae9349f619f697ca4366e94fad724d913ee6&columnid=268f86fdf61ac8614f09db38a2d0295253043b03e092c7ff48ab94290296125c](https://www.chinaisa.org.cn/gxportal/xfgl/portal/content.html?articleid=89c46c77f90e61ff7df01e3cc18eae9349f619f697ca4366e94fad724d913ee6&columnid=268f86fdf61ac8614f09db38a2d0295253043b03e092c7ff48ab94290296125c)'
# 1. 解析url,提取查询参数
parsed = urlparse(url)
query_dict_raw = parse_qs(parsed.query)
# parse_qs返回值是列表,取第一个值转为普通字典
data = {k: v[0] for k, v in query_dict_raw.items()}
# 2. 字典压缩成json字符串,去掉多余空格
json_str = json.dumps(data, separators=(',', ':'))
# 3. 双重url编码
encode1 = quote(json_str, safe='')
encode2 = quote(encode1, safe='')
result = f"params={encode2}"
print(result)
三、解码:双重编码反向还原
调试的时候,我们拿到params=%257b...字符串,需要还原回原始字典,解码顺序反过来:先unquote一次,再unquote第二次
from urllib.parse import unquote
import json
encoded_str = "params=%257b%2522articleid%2522%3a%252289c46c77f90e61ff7df01e3cc18eae9349f619f697ca4366e94fad724d913ee6%2522%2c%2522columnid%2522%3a%2522268f86fdf61ac8614f09db38a2d0295253043b03e092c7ff48ab94290296125c%2522%257d"
# 截取params=后面部分
val = encoded_str.split("params=")[1]
# 两次解码
d1 = unquote(val)
d2 = unquote(d1)
origin_dict = json.loads(d2)
print(origin_dict)
四、常见踩坑总结
- 忘记
safe='':默认safe="/",/不会编码,很多接口校验严格,会直接报错。 - 分不清单层和双层编码:接口返回参数带有大量
%25,基本就是双重url编码。 parse_qs返回list:同一个参数允许多次出现,{k:v[0]}只适用于参数唯一场景。- json序列化带空格:
json.dumps(data)默认带换行空格,编码后后端识别异常,务必用separators=(',',':')压缩json。 quote_plus和quote的区别:quote_plus会把空格编码成+,常用于application/x-www-form-urlencoded表单;quote空格编码成%20,url路径参数优先使用quote。
五、什么时候会遇到双重url编码
- 老项目java后端,服务端自动做一次decode
- 多层网关、nginx转发,每层都会自动解码一次
- 前端js先编码,浏览器请求时又自动编码一遍
- 爬虫对接政务、国企门户网站(本次案例就是典型场景)
调试小技巧:看到编码串大量%25,第一反应:这不是普通url编码,是双重编码。
六、小结
url编码本质就是特殊字符转义,单层编码是基础;而双重url编码是爬虫对接老旧网站高频遇到的特殊场景。核心逻辑:json字符串 → quote一次 → quote第二次。
python依靠urllib.parse内置库就能全部实现,无需requests或者其他第三方编码库。调试时可以逆向解码验证结果是否正确,避免接口报参数解析失败。
以上就是python url编码之单层编码、双重编码实战指南的详细内容,更多关于python url编码单层与双重编码的资料请关注代码网其它相关文章!
发表评论