关于python:带有超时、最大大小和连接池的HTTP请求

http request with timeout, maximum size and connection pooling

我正在寻找一种在Python(2.7)中执行HTTP请求的方法,其中有3个要求:

  • 超时(为了可靠性)
  • 内容最大大小(用于安全)
  • 连接池(用于性能)

我已经检查了相当多的PythonHTTP库,但它们都不满足我的要求。例如:

很好,但不合用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import urllib2
import json

r = urllib2.urlopen('https://github.com/timeline.json', timeout=5)
content = r.read(100+1)
if len(content) > 100:
    print 'too large'
    r.close()
else:
    print json.loads(content)

r = urllib2.urlopen('https://github.com/timeline.json', timeout=5)
content = r.read(100000+1)
if len(content) > 100000:
    print 'too large'
    r.close()
else:
    print json.loads(content)

请求:无最大大小

1
2
3
4
5
6
7
import requests
r = requests.get('https://github.com/timeline.json', timeout=5, stream=True)
r.headers['content-length'] # does not exists for this request, and not safe
content = r.raw.read(100000+1)
print content # ARF this is gzipped, so not the real size
print json.loads(content) # content is gzipped so pretty useless
print r.json() # Does not work anymore since raw.read was used

Urllib3:从来没有"read"方法起作用,即使有一个50mo的文件……

httplib:httplib.httpConnection不是池(只有一个连接)

我简直不敢相信URLLIB2是我能使用的最好的HTTP库!所以如果有人知道天秤座可以做什么,或者如何使用以前的天秤座…

编辑:

我找到的最好的解决方案要归功于Martijn Pieters(即使对于大文件,str加法也不会减慢速度)。

1
2
3
4
5
6
7
8
9
10
11
12
13
r = requests.get('https://github.com/timeline.json', stream=True)
size = 0
ctt = StringIO()


for chunk in r.iter_content(2048):
    size += len(chunk)
    ctt.write(chunk)
    if size > maxsize:
        r.close()
        raise ValueError('Response too large')

content = ctt.getvalue()


您可以使用requests来完成这项工作,但您需要知道raw对象是urllib3勇气的一部分,并利用HTTPResponse.read()调用支持的额外参数,该参数允许您指定要读取解码数据:

1
2
3
4
5
6
7
8
import requests
r = requests.get('https://github.com/timeline.json', timeout=5, stream=True)

content = r.raw.read(100000+1, decode_content=True)
if len(content) > 100000:
    raise ValueError('Too large a response')
print content
print json.loads(content)

或者,您可以在读取之前在raw对象上设置decode_content标志:

1
2
3
4
5
6
7
8
9
import requests
r = requests.get('https://github.com/timeline.json', timeout=5, stream=True)

r.raw.decode_content = True
content = r.raw.read(100000+1)
if len(content) > 100000:
    raise ValueError('Too large a response')
print content
print json.loads(content)

如果您不喜欢使用这样的urllib3胆量,可以使用response.iter_content()对解码后的内容进行分块迭代;这也使用基础HTTPResponse(使用.stream()生成器版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

r = requests.get('https://github.com/timeline.json', timeout=5, stream=True)

maxsize = 100000
content = ''
for chunk in r.iter_content(2048):
    content += chunk
    if len(content) > maxsize:
        r.close()
        raise ValueError('Response too large')

print content
print json.loads(content)

这里处理压缩数据大小的方式有细微的差别;r.raw.read(100000+1)将只读取10万字节的压缩数据;未压缩数据将根据最大大小进行测试。iter_content()方法将读取更多未压缩数据,在很少情况下,压缩流大于未压缩数据。

这两种方法都不允许r.json()工作;response._content属性不是由这些设置的;当然,您可以手工设置。但是,由于.raw.read().iter_content()调用已经允许您访问所讨论的内容,因此确实没有必要。