Wie stoppen Sie 'wget', nachdem es einen 404 bekommen hat?

12

Wenn Sie die Klammererweiterung mit verwenden wget, können Sie problemlos fortlaufend nummerierte Bilder abrufen:

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

Es holt die ersten 10 Dateien nummerierten , 90.jpgum 99.jpgganz gut, aber 100.jpgund weiter gibt einen 404: Datei nicht gefunden Fehler (ich habe nur 100 Bilder auf dem Server gespeichert ist ). Diese nicht vorhandenen Dateien werden eher zu einem "Problem", wenn Sie einen größeren Bereich verwenden, z. B. {00..200}bei 100 nicht vorhandenen Dateien die Ausführungszeit des Skripts verlängern und sogar eine leichte Belastung (oder zumindest eine Belästigung) darstellen der Kellner.

Gibt es eine Möglichkeit wget, anzuhalten, nachdem der erste 404-Fehler aufgetreten ist? (oder noch besser, zwei in einer Reihe, falls aus einem anderen Grund eine Datei im Bereich fehlt) Die Antwort muss keine Klammererweiterung verwenden. Schleifen sind auch in Ordnung.

IQAndreas
quelle
1
In einem Echtzeitszenario möchten Sie möglicherweise jede URL aufrufen, um den Status zu erfahren. 1, 2 or even n failuresist nicht der richtige Weg, wenn Sie [begin .. end]Indizes kennen . Warum würden Sie angeben [1..200]Bereich , wenn Sie wissen , dass es nur 100 Bilder in [1..100]. Ich denke, Sie können GNU parallelfür gleichzeitige Anfragen ausprobieren , um den Prozess zu beschleunigen.
SparKot
1
@SparKot ॐ Der Schlüssel ist, dass ich nicht weiß, dass nur 100 Bilder auf dem Server sind. Ich möchte, dass das Skript so viele Bilder wie möglich in der Serie herunterlädt, bis es herausgefunden hat, wo das Ende ist.
IQAndreas

Antworten:

9

Wenn Sie mit einer Schleife zufrieden sind:

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

Dies wird wgetfür jede URL in Ihrer Erweiterung ausgeführt, bis sie fehlschlägt, und dann breakaußerhalb der Schleife.

Wenn Sie zwei Fehler hintereinander möchten, wird es etwas komplizierter:

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

Sie können das ein wenig mit &&und ||statt schrumpfen if, aber es wird ziemlich hässlich.

Ich glaube nicht, dass wgetetwas eingebaut ist, um das zu tun.

Michael Homer
quelle
Darf ich vorschlagen elif, das zweite Beispiel klarer zu machen? So etwas vielleicht? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (es wird nur eine zusätzliche Zeile hinzugefügt , ohne das thens in dieselbe Zeile wie das ifs zu setzen)
IQAndreas
Meinetwegen. Die einzeilige Übersetzung ist jetzt nicht so einfach, aber trotzdem nicht sehr gut.
Michael Homer
9

Sie können die $?Variable verwenden, um den Rückkehrcode von wget abzurufen. Wenn es nicht Null ist, bedeutet dies, dass ein Fehler aufgetreten ist und Sie ihn abrechnen, bis er einen Schwellenwert erreicht hat. Dann könnte er aus der Schleife ausbrechen.

So etwas auf meinem Kopf

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

Die for-Schleife kann ein wenig aufgeräumt werden, aber Sie können die allgemeine Idee verstehen.

Das Ändern von $threshold -eq 16auf -eq 24würde bedeuten, dass es dreimal fehlschlägt, bevor es stoppt. Es wäre jedoch nicht zweimal hintereinander, es wäre, wenn es zweimal in der Schleife fehlschlägt.

Der Grund, warum 16und 24verwendet werden, ist die Summe der Rückkehrcodes.
wget antwortet mit einem Rückkehrcode, 8wenn es einen Antwortcode erhält, der einem Fehler vom Server entspricht, und ist somit 16die Summe nach 2 Fehlern.

Das Anhalten, wenn Fehler nur zweimal hintereinander auftreten, kann durch Zurücksetzen des Schwellenwerts erfolgen, wenn dies wgeterfolgreich ist, dh wenn der Rückkehrcode 0 ist


Eine Liste der wget-Rückkehrcodes finden Sie hier - http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html

Lawrence
quelle
2
Obwohl dies aus der Antwort abgeleitet werden kann, möchten Sie möglicherweise explizit darauf hinweisen, dass ein 404-Fehler einen Exit-Code von zurückgibt 8, daher die magischen Zahlen von 16und 24.
IQAndreas
1
Ich habe meine Antwort aktualisiert
Lawrence
1
Danke für $?! Sehr hilfreich!
NeverMind9
2

Mit GNU Parallel sollte dies funktionieren:

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

Ab Version 20140722 können Sie fast Ihre "zwei in einer Reihe" -Fehler haben: - Bei 2% können 2% der Jobs fehlschlagen:

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

IMO, sich auf wgetden Exit-Code / Status zu konzentrieren, kann für einige Anwendungsfälle zu naiv sein. Hier ist also einer, der den HTTP-Statuscode auch für einige detaillierte Entscheidungen berücksichtigt.

wgetBietet ein -S/--server-responseFlag zum Ausdrucken der HTTP-Antwortheader STDERRdes Befehls, die wir extrahieren und bearbeiten können.

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

In Python können Sie tun

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)

Überprüfen Sie die Dokumentation für den Unterprozess, wenn Sie weitere Informationen unter https://docs.python.org/2/library/subprocess.html erhalten möchten

Briankip
quelle
Es sei denn, es check_outputgibt etwas Magie, um eine wgetzu erkennen 404- ich glaube nicht, dass es hier angemessene Kontrollen gibt, und beantworte die Frage daher nicht wirklich.
Shalomb
Lesen Sie die Dokumente. Es prüft die Ausgabe in stdout oder stderr. wget hat einen spezifischen Code für 404's
briankip