Comment arrêter «wget» après avoir obtenu un 404?

12

Si vous utilisez l'extension d'accolade avec wget, vous pouvez facilement récupérer des images numérotées séquentiellement:

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

Il va chercher les 10 premiers fichiers numérotés 90.jpgà 99.jpgtrès bien, mais 100.jpget revenir en avant une 404: Fichier introuvable erreur (je seulement 100 images stockées sur le serveur). Ces fichiers inexistants deviennent plus un "problème" si vous utilisez une plage plus large, comme {00..200}, avec 100 fichiers inexistants, cela augmente le temps d'exécution du script, et peut même devenir un léger fardeau (ou au moins une gêne) sur le serveur.

Existe-t-il un moyen wgetd'arrêter une fois qu'il a reçu sa première erreur 404? (ou encore mieux, deux de suite, au cas où il y aurait un fichier manquant dans la plage pour une autre raison) La réponse n'a pas besoin d'utiliser l'expansion d'accolade; les boucles sont bien aussi.

IQAndreas
la source
1
Dans un scénario en temps réel, vous pouvez vouloir frapper chaque URL pour connaître l'état. 1, 2 or even n failuresn'est pas la bonne façon de connaître les [begin .. end]indices. Pourquoi spécifier une [1..200]plage alors que vous savez qu'il n'y a que 100 images dans [1..100]. Je suppose que vous pouvez essayer GNU parallelpour des demandes simultanées afin d'accélérer le processus.
SparKot
1
@SparKot ॐ La clé est que je ne sais pas qu'il n'y a que 100 images sur le serveur, je veux que le script télécharge autant d'images que possible dans la série jusqu'à ce qu'il ait compris où se trouve la fin.
IQAndreas

Réponses:

9

Si vous êtes satisfait d'une boucle:

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

Cela s'exécutera wgetpour chaque URL de votre expansion jusqu'à ce qu'elle échoue, puis breakhors de la boucle.

Si vous voulez deux échecs d'affilée, cela devient un peu plus compliqué:

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

Vous pouvez réduire cela un peu avec &&et ||au lieu de if, mais cela devient assez moche.

Je ne crois pas qu'il y wgetait quoi que ce soit en place pour cela.

Michael Homer
la source
Puis-je suggérer d'utiliser elifpour rendre le deuxième exemple plus clair? Quelque chose comme ça peut-être? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (il n'ajoute qu'une seule ligne supplémentaire, sans mettre le thens sur la même ligne que le ifs)
IQAndreas
C'est suffisant. La traduction sur une ligne n'est plus aussi simple maintenant, mais elle n'est pas très bonne de toute façon.
Michael Homer
9

Vous pouvez utiliser la $?variable pour obtenir le code retour de wget. Si elle n'est pas nulle, cela signifie qu'une erreur s'est produite et que vous la compilez jusqu'à ce qu'elle atteigne un seuil, elle pourrait alors sortir de la boucle.

Quelque chose comme ça du haut de ma tête

#!/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

La boucle for peut être nettoyée un peu, mais vous pouvez comprendre l'idée générale.

Changer le $threshold -eq 16pour -eq 24signifierait qu'il échouerait 3 fois avant de s'arrêter, mais ce ne serait pas deux fois de suite, ce serait le cas s'il échouait deux fois dans la boucle.

La raison pour laquelle 16et 24sont utilisés est le total des codes retour.
wget répond avec un code retour 8lorsqu'il reçoit un code de réponse qui correspond à une erreur du serveur, et 16est donc le total après 2 erreurs.

L'arrêt lorsque les échecs ne se produisent que deux fois de suite peut être effectué en réinitialisant le seuil chaque fois qu'il wgetréussit, c'est-à-dire lorsque le code retour est 0


Une liste des codes de retour wget peut être trouvée ici - http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html

Lawrence
la source
2
Bien que cela puisse être déduit de la réponse, vous voudrez peut-être souligner explicitement qu'une erreur 404 renvoie un code de sortie de 8, d'où les nombres magiques de 16et 24.
IQAndreas
1
J'ai mis à jour ma réponse
Lawrence
1
Merci pour $?! Très utile!
neverMind9
2

Avec GNU Parallel, cela devrait fonctionner:

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

À partir de la version 20140722, vous pouvez presque avoir votre "deux dans une rangée" -failure: - l'asphalte 2% permettra à 2% des travaux d'échouer:

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

IMO, en se concentrant sur wgetle code / statut de sortie de, peut être trop naïf pour certains cas d'utilisation, alors voici celui qui prend également en compte le code de statut HTTP pour une prise de décision granulaire.

wgetfournit un -S/--server-responseindicateur pour imprimer les en-têtes de réponse HTTP sur STDERRla commande - que nous pouvons extraire et agir.

#!/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
la source
-1

En python, vous pouvez faire

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)

Consultez la documentation du sous-processus si vous souhaitez en faire plus https://docs.python.org/2/library/subprocess.html

briankip
la source
À moins qu'il check_outputn'y ait un peu de magie wgetpour détecter un 404- je ne pense pas qu'il y ait des contrôles adéquats ici et ne répond donc pas vraiment à la question.
shalomb
Oui, lisez les documents. Il vérifie la sortie dans stdout ou stderr. wget a un code spécifique pour les 404
briankip