使用Requests在Python中传递查询字符串数组参数

56

我一直在尝试弄明白如何使用python-requests发送一个URL长这样的请求:

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)

对于我所做的大多数事情来说,那很好用。但是,我遇到了上面的url结构,不确定如何在Python中处理它,而不是手动构建字符串。我可以这样做,但宁愿不这样做。

是否有请求库中的某些功能我没发现或者是我不知道的Python特性?

还有,你们怎么称呼那种类型的参数,以便我可以更好地谷歌它?


我认为requests中没有任何功能可以构建这样的URL。您需要通过代码手动构建它。 - Arvind
这种类型的URL被称为“带查询字符串的URL”。 但是它们之间没有[]这样的东西。 - Arvind
5个回答

117

你需要做的就是将它放在列表中,并将键设置为类似于列表的字符串

data = {'name': 'hello', 'data[]': ['hello', 'world']}
response = requests.get('http://example.com/api/add.json', params=data)

2
我希望原始用户能够找到这个答案,因为它完全解决了使用SendGrid API的问题。感谢@Tomer。 - BenDundee
1
只需使用requests(2.9)进行data = {'name':'hello','data':['hello','world']}即可正常工作。 - Rémy Greinhofer
1
@RémyGreinhofer 那对我不起作用。我不得不使用Tomer的解决方案,我正在使用requests 2.10。 - Sergio
1
数据 = {'name': 'hello', 'data': ['hello', 'world']} 将被翻译为:data=hello&data=world&name=hello,但这不符合RFC标准。数据 = {'name': 'hello', 'data[]': ['hello', 'world']} 将被翻译为:data%5B%5D=hello&data%5B%5D=world&name=hello (data[]=hello&data[]=world&name=hello)。 - Tomer Zait
1
也可以与 aiohttp 很好地配合使用。 - Xiwei Wang
显示剩余2条评论

29

你所做的是正确的。结果的URL与你期望的相同。

>>> payload = {'name': 'hello', 'data': 'hello'}
>>> r = requests.get("http://example.com/api/params", params=payload)

您可以看到结果 URL:

>>> print(r.url)
http://example.com/api/params?name=hello&data=hello
根据url格式,特别是查询字符串的编码采用以下规则:
  • 字母(A-Z和a-z)、数字(0-9)和字符.,-,~_保持不变
  • 空格转码为+%20
  • 所有其他字符都使用%HH十六进制表示进行编码,并且任何非ASCII字符首先被编码为UTF-8(或其他指定编码)
因此,array[]将不符合预期,并将根据规则自动替换:
如果您构建一个如下的url:
`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'

这是因为在URL中,重复的键将被最后一个值所替代,而data[]将被替换为data%5B%5D

如果data%5B%5D不是问题(如果服务器能够正确解析它),那么您可以继续使用它。

来源链接


2
我同意你所说的一切。不幸的是,我遇到了一个 API(http://sendgrid.com/docs/API_Reference/Marketing_Emails_API/emails.html,请查看“将多个电子邮件收件人添加到列表中”),它期望有多个相同的键。因此需要一种方法来强制重复,或者我需要手动构建参数字符串。 - Buddy Lindsey
是的,正如我所说,您可以使用字符串操作并使用该URL进行制作,但是如果您想坚持使用“requests”库,则不提供该功能。 - Arvind
从希望中得到的东西永远不会有损失。我将把这个标记为答案,因为它至少证明了我已经想到的事情。 - Buddy Lindsey
这个答案解释了请求是如何发送的。然而,如果你的API正确处理查询参数数组(就像Django的QueryDict.getlist()方法[https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.QueryDict.getlist]一样),那么你可以像这样发送查询参数,它们将会被正确处理。基本上,API需要正确处理请求。 - Blairg23
如果您有三个或更多类似的键,则它们将被覆盖,那么这不会起作用。 - urek mazino

10
如果不一定需要使用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)技巧来编码URL参数,但当我不知道requests模块时使用了它。

对于requests模块,@Tomer提供的答案应该可以工作。


1
一些 API 服务器期望在 URL 查询字符串中的值为 JSON 数组。然而,请求参数并不会将参数转换为 JSON 数组作为值。
我在解决类似问题时的方法是使用 urllib.parse.urlencode 对查询字符串进行编码,将其添加到 URL 中并传递给 requests。
例如:
from urllib.parse import urlencode
query_str = urlencode(params)
url = "?" + query_str
response = requests.get(url, params={}, headers=headers)

0

解决方案很简单,只需使用著名的函数: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'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接