Cómo escribir un indicador de progreso de descarga en Python?


Estoy escribiendo una pequeña aplicación para descargar archivos a través de http (como, por ejemplo, se describe aquí).

También quiero incluir un pequeño indicador de progreso de descarga que muestre el porcentaje del progreso de descarga.

Esto es lo que se me ocurrió:

    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()

Salida: Mi nombre de archivo... 9%

¿Alguna otra idea o recomendación para hacer esto?

Una cosa que es algo molesto es el cursor parpadeante en el terminal en el primer dígito de la porcentaje. ¿Hay alguna manera de evitar esto? ¿Hay alguna forma de ocultar el cursor?

EDITAR:

Aquí una mejor alternativa usando una variable global para el nombre de archivo en dlProgress y el código' \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()

Salida: Mi nombre de archivo...9%

Y el cursor aparece al FINAL de la línea. Mucho mejor.

 50
Author: Community, 2008-09-09

9 answers

Hay una biblioteca de barras de progreso de texto para python en http://pypi.python.org/pypi/progressbar/2.2 que usted podría encontrar útil:

Esta biblioteca proporciona una barra de progreso en modo texto. Esto se usa típicamente para mostrar el progreso de una operación de larga duración, proporcionando una pista visual de que el procesamiento está en marcha.

La clase ProgressBar gestiona el progreso, y el formato de la línea viene dado por varios widgets. Un widget es un objeto que puede mostrar diferentemente dependiendo del estado del progreso. Hay tres tipos de widget: - una cadena, que siempre se muestra; - un ProgressBarWidget, que puede devolver un valor diferente cada vez que se llama a su método de actualización; y-un ProgressBarWidgetHFill, que es como ProgressBarWidget, excepto que se expande para llenar el ancho restante de la línea.

El módulo progressbar es muy fácil de usar, pero muy potente. Y admite automáticamente características como el cambio de tamaño automático cuando disponible.

 17
Author: Readonly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-09 04:48:28

También puedes probar:

sys.stdout.write("\r%2d%%" % percent)
sys.stdout.flush()

Usando un solo retorno de carro al principio de su cadena en lugar de varios espacios atrás. El cursor seguirá parpadeando, pero parpadeará después del signo de porcentaje en lugar de debajo del primer dígito, y con un carácter de control en lugar de tres puede obtener menos parpadeo.

 15
Author: Commodore Jaeger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-09 04:21:09

Por si sirve de algo, aquí está el código que usé para que funcione:

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) )
 6
Author: tstone2077,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-17 13:34:20

Si utiliza el paquete curses, tiene un control mucho mayor de la consola. También tiene un costo más alto en complejidad de código y probablemente sea innecesario a menos que esté desarrollando una gran aplicación basada en consola.

Para una solución simple, siempre puede poner la rueda giratoria al final del mensaje de estado (la secuencia de caracteres |, \, -, / que en realidad se ve bien bajo el cursor parpadeante.

 4
Author: hazzen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-09 04:14:37

Usé este código:

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()
 1
Author: MichaelvdNet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-02 14:01:43
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)
 1
Author: Paal Pedersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-18 14:43:56

Para archivos pequeños es posible que necesite tener estas líneas para evitar porcentajes locos:

Sys.stdout.escribir ("\r % 2d % % " % percent)

Sys.stdout.flush ()

Salud

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-01 10:04:19
 0
Author: mou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-14 08:31:23

Tarde a la fiesta, como siempre. Aquí hay una implementación que apoya la presentación de informes de progreso, como el núcleo 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)
 0
Author: mafrosis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-10 04:38:33