为Python 3下载进度条

12

我需要在Python 3中显示文件下载进度。我在Stackoverflow上看到了一些相关话题,但考虑到我是编程初学者,没有人发布完整的示例,只是其中一部分,或者我能够在Python 3上运行的代码片段,对我来说都不好用...

附加信息:

好的,所以我有这个:

from urllib.request import urlopen
import configparser
#checks for files which need to be downloaded
print('    Downloading...')
file = urlopen(file_url)
#progress bar here
output = open('downloaded_file.py','wb')
output.write(file.read())
output.close()
os.system('downloaded_file.py')

脚本通过Python命令行运行。


5
这个问题缺少很多有用的信息:您希望这个进度条放在哪里?在控制台?在网页上?在桌面应用程序中?您是如何下载文件的?等等。 - Alex Couper
Python 2 中的 urllib2 和 Python 3 中的 urllib 差不多是一样的。 - poke
我注意到在Python 3中,urllib.request是Python 2中的urllib2...如果我错了,请纠正我... - Mirac7
1
@Cœur 这个链接对于需要使用Python 2的人很有用。如果您愿意,可以将链接移动到评论中。Python 3代码可以改进(因此它不适用于防止未来的答案)。 - jfs
相关链接:Python urllib2 进度钩子 - Cœur
2个回答

25

有一个urlretrieve()函数,可以将URL下载到文件,并允许指定一个回调函数来报告进度:

#!/usr/bin/env python3
import sys
from urllib.request import urlretrieve

def reporthook(blocknum, blocksize, totalsize):
    readsofar = blocknum * blocksize
    if totalsize > 0:
        percent = readsofar * 1e2 / totalsize
        s = "\r%5.1f%% %*d / %d" % (
            percent, len(str(totalsize)), readsofar, totalsize)
        sys.stderr.write(s)
        if readsofar >= totalsize: # near the end
            sys.stderr.write("\n")
    else: # total size is unknown
        sys.stderr.write("read %d\n" % (readsofar,))

urlretrieve(url, 'downloaded_file.py', reporthook)

这是一个图形用户界面进度条:

import sys
from threading import Event, Thread
from tkinter import Tk, ttk
from urllib.request import urlretrieve

def download(url, filename):
    root = progressbar = quit_id = None
    ready = Event()
    def reporthook(blocknum, blocksize, totalsize):
        nonlocal quit_id
        if blocknum == 0: # started downloading
            def guiloop():
                nonlocal root, progressbar
                root = Tk()
                root.withdraw() # hide
                progressbar = ttk.Progressbar(root, length=400)
                progressbar.grid()
                # show progress bar if the download takes more than .5 seconds
                root.after(500, root.deiconify)
                ready.set() # gui is ready
                root.mainloop()
            Thread(target=guiloop).start()
        ready.wait(1) # wait until gui is ready
        percent = blocknum * blocksize * 1e2 / totalsize # assume totalsize > 0
        if quit_id is None:
            root.title('%%%.0f %s' % (percent, filename,))
            progressbar['value'] = percent # report progress
            if percent >= 100:  # finishing download
                quit_id = root.after(0, root.destroy) # close GUI

    return urlretrieve(url, filename, reporthook)

download(url, 'downloaded_file.py')

在 Python 3.3 中,urlretrieve()reporthook接口发生了变化(参见问题 16409)。为了解决这个问题,您可以通过FancyURLopener访问先前的接口:

from urllib.request import FancyURLopener
urlretrieve = FancyURLopener().retrieve

要在同一线程中更新进度条,您可以内联urlretrieve()代码:

from tkinter import Tk, ttk
from urllib.request import urlopen

def download2(url, filename):
    response = urlopen(url)
    totalsize = int(response.headers['Content-Length']) # assume correct header
    outputfile = open(filename, 'wb')

    def download_chunk(readsofar=0, chunksize=1 << 13):
        # report progress
        percent = readsofar * 1e2 / totalsize # assume totalsize > 0
        root.title('%%%.0f %s' % (percent, filename,))
        progressbar['value'] = percent

        # download chunk
        data = response.read(chunksize)
        if not data: # finished downloading
            outputfile.close()
            root.destroy() # close GUI
        else:
            outputfile.write(data) # save to filename
            # schedule to download the next chunk
            root.after(0, download_chunk, readsofar + len(data), chunksize)

    # setup GUI to show progress
    root = Tk()
    root.withdraw() # hide
    progressbar = ttk.Progressbar(root, length=400)
    progressbar.grid()
    # show progress bar if the download takes more than .5 seconds
    root.after(500, root.deiconify)
    root.after(0, download_chunk)
    root.mainloop()

download2(url, 'downloaded_file.py')

@Mirac7: 我已添加一个变量,它显示GUI并从同一线程中下载文件。 - jfs
1
好的完整答案 +1 - doru
@J.F.Sebastian有可能在reporthook中包含下载速度的方法吗? - SvbZ3r0
@J.F.Sebastian 傻我了!!我没想到那个。非常感谢你! - SvbZ3r0
1
@GughanRavikumar 它可能看起来像这样:t = time.monotonic(); speed = blocksize / (t - last[0]); average_speed = readsofar / (t - start); last[0] = t - jfs
显示剩余2条评论

2

我认为这段代码可以帮助你。虽然我不确定它是否完全符合你的要求,但至少它应该能给你一些工作的方向。

import tkinter 
from tkinter import ttk
from urllib.request import urlopen


def download(event):
    file = urlopen('http://www.python.org/')
    output = open('downloaded_file.txt', 'wb')
    lines= file.readlines()
    i = len(lines)

    for line in lines:
        output.write(line)
        pbar.step(100/i)

    output.close()
    file.close()




root = tkinter.Tk()
root.title('Download bar')

pbar = ttk.Progressbar(root, length=300)
pbar.pack(padx=5, pady=5)

btn = tkinter.Button(root, text="Download")
# bind to left mouse button click
btn.bind("<Button-1>", download)
btn.pack(pady=10)

root.mainloop()

这个可以,我试过了。

已经尝试过了...它运行得很好,但是当我点击下载时,它只是创建一个空脚本,并且进度条为空。 - Mirac7
1
@doru 为了避免打开URL两次,为什么不将file.readlines()赋值给一个变量呢? - Benjamin Hodgson
你能告诉我如何通过进度条显示百分比进度吗? - Mirac7
我不确定是否可行,但我会调查并告诉你。 - doru
@doru:你可以像urlretrieve()一样从Content-Length头获取近似的文件大小。这将允许你显示百分比进度。 - jfs
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接