背景
調試 http GET請求的 map 參數,鏈路攜帶參數一直有問題,最終采用如下方式攜帶map 解決
user={"demo":"true","info":"王者"}
url encode之后的效果如下所示
user=%7B%22demo%22:%22true%22,%22info%22:%22%E7%8E%8B%E8%80%85%22%7D
最終http的url如下:
http://www.demo.com?user=%7B%22demo%22:%22true%22,%22info%22:%22%E7%8E%8B%E8%80%85%22%7D
代碼
方式一
tmp = {"demo":"true","info":"王者"}
json_str = json.dumps(tmp)
encoded_str = urllib.parse.quote(json_str)
url += '&user=%s' % encoded_str
方式二
tmp = {"demo":"true","info":"王者"}
params['user'] = json.dumps(tmp)
r = requests.get(url, headers=headers, params=params, timeout=2)
urllib.parse.quote
URL 中,某些字符具有特殊含義
例如 /
用于分隔 URL 的不同部分,?
用于標識查詢字符串的起始,&
用于分隔查詢字符串中的不同參數等
當需要在 URL 里包含可能與這些特殊字符沖突的字符(像空格、中文、?
、&
、=
等特殊符號等)時,就必須對這些字符進行 url 編碼,以保證 URL 的正確性和完整性
urllib.parse.quote 函數的作用就是把字符串里的特殊字符轉換為符合 URL 規范的編碼形式
urllib.parse.quote 函數采用的是百分號編碼(Percent-encoding)規則,也稱作 URL 編碼。在此編碼規則下,每個特殊字符會被替換成 %
后面跟著兩個十六進制數字,這兩個數字代表該字符的 ASCII 碼值。比如,空格會被編碼為 %20
,中文等非 ASCII 字符會先轉化為 UTF-8 字節序列,然后每個字節再進行百分號編碼
urllib.parse.quote(string, safe='/', encoding=None, errors=None)
string:
這是必需參數,指的是需要進行 URL 編碼的字符串
safe:
可選,指定哪些字符不需要進行編碼,默認值為 /
,意味著 /
字符不會被編碼
可以根據實際需求修改這個參數,例如 safe=‘’ 表示對所有字符都進行編碼
encoding:
可選,指定字符串的編碼方式,默認使用 UTF - 8 編碼
errors:
可選,指定編碼錯誤的處理方式,默認值為 ‘strict’,表示遇到編碼錯誤時會拋出異常
示例
import urllib.parse# 包含特殊字符和中文的字符串
string_to_encode = "你好, world! & / ? ="# 進行 URL 編碼
encoded_string = urllib.parse.quote(string_to_encode)print(encoded_string)
效果
%E4%BD%A0%E5%A5%BD%2C%20world%21%20%26%20/%20%3F%20%3D
編碼學習
百分號編碼(Percent-encoding)也稱為 URL 編碼,它將非 ASCII 字符先轉換為 UTF-8 字節序列,然后每個字節再用 %
加上對應的兩位十六進制數來表示。
如何將漢字 “我” 轉換為 %E6%88%91
:
字符編碼為 UTF-8 字節序列:在 UTF-8 編碼中,不同的字符會被編碼為不同長度的字節序列。像漢字通常會被編碼為 3 個字節。“我” 這個字在 UTF-8 編碼下對應的字節序列是 0xE6 0x88 0x91
轉換為百分號編碼形式:把每個字節轉換為 %
加上對應的兩位十六進制數。
例如,字節 0xE6
轉換為 %E6
,字節 0x88
轉換為 %88
,字節 0x91
轉換為 %91
。最終 “我” 就被編碼為 %E6%88%91
cat demo.py
# coding: utf-8# 定義要編碼的字符
char = "我"# 對字符進行 UTF-8 編碼,得到字節序列
utf8_bytes = char.encode('utf-8')# 將字節序列轉換為百分號編碼形式
percent_encoded = ''.join(f'%{byte:02X}' for byte in utf8_bytes)# %E6%88%91
print(percent_encoded)
url編碼時,為什么有些編碼是把空格編碼為+
,有些則編碼為%20
主要取決于使用的編碼規范和具體場景
歷史背景與規范差異
+
編碼(application/x-www-form-urlencoded)
表單提交規范:在 HTML 表單使用 POST 方法提交數據時,如果表單的 enctype 屬性設置為 application/x-www-form-urlencoded(這是表單提交的默認編碼類型),空格會被編碼為 +
。這個規范源自早期的互聯網實踐,是為了在傳輸數據時能夠更緊湊地表示空格,因為 +
比 %20
占用的字符更少
相關標準:這種編碼方式在 HTML 表單數據處理和一些老的 CGI(Common Gateway Interface)程序中廣泛使用。例如,當在一個 HTML 表單中輸入包含空格的內容并提交時,服務器端接收到的數據里空格就會以 +
的形式呈現。
%20
編碼(通用 URL 編碼)
RFC 3986 標準:根據互聯網工程任務組(IETF)發布的 RFC 3986 標準,在通用的 URL 編碼中,空格應該被編碼為 %20
。這個標準定義了統一的 URI(Uniform Resource Identifier)語法和編碼規則,適用于各種類型的 URL,包括路徑、查詢參數等。
通用性和兼容性:%20
編碼更具通用性和兼容性,因為它遵循了標準的百分號編碼規則,可以確保在不同的系統和應用程序中正確解析。在大多數現代的 HTTP 請求庫和工具中,默認使用 %20
來編碼空格。
不同編程語言和庫的處理方式
使用 +
編碼的情況
Python 的 urllib.urlencode
(Python 2):
在 Python 2 的 urllib 模塊中,urlencode 函數默認將空格編碼為 +
,這是為了與 application/x-www-form-urlencoded 規范保持一致
# coding: utf-8import urllibparams = {'key': 'hello world'}
encoded_params = urllib.urlencode(params)# 輸出: key=hello+world
print encoded_params
使用 %20 編碼的情況
Python 的 urllib.parse.quote(Python 3):
在 Python 3 的 urllib.parse 模塊中,quote 函數遵循 RFC 3986 標準,將空格編碼為 %20
# coding: utf-8import urllib.parsestring = 'hello world'
encoded_string = urllib.parse.quote(string)# 輸出: hello%20world
print(encoded_string)
requests處理時的 params 默認做法
# coding: utf-8import requestsparams = {'key': 'hello world'}
resp = requests.get('http://example.com', params=params)# http://example.com/?key=hello+world
print(resp.url)
# <Response [200]>
print(resp)
示例
# coding: utf-8import json
import requestsuser= {"demo": "true","info": "王者"
}
# {'user': '{"demo": "true", "info": "\\u738b\\u8005"}'} map以這種方式json化有空格
user_str = json.dumps(user)
params = {'user': user_str}
response = requests.get('http://example.com', params=params)# http://example.com/?user=%7B%22demo%22%3A+%22true%22%2C+%22info%22%3A+%22%5Cu738b%5Cu8005%22%7D
print(response.url)
# <Response [200]>
print(response)
手動挨個編碼
# coding: utf-8import requests
import urllib.parse# 定義參數
original_params = {'key': 'hello world'
}# 手動編碼參數值
encoded_params = {key: urllib.parse.quote(value) for key, value in original_params.items()}url = 'http://httpbin.org/get'
response = requests.get(url, params=encoded_params)# http://httpbin.org/get?key=hello%2520world
print(response.url)
編碼過度了,編碼了兩遍
使用%20
# coding: utf-8
import requests
import urllib.parse# 定義參數
original_params = {'key': 'hello world'
}# 手動編碼參數值并構建參數字符串
param_list = []
for key, value in original_params.items():encoded_value = urllib.parse.quote(value)param_list.append(f"{key}={encoded_value}")
param_string = '&'.join(param_list)base_url = 'http://httpbin.org/get'
full_url = f"{base_url}?{param_string}"# http://httpbin.org/get?key=hello%20world
response = requests.get(full_url)
print(response.url)
總結
空格編碼為 +
主要用于 HTML 表單提交和一些遵循 application/x-www-form-urlencoded 規范的場景
空格編碼為 %20
遵循 RFC 3986 標準,適用于通用的 URL 編碼,具有更好的通用性和兼容性
在實際應用中,需要根據具體的需求和場景選擇合適的編碼方式