【问题标题】:multithreaded file download in python and updating in shell with download progress在 python 中下载多线程文件并在 shell 中更新下载进度
【发布时间】:2014-06-14 05:05:48
【问题描述】:

为了学习多线程文件下载,我写了这个小菜一碟:

import urllib2
import os
import sys
import time
import threading

urls = ["http://broadcast.lds.org/churchmusic/MP3/1/2/nowords/271.mp3",
"http://s1.fans.ge/mp3/201109/08/John_Legend_So_High_Remix(fans_ge).mp3",
"http://megaboon.com/common/preview/track/786203.mp3"]

url = urls[1]

def downloadFile(url, saveTo=None):
    file_name = url.split('/')[-1]
    if not saveTo:
        saveTo = '/Users/userName/Desktop'
    try:
        u = urllib2.urlopen(url)
    except urllib2.URLError , er:
        print("%s" % er.reason)
    else:

        f = open(os.path.join(saveTo, file_name), 'wb')
        meta = u.info()
        file_size = int(meta.getheaders("Content-Length")[0])
        print "Downloading: %s Bytes: %s" % (file_name, file_size)
        file_size_dl = 0
        block_sz = 8192
        while True:
            buffer = u.read(block_sz)
            if not buffer:
                break

            file_size_dl += len(buffer)
            f.write(buffer)
            status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
            status = status + chr(8)*(len(status)+1)
            sys.stdout.write('%s\r' % status)
            time.sleep(.2)
            sys.stdout.flush()
            if file_size_dl == file_size:
                print r"Download Completed %s%% for file %s, saved to %s" % (file_size_dl * 100. / file_size, file_name, saveTo,)
        f.close()
        return


def synchronusDownload():
    urls_saveTo = {urls[0]: None, urls[1]: None, urls[2]: None}
    for url, saveTo in urls_saveTo.iteritems():
        th = threading.Thread(target=downloadFile, args=(url, saveTo), name="%s_Download_Thread" % os.path.basename(url))
        th.start()

synchronusDownload()

但似乎对于第二次下载的启动,它会等待第一个线程,然后去下载下一个文件,就像在 shell 中打印的那样。

我的计划是同时开始所有下载并打印下载文件的更新进度。

任何帮助将不胜感激。 谢谢。

【问题讨论】:

    标签: python multithreading download urllib2


    【解决方案1】:

    这是一个常见问题,以下是通常采取的步骤:

    1.) 使用 Queue.Queue 创建一个包含您要访问的所有 url 的队列。

    2.) 创建一个继承自 threading.Thread 的类。它应该有一个 run 方法,从队列中抓取一个 url 并获取数据。

    3.) 根据您的类创建一个线程池作为“工作者”

    4.) 在 queue.join() 完成之前不要退出程序

    【讨论】:

    • 其实我也发现了一个很好的例子:ibm.com/developerworks/aix/library/au-threadingpython
    • 这通常是个好建议,但不能解决他的问题。如果使用这些提示,他仍然会看到相同的行为。
    • 听起来当一个线程启动时sys.stdout.write('%s\r' % status) sys.stdout.flush() 不会补偿执行每个线程旁边的每个线程,即使对于终端中的单独线程也是如此?
    • @RohitJ : '在完成queue.join() 之前不要退出程序' 如果队列中的每个线程都持有大量数据,这不会影响系统性能吗?
    【解决方案2】:

    您的函数实际上是并行运行的。您可以通过在每个函数开始时打印来验证这一点 - 一旦您的程序启动,将打印 3 个输出。

    发生的情况是您的前两个文件太小,以至于在调度程序切换线程之前它们已被完全下载。尝试在列表中设置更大的文件:

    urls = [
    "http://www.wswd.net/testdownloadfiles/50MB.zip",
    "http://www.wswd.net/testdownloadfiles/20MB.zip",
    "http://www.wswd.net/testdownloadfiles/100MB.zip",
    ]
    

    程序输出:

    Downloading: 100MB.zip Bytes: 104857600
    Downloading: 20MB.zip Bytes: 20971520
    Downloading: 50MB.zip Bytes: 52428800
    Download Completed 100.0% for file 20MB.zip, saved to .
    Download Completed 100.0% for file 50MB.zip, saved to .
    Download Completed 100.0% for file 100MB.zip, saved to .
    

    【讨论】:

    • 我在函数 synchronousDownload 的 for 循环中添加了这一行 print "Downloading %s and saving to %s" % (os.path.basename(url), saveTo) 并得到了你的意思,但我期待如果所有执行 simulatanuesly 然后 shell 应该几乎同时开始更新每个线程.. .
    • 每个线程同时开始(也同时开始下载)
    • 但这无助于 sys.stdout.write('%s\r' % status) sys.stdout.flush() 同时在 shell 中转储同步更新信息,或者它会不会?
    • sys.stdout.write('%s\r' % status) 正在工作。如果您仔细观察,百分比在 3 次下载之间交替出现,因此“完成百分比”值在线程之间切换时似乎是随机上下的。这可能不是您想要的,但您不能从 3 个线程写入终端并期望得到一致的结果。
    • 是的,我就是这么想的,也许我可以使用curses 或在用户界面中使用Qt 进行操作
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多