在Requests 2.3.0中避免空块的ChunkedEncodingError

13 投票
1 回答
7081 浏览
提问于 2025-04-18 12:45

我正在使用Requests库从服务器下载一个几GB的文件。为了能够显示下载进度(并且为了不把整个文件都放在内存里),我设置了stream=True,并把下载的内容写入一个文件:

with open('output', 'w') as f:
    response = requests.get(url, stream=True)

    if not response.ok:
        print 'There was an error'
        exit()

    for block in response.iter_content(1024 * 100):
        f.write(block)
        completed_bytes += len(block)
        write_progress(completed_bytes, total_bytes)

但是,在下载的某个随机时刻,Requests会抛出一个ChunkedEncodingError错误。我查看了源代码,发现这个错误对应于一个IncompleteRead异常。我在相关代码周围加了一个日志语句,发现e.partial = "\r"。我知道服务器对下载的优先级很低,我怀疑这个异常是在服务器等待发送下一个数据块时等得太久导致的。

正如预期的那样,这个异常会中断下载。不幸的是,服务器并没有实现HTTP/1.1的内容范围功能,所以我不能简单地恢复下载。我尝试过增加urllib3的内部超时时间,但这个异常依然存在。

有没有办法让底层的urllib3(或者Requests)对这些空的(或者延迟的)数据块更加宽容,以便文件能够完整下载呢?

1 个回答

1
import httplib

def patch_http_response_read(func):
    def inner(*args):
        try:
            return func(*args)
        except httplib.IncompleteRead, e:
            return e.partial
    return inner

httplib.HTTPResponse.read = patch_http_response_read(httplib.HTTPResponse.read)

我现在无法重现你遇到的问题,但我觉得这可能是一个解决办法。这个办法可以帮助你处理那些有问题的http服务器。

大多数有问题的服务器会传输所有数据,但由于实现上的错误,它们会错误地关闭连接,这样httplib就会报错,导致你宝贵的数据被丢失。

撰写回答