Python进度栏和下载


72

我有一个python脚本,可启动一个可下载文件的URL。有什么方法可以让python使用命令行显示与启动浏览器相反的下载进度?


8
如果我的回答解决了您的问题,请将其标记为已接受(单击小勾号轮廓)。
— Endophage

Answers:


102

已更新您的示例网址:

我刚刚为此编写了一种超级简单的方法(将其略微修改),以将pdf刮出某个站点。注意,它仅在基于UNIX的系统(Linux,Mac OS)上正常运行,因为Powershell无法处理“ \ r”

import requests

link = "http://indy/abcde1245"
file_name = "download.data"
with open(file_name, "wb") as f:
    print "Downloading %s" % file_name
    response = requests.get(link, stream=True)
    total_length = response.headers.get('content-length')

    if total_length is None: # no content length header
        f.write(response.content)
    else:
        dl = 0
        total_length = int(total_length)
        for data in response.iter_content(chunk_size=4096):
            dl += len(data)
            f.write(data)
            done = int(50 * dl / total_length)
            sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50-done)) )    
            sys.stdout.flush()

它使用了请求库,因此您需要安装它。这会将类似以下内容的内容输出到您的控制台中:

>下载download.data

> [=============]

脚本中进度条的宽度为52个字符(2[]个字符就是进度的50个字符)。每个=代表下载量的2%。


在任何地方都没有定义请求
— 2013年

1
我有同样的问题,什么是pdf?
— user1607549 2013年

2
您可能需要在iter_content中定义chunk_size,这样不会太慢。
— 2015年

2
正如@ 0942v8653所提到的,iter_content()需要一个chunk_size,因此您可以指定它的速度,但是如果要下载的内容足够小以至于它的〜1%可以容纳在内存中,则可以通过执行chunk_size来简化很多代码= total_length / 100,循环的每次迭代
— 将占

1
在Windows上为我工作。也将一行从更改for data in response.iter_content():为for data in response.iter_content(chunk_size=total_length/100):。
— mrgloom

72

您可以使用“ clint ”包(由“ requests”由同一作者编写)将一个简单的进度条添加到您的下载中,如下所示:

from clint.textui import progress

r = requests.get(url, stream=True)
path = '/some/path/for/file.txt'
with open(path, 'wb') as f:
    total_length = int(r.headers.get('content-length'))
    for chunk in progress.bar(r.iter_content(chunk_size=1024), expected_size=(total_length/1024) + 1): 
        if chunk:
            f.write(chunk)
            f.flush()

这将为您提供动态输出,如下所示:

[################################] 5210/5210 - 00:00:01

它也应该在多个平台上工作!您也可以使用.dots和.mill而不是.bar将条形更改为点或微调器。

请享用!


2
如果这可以成为python标准库的一部分,那就太好了。
— 2014年

path是要保存文件的文件名。
— tshrinivasan 2015年

路径=“ filename.ext”
— tshrinivasan 2015年

2
克林特现在已经停产
— MRID

我不可避免地要返回此内容时发表评论-这太好了!
— scubbo

29

带有TQDM的Python 3

这是TQDM文档中建议的技术。

import urllib.request

from tqdm import tqdm


class DownloadProgressBar(tqdm):
    def update_to(self, b=1, bsize=1, tsize=None):
        if tsize is not None:
            self.total = tsize
        self.update(b * bsize - self.n)


def download_url(url, output_path):
    with DownloadProgressBar(unit='B', unit_scale=True,
                             miniters=1, desc=url.split('/')[-1]) as t:
        urllib.request.urlretrieve(url, filename=output_path, reporthook=t.update_to)

1
这是迄今为止最好的。
— 阿米特·卡雷尔

14

我很惊讶没有建议使用tqdm!在此处输入图片说明


41
如果您提供了一些可以在此上下文中使用tqdm的代码,则您的答案将会得到改善。
— SA

5

我认为您也可以使用click,它也有一个不错的进度栏库。

import click
with click.progressbar(length=total_size, label='Downloading files') as bar:
    for file in files:
        download(file)
        bar.update(file.size)

请享用 !


3
@MortenB是吗?我ModuleNotFoundError: No module named 'click'上3.6.1。
— nyuszika7h

这是需要安装的第二方库
— AbdealiJK

1
@AbdealiJK第三方
— Smart Manoj,

5

很抱歉迟到了答案;刚刚更新了tqdm文档:

https://github.com/tqdm/tqdm/#hooks-and-callbacks

使用urllib.urlretrieve和面向对象:

import urllib
from tqdm.auto import tqdm

class TqdmUpTo(tqdm):
    """Provides `update_to(n)` which uses `tqdm.update(delta_n)`."""
    def update_to(self, b=1, bsize=1, tsize=None):
        """
        b  : Blocks transferred so far
        bsize  : Size of each block
        tsize  : Total size
        """
        if tsize is not None:
            self.total = tsize
        self.update(b * bsize - self.n)  # will also set self.n = b * bsize

eg_link = "https://github.com/tqdm/tqdm/releases/download/v4.46.0/tqdm-4.46.0-py2.py3-none-any.whl"
eg_file = eg_link.split('/')[-1]
with TqdmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1,
              desc=eg_file) as t:  # all optional kwargs
    urllib.urlretrieve(
        eg_link, filename=eg_file, reporthook=t.update_to, data=None)
    t.total = t.n

或使用requests.get和文件包装器:

import requests
from tqdm.auto import tqdm

eg_link = "https://github.com/tqdm/tqdm/releases/download/v4.46.0/tqdm-4.46.0-py2.py3-none-any.whl"
eg_file = eg_link.split('/')[-1]
response = requests.get(eg_link, stream=True)
with tqdm.wrapattr(open(eg_file, "wb"), "write", miniters=1,
                   total=int(response.headers.get('content-length', 0)),
                   desc=eg_file) as fout:
    for chunk in response.iter_content(chunk_size=4096):
        fout.write(chunk)

您当然可以混合搭配技术。


5

有一个关于request和tqdm的答案。

import requests
from tqdm import tqdm


def download(url: str, fname: str):
    resp = requests.get(url, stream=True)
    total = int(resp.headers.get('content-length', 0))
    with open(fname, 'wb') as file, tqdm(
        desc=fname,
        total=total,
        unit='iB',
        unit_scale=True,
        unit_divisor=1024,
    ) as bar:
        for data in resp.iter_content(chunk_size=1024):
            size = file.write(data)
            bar.update(size)

要点:https : //gist.github.com/yanqd0/c13ed29e29432e3cf3e7c38467f42f51


1

该tqdm软件包现在包含一个旨在处理这种情况的函数:wrapattr。您只需包装对象的read(或write)属性,然后tqdm即可处理其余部分。这是一个简单的下载功能,可将其与一起使用requests:

def download(url, filename):
    import functools
    import pathlib
    import shutil
    import requests
    import tqdm
    
    r = requests.get(url, stream=True, allow_redirects=True)
    if r.status_code != 200:
        r.raise_for_status()  # Will only raise for 4xx codes, so...
        raise RuntimeError(f"Request to {url} returned status code {r.status_code}")
    file_size = int(r.headers.get('Content-Length', 0))

    path = pathlib.Path(filename).expanduser().resolve()
    path.parent.mkdir(parents=True, exist_ok=True)

    desc = "(Unknown total file size)" if file_size == 0 else ""
    r.raw.read = functools.partial(r.raw.read, decode_content=True)  # Decompress if needed
    with tqdm.tqdm.wrapattr(r.raw, "read", total=file_size, desc=desc) as r_raw:
        with path.open("wb") as f:
            shutil.copyfileobj(r_raw, f)

    return path

0

#ToBeOptimized-基准 如果您想困惑自己的大脑并手工制作逻辑

#定义进度条功能

def print_progressbar(total,current,barsize=60):
    progress=int(current*barsize/total)
    completed= str(int(current*100/total)) + '%'
    print('[' , chr(9608)*progress,' ',completed,'.'*(barsize-progress),'] ',str(i)+'/'+str(total), sep='', end='\r',flush=True)

#示例代码

total= 6000
barsize=60
print_frequency=max(min(total//barsize,100),1)
print("Start Task..",flush=True)
for i in range(1,total+1):
  if i%print_frequency == 0 or i == 1:
    print_progressbar(total,i,barsize)
print("\nFinished",flush=True)

#进度栏快照:

以下几行仅用于说明。在命令提示符下,您将看到单个进度条,其中显示了增量进度。

[ 0%............................................................] 1/6000

[██████████ 16%..................................................] 1000/6000

[████████████████████ 33%........................................] 2000/6000

[██████████████████████████████ 50%..............................] 3000/6000

[████████████████████████████████████████ 66%....................] 4000/6000

[██████████████████████████████████████████████████ 83%..........] 5000/6000

[████████████████████████████████████████████████████████████ 100%] 6000/6000

祝你好运,享受!


谢谢你的功能。只是一点评论。该函数当前依赖于外部变量i才能正常工作。如果将这些i变量替换 为 current函数中的变量,则效果很好。
— jtagle

感谢您的代码审查。根据建议更改了变量
— Himanshu Binjola

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.