python请求的URL规则

时间:2016-03-17 03:33:52

标签: python python-requests

最近,我将客户端上传代码从HTTPConnection移植到requests。上传图片时:

file_name ='/path/to/216169_1286900924tait.jpg?pt=5&ek=1'

存储在磁盘上的图像实际上是名称,我想将它上传到具有相同路径和名称的远程服务器,所以我构建了这样的请求:

url = 'http://host/bucket_name/%s' % (file_name)
headers = {...} # some other headers
with open(file_name, 'rb') as fd:
    data = fd.read()
    r = requests.put(url, data=data, headers=headers)
    assert(r.status_code==200)
    ....

但发送给服务器的请求改为:

/path/to/216169_1286900924tait.jpg

requests应将尾部编码为%3Fpt%3D5%26ek%3D1,但似乎requestsurl的url-encode不执行任何操作,我认为它可能与{{1}匹配模式请求参数,如何在没有模式匹配的情况下使?pt=5&ek=1盲目地转换网址?

更新

服务器获取修剪后的网址并使用它计算签名,因此与我使用原始网址计算的签名不匹配,因此403返回。

3 个答案:

答案 0 :(得分:0)

您可能在构建网址的方式上遇到问题:

>>> payload = {'pt': 5, 'ek': '1'}
>>> r = requests.get('http://host/bucket_name/file_name', params=payload)

如果你打印(r.url),你应该有正确的表格。

答案 1 :(得分:0)

为什么requests会假设对查询参数进行编码?它不知道您不希望将该部分URL视为查询字符串。此外,请求按原样发送到服务器,查询字符串不会如您所建议的那样省略。您可以使用nc验证该内容:

# run nc server
$ nc -l 1234

# then send request from Python
>>> requests.put('http://localhost:1234/path/to/216169_1286900924tait.jpg?pt=5&ek=1', data='any old thing')

nc将显示请求:

PUT /path/to/216169_1286900924tait.jpg?pt=5&ek=1 HTTP/1.1
Host: localhost:1234
Content-Length: 13
User-Agent: python-requests/2.9.1
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate

any old thing

因此远程服务器(正确地根据HTTP协议)将文件名的?pt=5&ek=1部分解释为查询参数。它还应该做什么?

为了进行比较,我认为它之前曾与httplib.HTTPConnection合作:

>>> import httplib
>>> r = httplib.HTTPConnection('localhost', 1234)
>>> r.request('PUT', '/path/to/216169_1286900924tait.jpg?pt=5&ek=1', 'hello from httplib')

生成此请求:

PUT /path/to/216169_1286900924tait.jpg?pt=5&ek=1 HTTP/1.1
Host: localhost:1234
Accept-Encoding: identity
Content-Length: 18

hello from httplib

请注意,发送网址的方式没有区别。

答案 2 :(得分:0)

我深入研究了requests源代码,我找到了以下代码行(是的,requests基于urllib3):

scheme, auth, host, port, path, query, fragment = urllib3.util.parse_url(url)

看起来,您应该在构建网址字符串时手动对网址进行网址编码,例如:

>>> path = '''~!@#$^&*()_+|}{":?><`-=\\][\';.,'''
>>> url = 'http://host.com/bucket/%s' % path
>>> urllib3.util.parse_url(url)
>>> Url(scheme='http', auth=None, host='host.com', port=None, path='/bucket/~!@', query=None, fragment='$^&*()_+|}{":?><`-=B%7C%7D%7B%22%3A%3F%3E%3C%60-%3D%5C%5D%5B%27%3B.%2C')

如果您对path进行编码,请注意path字段输出path相同:

>>> path = '''~!@#$^&*()_+|}{":?><`-=\\][\';.,'''
>>> url = 'http://host.com/bucket/%s' % (urllib.quote(path, ''))
>>> print url
>>> http://host.com/bucket/%7E%21%40%23%24%25%5E%26%2A%28%29_%2B%7C%7D%7B%22%3A%3F%3E%3C%60-%3D%5C%5D%5B%27%3B.%2C
>>> urllib3.util.parse_url(url)
>>> Url(scheme='http', auth=None, host='host.com', port=None, path='/bucket/%7E%21%40%23%24%25%5E%26%2A%28%29_%2B%7C%7D%7B%22%3A%3F%3E%3C%60-%3D%5C%5D%5B%27%3B.%2C', query=None, fragment=None)

这就是我想要的。但是如果你想将unicode字符传递给path,你不需要对它们进行编码,它们会自动转换为%xx%xx格式。但是对于你传入URL的任何字符,url encode都是一个很好的建议。