Пишу небольшое приложение для загрузки файлов по http (как, например, описанное здесь).
Я также хочу включить небольшой индикатор прогресса загрузки, показывающий процент процесса загрузки.
Вот что я придумал:
sys.stdout.write(rem_file + "...")
urllib.urlretrieve(rem_file, loc_file, reporthook=dlProgress)
def dlProgress(count, blockSize, totalSize):
percent = int(count*blockSize*100/totalSize)
sys.stdout.write("%2d%%" % percent)
sys.stdout.write("\b\b\b")
sys.stdout.flush()
Вывод: MyFileName ... 9%
Есть ли другие идеи или рекомендации по этому поводу?
Одна вещь, которая несколько раздражает, - это мигающий курсор в терминале на первой цифре процента. Есть ли способ предотвратить это? Есть ли способ скрыть курсор?
Обновлено:
Вот лучшая альтернатива с использованием глобальной переменной для имени файла в dlProgress и кода '\ r':
global rem_file # global variable to be used in dlProgress
urllib.urlretrieve(rem_file, loc_file, reporthook=dlProgress)
def dlProgress(count, blockSize, totalSize):
percent = int(count*blockSize*100/totalSize)
sys.stdout.write("\r" + rem_file + "...%d%%" % percent)
sys.stdout.flush()
Вывод: MyFileName ... 9%
И курсор появляется в КОНЦЕ строки. Намного лучше.
Вы также можете сделать / r + flush () на стандартном выходе. Я просто предполагаю, что это работает в Windows на основе упомянутого вами поведения терминала.
связанные stackoverflow.com/questions/15644964/…






Если вы используете пакет curses, у вас будет гораздо больший контроль над консолью. Это также связано с более высокими затратами на сложность кода и, вероятно, не требуется, если вы не разрабатываете большое консольное приложение.
В качестве простого решения вы всегда можете поместить вращающееся колесо в конец сообщения о состоянии (последовательность символов |, \, -, /, которая действительно хорошо выглядит под мигающим курсором.
Вы также можете попробовать:
sys.stdout.write("\r%2d%%" % percent)
sys.stdout.flush()
Использование одного возврата каретки в начале строки, а не нескольких пробелов. Ваш курсор по-прежнему будет мигать, но он будет мигать после знака процента, а не под первой цифрой, и с одним управляющим символом вместо трех вы можете получить меньше мерцания.
В http://pypi.python.org/pypi/progressbar/2.2 есть библиотека текстовых индикаторов выполнения для python, которая может оказаться вам полезной:
This library provides a text mode progressbar. This is tipically used to display the progress of a long running operation, providing a visual clue that processing is underway.
The ProgressBar class manages the progress, and the format of the line is given by a number of widgets. A widget is an object that may display diferently depending on the state of the progress. There are three types of widget: - a string, which always shows itself; - a ProgressBarWidget, which may return a diferent value every time it's update method is called; and - a ProgressBarWidgetHFill, which is like ProgressBarWidget, except it expands to fill the remaining width of the line.
The progressbar module is very easy to use, yet very powerful. And automatically supports features like auto-resizing when available.
как иметь несколько индикаторов выполнения в терминале, если несколько загрузок указаны в отдельных потоках?
Для небольших файлов вам могут понадобиться следующие строки, чтобы избежать сумасшедших процентов:
sys.stdout.write ("\ r% 2d %%"% процентов)
sys.stdout.flush ()
Ваше здоровье
Вот как я это сделал, это может вам помочь: https://github.com/mouuff/MouDownloader/blob/master/api/download.py
Я использовал этот код:
url = (<file location>)
file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(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)
print status,
f.close()
Поздно на вечеринку, как обычно. Вот реализация, которая поддерживает отчеты о прогрессе, например, ядро urlretrieve:
import urllib2
def urlretrieve(urllib2_request, filepath, reporthook=None, chunk_size=4096):
req = urllib2.urlopen(urllib2_request)
if reporthook:
# ensure progress method is callable
if hasattr(reporthook, '__call__'):
reporthook = None
try:
# get response length
total_size = req.info().getheaders('Content-Length')[0]
except KeyError:
reporthook = None
data = ''
num_blocks = 0
with open(filepath, 'w') as f:
while True:
data = req.read(chunk_size)
num_blocks += 1
if reporthook:
# report progress
reporthook(num_blocks, chunk_size, total_size)
if not data:
break
f.write(data)
# return downloaded length
return len(data)
Как бы то ни было, вот код, который я использовал, чтобы заставить его работать:
from urllib import urlretrieve
from progressbar import ProgressBar, Percentage, Bar
url = "http://......."
fileName = "file"
pbar = ProgressBar(widgets=[Percentage(), Bar()])
urlretrieve(url, fileName, reporthook=dlProgress)
def dlProgress(count, blockSize, totalSize):
pbar.update( int(count * blockSize * 100 / totalSize) )
Это дает мне только: pbar.update (int (count * blockize * 100 / totalSize)) NameError: глобальное имя 'blockize' не определено
это была опечатка. Это должен был быть входной параметр. Я обновил ответ, чтобы отразить это. Спасибо!
def download_progress_hook(count, blockSize, totalSize):
"""A hook to report the progress of a download. This is mostly intended for users with slow internet connections. Reports every 5% change in download progress.
"""
global last_percent_reported
percent = int(count * blockSize * 100 / totalSize)
if last_percent_reported != percent:
if percent % 5 == 0:
sys.stdout.write("%s%%" % percent)
sys.stdout.flush()
else:
sys.stdout.write(".")
sys.stdout.flush()
last_percent_reported = percent
urlretrieve(url, filename, reporthook=download_progress_hook)
global rem_fileимеет смысл только внутри функции, где вы привязываете его к новому объектуrem_file = ..., в противном случае (если вы только читаете его значение)global rem_fileне нужен.