2008-09-09 17 views
47

Estoy escribiendo una pequeña aplicación para descargar archivos a través de http (como, por ejemplo, se describe here).¿Cómo escribir un indicador de progreso de descarga en Python?

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

Esto es lo que 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: MyFileName ... 9%

Cualquier otra idea o recomendaciones para hacer esto?

Una cosa que es un poco molesto es el cursor parpadeante en el terminal en el primer dígito del porcentaje. ¿Hay alguna forma de prevenir esto? ¿Hay alguna manera de ocultar el cursor?

EDIT:

Aquí una mejor alternativa que utiliza 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: MyFileName ... 9%

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

+0

'rem_file' mundial tiene sentido solo dentro de una función donde lo vincula a un nuevo objeto 'rem_file = ...' de lo contrario (si solo lee su valor) 'global rem_file' no es necesario. – jfs

+0

También puede hacer un/r + flush() en salida estándar. Solo estoy adivinando que esto se está ejecutando en Windows según el comportamiento del terminal que mencionas. – meawoppl

+0

relacionado http://stackoverflow.com/questions/15644964/python-progress-bar-and-downloads –

Respuesta

17

Hay una biblioteca de texto de la barra de progreso en el pitón http://pypi.python.org/pypi/progressbar/2.2 que le puede resultar útil:

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.

+0

Acabo de encontrar esto y fue muy fácil de usar. ¡Gracias por el consejo! – Twisty

+0

cómo tener múltiples barras de progreso en la terminal si se insinúan descargas múltiples en hilos separados? –

4

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

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

13

También puede intentar:.

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

uso de un único retorno de carro al comienzo de su cadena en lugar de varias teclas de retroceso. el cursor parpadeará todavía, pero va a parpadear después de que el signo de porcentaje en lugar de debajo del primer dígito, y con un carácter de control en vez de tres que puede obtener menos parpadeo.

0

Para pequeños archivos que usted puede necesitar tenía estas líneas con el fin de evitar porcentajes locos:

sys.stdout .WRITE ("\ r% 2d %%" % por ciento)

sys.stdout.flush()

Saludos

1

que utilizan 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() 
0

tarde a la fiesta, como de costumbre. He aquí una aplicación que soporte los informes de progreso, al igual que 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) 
6

Por lo que vale la pena, aquí está el código que he utilizado 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)) 
+1

Esto solo me da: pbar.update (int (count * blocksize * 100/totalSize)) NameError: el nombre global 'blocksize' no está definido –

+0

era un error tipográfico. Debería haber sido el parámetro de entrada. He actualizado la respuesta para reflejar eso. ¡Gracias! – tstone2077

1
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) 
Cuestiones relacionadas