pythonでプログレスバーを表示させながらダウンロードする
バイナリファイルをオンメモリで一度に処理するのではなくバッファ単位で少しずつ処理するコードのサンプル作ってみた。ついでにプログレスバーで表示。
使い方:
入力:ファイルのurlリストをプログラムの引数で渡すか、標準入力から1行ごとに入力するかどちらか。
出力:プログレスバーを表示しながらファイルをダウンロードする
処理を簡単にするために、サーバは適切にContent-Lengthをレスポンスヘッダにつけると仮定してる。
# coding: utf-8 # downloader.py # sample code for displaying progress bar while downloading file on network # import sys import os import itertools import urllib class ProgressBar: """displaying a simple progress bar""" def __init__(self, whole, barsize=30, ch="#", nch=" ", message=""): self.whole = whole self.barsize = barsize self.ch = ch self.nch = nch self.message = message def update(self, current): done = float(current) / self.whole chs = int(done * self.barsize) bar = [i <= chs and self.ch or self.nch for i in xrange(self.barsize)] bar.insert(0, "[") bar.append("] %d%%" % int(done * 100.)) bar.append("\r") bar.insert(0, self.message + " : ") sys.stdout.write("".join(bar)) sys.stdout.flush() def terminate(self): sys.stdout.write("\n") sys.stdout.flush() class ProgressBarDownloader(ProgressBar): """download file on network with displaying progress bar""" BUFSIZE = 1024*100 def __init__(self, fileurl): self.conn = urllib.urlopen(fileurl) filename = fileurl[fileurl.rfind("/")+1:].split("?")[0] if os.path.exists(filename): for i in itertools.count(1): if not os.path.exists(filename + "." + str(i)): filename = filename + "." + str(i) break if filename == "": filename = "default" # fetch content-length header self.contentLength = int(self.conn.headers["content-length"]) message = filename if len(message) > 18: message = "".join([message[:10], " .. " , os.path.splitext(filename)[1]]) message = message.ljust(20) ProgressBar.__init__(self, self.contentLength, message=message) sys.stdout.flush() # create file to be written self.file = open(filename, "wb") def start(self): self.update(0) acc = 0 while True: buffer = self.conn.read(ProgressBarDownloader.BUFSIZE) if not buffer: break acc += len(buffer) self.file.write(buffer) self.update(acc) self.terminate() self.conn.close() self.file.close() def main(): urls = map(lambda s: s.strip(), sys.stdin.readlines()) if sys.argv.__len__() >= 2: urls = sys.argv[1:] # UA変更 urllib.FancyURLopener.version = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1) Gecko/20090624 Firefox/3.5" for url in urls: ProgressBarDownloader(url).start() if __name__ == '__main__': main()
入力ファイル例:
下のテキストをファイル名「in」で保存
http://upload.wikimedia.org/wikipedia/commons/thumb/8/82/CharlesBabbage.jpg/200px-CharlesBabbage.jpg http://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Alan_Turing_Memorial_Closer.jpg/180px-Alan_Turing_Memorial_Closer.jpg http://v-t.jp/jp/topics/column/img/shima.jpg http://upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Larry_Wall_YAPC_2007.jpg/180px-Larry_Wall_YAPC_2007.jpg http://upload.wikimedia.org/wikipedia/commons/f/f7/Richard_Matthew_Stallman.jpeg http://upload.wikimedia.org/wikipedia/commons/thumb/6/69/Linus_Torvalds.jpeg/391px-Linus_Torvalds.jpeg http://upload.wikimedia.org/wikipedia/commons/0/00/James_Gosling_2005.jpg http://upload.wikimedia.org/wikipedia/commons/e/e3/Paulgraham_240x320.jpg http://japanese.joelonsoftware.com/Images/TinyJoel.jpg http://upload.wikimedia.org/wikipedia/commons/thumb/2/26/Donald_Knuth_DSC00624.jpg/180px-Donald_Knuth_DSC00624.jpg http://jibun.atmarkit.co.jp/ljibun01/rensai/genius/01/images/genius01_01.jpg
実行結果
$ python downloader.py < in 200px-Char .. .jpg : [##############################] 100% 180px-Alan .. .jpg : [##############################] 100% shima.jpg : [##############################] 100% 180px-Larr .. .jpg : [##############################] 100% Richard_Ma .. .jpeg : [##############################] 100% 391px-Linu .. .jpeg : [##############################] 100% James_Gosl .. .jpg : [##############################] 100% Paulgraham .. .jpg : [##############################] 100% TinyJoel.jpg : [##############################] 100% 180px-Dona .. .jpg : [##############################] 100% genius01_01.jpg : [##############################] 100%