使用Requests库在Python中处理查询字符串数组参数
我一直在尝试用 python-requests
发送一个请求,网址看起来像这样:
http://example.com/api/add.json?name='hello'&data[]='hello'&data[]='world'
通常我可以构建一个字典,然后这样做:
data = {'name': 'hello', 'data': 'world'}
response = requests.get('http://example.com/api/add.json', params=data)
这样做对我大部分的工作都没问题。不过,我遇到了上面那种网址结构,我不太确定怎么在 Python 中做到这一点,而不需要手动拼接字符串。我可以这样做,但我更希望不这样。
在 requests 库中有没有我遗漏的东西,或者有什么 Python 的特性我不知道的?
另外,这种参数类型应该怎么称呼,这样我可以更好地在网上搜索?
5 个回答
解决办法就是使用一个非常有名的函数:urlencode
>>> import urllib.parse
>>> params = {'q': 'Python URL encoding', 'as_sitesearch': 'www.urlencoder.io'}
>>> urllib.parse.urlencode(params)
'q=Python+URL+encoding&as_sitesearch=www.urlencoder.io'
有些API服务器在网址的查询字符串中需要一个JSON数组作为值。但是,普通的请求参数并不会自动生成这样的JSON数组。
我遇到类似的问题时,是通过使用urllib.parse.urlencode来编码查询字符串,然后把它加到网址里,再传给请求。
例如:
from urllib.parse import urlencode
query_str = urlencode(params)
url = "?" + query_str
response = requests.get(url, params={}, headers=headers)
如果你不一定要使用requests模块,可以试试用urllib
和urllib2
这两个组合来解决问题:
payload = [('name', 'hello'), ('data[]', ('hello', 'world'))]
params = urllib.urlencode(payload, doseq=True)
sampleRequest = urllib2.Request('http://example.com/api/add.json?' + params)
response = urllib2.urlopen(sampleRequest)
这种方法稍微复杂一点,需要用到doseq(uence)的技巧来编码网址参数。不过在我还不知道requests模块的时候,我就是用这个方法的。
如果你选择使用requests模块的话,@Tomer提供的答案应该可以解决你的问题。
你只需要把它放在一个列表里,然后把关键字设置成像列表一样的字符串:
data = {'name': 'hello', 'data[]': ['hello', 'world']}
response = requests.get('http://example.com/api/add.json', params=data)
你做的事情是对的,生成的链接和你预期的是一样的。
>>> payload = {'name': 'hello', 'data': 'hello'}
>>> r = requests.get("http://example.com/api/params", params=payload)
你可以查看生成的链接:
>>> print(r.url)
http://example.com/api/params?name=hello&data=hello
根据网址格式:
特别是,编码查询字符串时遵循以下规则:
- 字母(A–Z 和 a–z)、数字(0–9)以及字符
.
、-
、~
和_
保持不变 - 空格会被编码为
+
或%20
- 所有其他字符会被编码为 %HH 的十六进制表示,任何非 ASCII 字符会先被编码为 UTF-8(或其他指定的编码)
所以 array[]
不会按预期那样工作,会根据规则自动替换:
如果你构建一个链接像这样:
`Build URL: http://example.com/api/add.json?name='hello'&data[]='hello'&data[]='world'`
输出将是:
>>> payload = {'name': 'hello', "data[]": 'hello','data[]':'world'}
>>> r = requests.get("http://example.com/api/params", params=payload)
>>> r.url
u'http://example.com/api/params?data%5B%5D=world&name=hello'
这是因为重复的键会被链接中最后一个值替换,而 data[]
会被替换为 data%5B%5D
。
如果 data%5B%5D
不是问题(如果服务器能正确解析它),那么你可以继续使用它。