¿Cómo se detiene 'wget' después de que obtiene un 404?

12

Si utiliza la expansión de llaves con wget, puede obtener imágenes numeradas secuencialmente con facilidad:

$ wget 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'

Obtiene los primeros 10 archivos numerados 90.jpgpara que 99.jpgestén bien, pero 100.jpgluego devuelve un error 404: Archivo no encontrado (solo tengo 100 imágenes almacenadas en el servidor). Estos archivos inexistentes se vuelven más un "problema" si usa un rango mayor, como {00..200}, con 100 archivos inexistentes, aumenta el tiempo de ejecución del script e incluso podría convertirse en una carga ligera (o al menos molesta) en el servidor.

¿Hay alguna forma de wgetdetenerse después de haber recibido su primer error 404? (o incluso mejor, dos seguidos, en caso de que falte un archivo en el rango por otra razón) La respuesta no necesita usar la expansión de llaves; los bucles también están bien.

IQAndreas
fuente
1
En un escenario en tiempo real, es posible que desee presionar cada URL para conocer el estado. 1, 2 or even n failuresno es la forma correcta cuando sabes [begin .. end]índices. ¿Por qué especificarías el [1..200]rango si sabes que solo hay 100 imágenes [1..100]? Supongo que puedes probar GNU parallelpara solicitudes simultáneas para acelerar el proceso.
SparKot
1
@SparKot ॐ La clave es que no sé que solo hay 100 imágenes en el servidor, quiero que el script descargue tantas imágenes como sea posible en la serie hasta que haya descubierto dónde está el final.
IQAndreas

Respuestas:

9

Si estás contento con un bucle:

for url in 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'
do
    wget "$url" || break
done

Eso se ejecutará wgetpara cada URL en su expansión hasta que falle, y luego breaksalga del ciclo.

Si desea dos fallas seguidas, se vuelve un poco más complicado:

for url in 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'
do
    if wget "$url"
    then
        failed=
    elif [ "$failed" ]
    then
        break
    else
        failed=yes
    fi
done

Puedes reducir eso un poco con &&y en ||lugar de if, pero se pone bastante feo.

No creo que wgethaya nada incorporado para hacer eso.

Michael Homer
fuente
¿Puedo sugerir usar elifpara aclarar el segundo ejemplo? ¿Algo así quizás? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (solo agrega una línea adicional, sin incluir poner la thens en la misma línea que la ifs)
IQAndreas
Lo suficientemente justo. La traducción de una línea no es tan sencilla ahora, pero de todos modos no es muy buena.
Michael Homer
9

Puede usar la $?variable para obtener el código de retorno de wget. Si no es cero, significa que se produjo un error y lo cuenta hasta que alcanza un umbral, entonces podría salir del ciclo.

Algo así fuera de mi cabeza

#!/bin/bash

threshold=0
for x in {90..110}; do
    wget 'http://www.iqandreas.com/sample-images/100-100-color/'$x'.jpg'
    wgetreturn=$?
    if [[ $wgetreturn -ne 0 ]]; then
        threshold=$(($threshold+$wgetreturn))
        if [[ $threshold -eq 16 ]]; then
                break
        fi
    fi
done

El bucle for se puede limpiar un poco, pero puede comprender la idea general.

Cambiar el $threshold -eq 16a -eq 24significaría que fallaría 3 veces antes de detenerse, sin embargo, no sería dos veces seguidas, lo sería si fallara dos veces en el ciclo.

La razón por la cual 16y 24se utiliza es que es el total de los códigos de retorno.
wget responde con un código de retorno de 8cuando recibe un código de respuesta que corresponde a un error del servidor y, por lo tanto, 16es el total después de 2 errores.

Se puede detener cuando las fallas solo ocurren dos veces seguidas restableciendo el umbral siempre que wgettenga éxito, es decir, cuando el código de retorno es 0


Puede encontrar una lista de códigos de retorno de wget aquí: http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html

Lawrence
fuente
2
Aunque se puede deducir de la respuesta, es posible que desee señalar explícitamente que un error 404 devuelve un código de salida de 8, de ahí los números mágicos de 16y 24.
IQAndreas
1
He actualizado mi respuesta
Lawrence
1
Gracias por $?! ¡Muy útil!
neverMind9
2

Con GNU Parallel, esto debería funcionar:

parallel --halt 1 wget ::: 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'

A partir de la versión 20140722, casi puede tener su falla "dos en una fila": - el 2% permitirá que falle el 2% de los trabajos:

parallel --halt 2% wget ::: 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'
Ole Tange
fuente
1

En mi opinión, centrarse en el wgetcódigo / estado de salida puede ser demasiado ingenuo para algunos casos de uso, así que aquí hay uno que considera el código de estado HTTP también para una toma de decisiones granular.

wgetproporciona una -S/--server-responsemarca para imprimir los Encabezados de respuesta HTTP en STDERRel comando, que podemos extraer y actuar sobre ellos.

#!/bin/bash

set -eu

error_max=2
error_count=0

urls=( 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg' )

for url in "${urls[@]}"; do
  set +e
  http_status=$( wget --server-response -c "$url" 2>&1 )
  exit_status=$?
  http_status=$( awk '/HTTP\//{ print $2 }' <<<"$http_status" | tail -n 1 )

  if (( http_status >= 400 )); then
    # Considering only HTTP Status errors
    case "$http_status" in
      # Define your actions for each 4XX Status Code below
      410) : Gone
        ;;
      416) : Requested Range Not Satisfiable
        error_count=0  # Reset error_count in case of `wget -c`
        ;;
      403) : Forbidden
        ;&
      404) : Not Found
        ;&
      *)     (( error_count++ ))
        ;;
    esac
  elif (( http_status >= 300 )); then
     # We're unlikely to reach here in case of 1XX, 3XX in $http_status
     # but ..
     exit_status=0
  elif (( http_status >= 200 )); then
     # 2XX in $http_status considered successful
     exit_status=0
  elif (( exit_status > 0 )); then

    # Where wget's exit status is one of
    # 1   Generic error code.
    # 2   Parse error 
    #     - when parsing command-line options, the .wgetrc or .netrc...
    # 3   File I/O error.
    # 4   Network failure.
    # 5   SSL verification failure.
    # 6   Username/password authentication failure.
    # 7   Protocol errors.

    (( error_count++ ))
  fi

  echo "$url -> http_status: $http_status, exit_status=$exit_status, error_count=$error_count" >&2

  if (( error_count >= error_max )); then
    echo "error_count $error_count >= $error_max, bailing out .." >&2
    exit "$exit_status"
  fi

done
shalomb
fuente
-1

En python puedes hacer

from subprocess import *

def main():
    for i in range(90, 110):
       try :
          url = "url/"+str(i)
          check_output(["wget", url])
       except CalledProcessError:
          print "Wget returned none zero output, quiting"
          sys.exit(0)

Consulte la documentación del subproceso si desea hacer más https://docs.python.org/2/library/subprocess.html

briankip
fuente
A menos que check_outputhaga un poco de magia wgetpara detectar un 404... No creo que haya controles adecuados aquí, por lo que realmente no responde la pregunta.
shalomb
Sí, lee los documentos. Comprueba la salida en stdout o stderr. wget tiene un código específico para 404's
briankip