PycURL附件和进度函数

时间:2013-11-01 09:41:09

标签: python curl pycurl

使用您发送请求的API处理一个小项目,然后它会返回一个附有zip文件的响应,然后您下载该文件。我自动执行此下载的第一步尝试使用setopt(curl.WRITEDATA,fp)函数,但每次尝试时都会使我的Python脚本崩溃。然后我更改了大头钉并使用WRITEFUNCTION将数据写入缓冲区,然后将其写入一个工作正常的文件。

这一切都很好但后来我想添加一个进度条来查看已经下载了多少文件并提供了一些用户反馈等等。这就是事情开始变得奇怪的地方,因为现在进度条达到了100%在一秒钟内,zip文件尚未完成下载。当我改变我的进度函数只打印它正在下载的文件的大小时,它会报告几个100字节的数量级(比zip文件小很多)。无论如何使用pycurl中的函数(和下面的curl)来跟踪附件下载的进度而不是请求本身?

此外,如果有人可以帮助解决可能有用的WRITEDATA问题,我想这两个问题可能会有关。

2 个答案:

答案 0 :(得分:3)

以下代码将使用pycurl下载文件并显示当前进度(以文本形式显示):

import pycurl
# for displaying the output text
from sys import stderr as STREAM

# replace with your own url and path variables
url = "http://ovh.net/files/100Mb.dat"
path = 'test_file.dat'

# use kiB's
kb = 1024

# callback function for c.XFERINFOFUNCTION
def status(download_t, download_d, upload_t, upload_d):
    STREAM.write('Downloading: {}/{} kiB ({}%)\r'.format(
        str(int(download_d/kb)),
        str(int(download_t/kb)),
        str(int(download_d/download_t*100) if download_t > 0 else 0)
    ))
    STREAM.flush()

# download file using pycurl
with open(path, 'wb') as f:
    c = pycurl.Curl()
    c.setopt(c.URL, url)
    c.setopt(c.WRITEDATA, f)
    # display progress
    c.setopt(c.NOPROGRESS, False)
    c.setopt(c.XFERINFOFUNCTION, status)
    c.perform()
    c.close()

# keeps progress onscreen after download completes
print()

输出应如下所示:

Downloading: 43563/122070 kiB (35%)

如果您想使用实际的进度条,也可以这样做。但是,这需要更多的工作。

以下代码使用tqdm包生成进度条。它在下载文件时实时更新,并显示下载速度和估计的剩余时间。由于tqdm工作方式的限制,因此还需要requests软件包。这也与total_dl_d变量是数组而不是整数的原因有关。

import pycurl
# needed to predict total file size
import requests
# progress bar
from tqdm import tqdm

# replace with your own url and path variables
url = "http://ovh.net/files/10Mb.dat"
path = 'test_file.dat'

# show progress % and amount in bytes
r = requests.get(url)
total_size = int(r.headers.get('content-length', 0))
block_size = 1024

# create a progress bar and update it manually
with tqdm(total=total_size, unit='iB', unit_scale=True) as pbar:
    # store dotal dl's in an array (arrays work by reference)
    total_dl_d = [0]
    def status(download_t, download_d, upload_t, upload_d, total=total_dl_d):
        # increment the progress bar
        pbar.update(download_d - total[0])
        # update the total dl'd amount
        total[0] = download_d

    # download file using pycurl
    with open(path, 'wb') as f:
        c = pycurl.Curl()
        c.setopt(c.URL, url)
        c.setopt(c.WRITEDATA, f)
        # follow redirects:
        c.setopt(c.FOLLOWLOCATION, True)
        # custom progress bar
        c.setopt(c.NOPROGRESS, False)
        c.setopt(c.XFERINFOFUNCTION, status)
        c.perform()
        c.close()

说明所描述问题的可能原因:

(问题中没有提供任何代码,因此我不得不猜测到底是什么引起了上述问题...)

基于变量名(fp,即file_path)...
文件写入(WRITEDATA)问题可能是由于提供了文件路径(str)而不是文件对象(io.BufferedWriter)。

根据我的经验...
在文件下载期间反复调用XFERINFOFUNCTION回调。回调仅提供文件总大小和已下载为参数的总大小。自上次调用以来,它不计算增量(差异)。进度条描述的问题(“进度条在一秒钟内达到100%,并且zip文件尚未完成下载”)可能是由于 total 总数(已下载)当期望增加的金额时,用作update的金额。 如果进度条每次都按总计量进行递增,则它不会反映实际的下载量。显示更大的数量。然后,它将超过100%,并具有各种故障。


来源:

答案 1 :(得分:0)

对于@Elliot G. 的 tqdm 答案,我们应该只获取标题而不是像这样的整个正文;

# show progress % and amount in bytes
r = requests.head(url)   # <==============================
total_size = int(r.headers.get('content-length', 0))
block_size = 1024

由于缺乏声誉,我无法对 @Elliot G. 的回答发表评论,所以我发布了这个。