如何使用python请求执行限时响应下载?

如何使用python请求执行限时响应下载?,python,python-requests,urllib3,Python,Python Requests,Urllib3,当用python下载一个大文件时,我不仅要为连接过程设置时间限制,还要为下载设置时间限制 我正在尝试使用以下python代码: import requests r = requests.get('http://ipv4.download.thinkbroadband.com/1GB.zip', timeout = 0.5, prefetch = False) print r.headers['content-length'] print len(r.raw.read()) 这不起作用(下

当用python下载一个大文件时,我不仅要为连接过程设置时间限制,还要为下载设置时间限制

我正在尝试使用以下python代码:

import requests

r = requests.get('http://ipv4.download.thinkbroadband.com/1GB.zip', timeout = 0.5, prefetch = False)

print r.headers['content-length']

print len(r.raw.read())
这不起作用(下载不受时间限制),正如文档中正确指出的:

如果可能的话,这将非常好:

r.raw.read(timeout = 10)

问题是,如何对下载设置时间限制?

在一个线程中运行下载,如果没有按时完成,则可以中止下载

import requests
import threading

URL='http://ipv4.download.thinkbroadband.com/1GB.zip'
TIMEOUT=0.5

def download(return_value):
    return_value.append(requests.get(URL))

return_value = []
download_thread = threading.Thread(target=download, args=(return_value,))
download_thread.start()
download_thread.join(TIMEOUT)

if download_thread.is_alive():
    print 'The download was not finished on time...'
else:
    print return_value[0].headers['content-length']

当使用Requests'
prefetch=False
参数时,您可以一次(而不是一次)拉入任意大小的响应块

您需要做的是告诉请求不要预加载整个请求,并保留您自己的时间来记录到目前为止的阅读量,同时一次获取小块内容。您可以使用
r.raw.read(chunk\u SIZE)
获取块。总的来说,代码如下所示:

import requests
import time

CHUNK_SIZE = 2**12  # Bytes
TIME_EXPIRE = time.time() + 5  # Seconds

r = requests.get('http://ipv4.download.thinkbroadband.com/1GB.zip', prefetch=False)

data = ''
buffer = r.raw.read(CHUNK_SIZE)
while buffer:
    data += buffer
    buffer = r.raw.read(CHUNK_SIZE)

    if TIME_EXPIRE < time.time():
        # Quit after 5 seconds.
        data += buffer
        break

r.raw.release_conn()

print "Read %s bytes out of %s expected." % (len(data), r.headers['content-length'])
导入请求
导入时间
块大小=2**12字节
TIME_EXPIRE=TIME.TIME()+5秒
r=请求。获取('http://ipv4.download.thinkbroadband.com/1GB.zip,预取=False)
数据=“”
缓冲区=r.raw.read(块大小)
而缓冲区:
数据+=缓冲区
缓冲区=r.raw.read(块大小)
如果时间过期

请注意,这有时可能会比分配给最终
r.raw的5秒多一点。读取(…)
可能会延迟任意时间。但至少它不依赖于多线程或套接字超时。答案是:不要使用请求,因为它是阻塞的。使用非阻塞网络I/O,例如eventlet:

import eventlet
from eventlet.green import urllib2
from eventlet.timeout import Timeout

url5 = 'http://ipv4.download.thinkbroadband.com/5MB.zip'
url10 = 'http://ipv4.download.thinkbroadband.com/10MB.zip'

urls = [url5, url5, url10, url10, url10, url5, url5]

def fetch(url):
    response = bytearray()
    with Timeout(60, False):
        response = urllib2.urlopen(url).read()
    return url, len(response)

pool = eventlet.GreenPool()
for url, length in pool.imap(fetch, urls):
    if (not length):
        print "%s: timeout!" % (url)
    else:
        print "%s: %s" % (url, length)
产生预期结果:

http://ipv4.download.thinkbroadband.com/5MB.zip: 5242880
http://ipv4.download.thinkbroadband.com/5MB.zip: 5242880
http://ipv4.download.thinkbroadband.com/10MB.zip: timeout!
http://ipv4.download.thinkbroadband.com/10MB.zip: timeout!
http://ipv4.download.thinkbroadband.com/10MB.zip: timeout!
http://ipv4.download.thinkbroadband.com/5MB.zip: 5242880
http://ipv4.download.thinkbroadband.com/5MB.zip: 5242880

我并不主张这是最好的解决方案,但这里有一个通用的解决方案,用于对使用信号的函数调用设置时间限制:。这是一个难题,我不建议使用它,除非没有更优雅的解决方案。是的,信号不是一个选项,因为这不是一条安全的道路。用python线程是有问题的,而且我不能在超时时终止线程,这不是一个干净的解决方案。如果您愿意,可以用进程替换线程。为什么不能终止线程?“在python和任何语言中,突然终止线程通常是一种不好的模式。”无法告诉线程停止。使用进程太复杂,需要进程间通信。在python中,线程实际上无法停止。它们可以用
stop
方法标记为stopped,但实际上它们会在后台继续运行。不幸的是,这不起作用,因为不仅最后一次,甚至每一次r.raw.read(…)都可能延迟任意时间。这通常会导致从任意URL下载时错过超时。然后听起来套接字超时是唯一的方法。您看到了吗?使用此代码,超时触发时会发生什么情况?:)关于套接字的状态,您有什么保证?好的,这里没有线程,操作仍然是并行运行的。当超时触发时,正在进行的非阻塞操作将被取消。不要杀人。插座是关着的。我希望;)