Jak zatrzymać „wget” po otrzymaniu 404?

12

Jeśli używasz rozszerzenia nawiasów klamrowych wget, możesz z łatwością pobierać obrazy o numerach sekwencyjnych:

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

Pobiera pierwsze 10 plików ponumerowanych, 90.jpgaby w 99.jpgporządku, ale 100.jpgpotem zwraca błąd 404: Nie znaleziono pliku (mam tylko 100 obrazów zapisanych na serwerze). Te nieistniejące pliki stają się bardziej „problemem”, jeśli użyjesz większego zakresu, np. W {00..200}przypadku 100 nieistniejących plików wydłuży to czas wykonywania skryptu, a nawet może stanowić niewielkie obciążenie (lub przynajmniej irytację) dla serwer.

Czy jest jakiś sposób na wgetzatrzymanie po otrzymaniu pierwszego błędu 404? (lub jeszcze lepiej, dwa z rzędu, w przypadku braku pliku w zakresie z innego powodu) Odpowiedź nie wymaga użycia interpretacji nawiasów; pętle też są w porządku.

IQAndreas
źródło
1
W scenariuszu w czasie rzeczywistym możesz kliknąć każdy adres URL, aby poznać stan. 1, 2 or even n failuresnie jest właściwy sposób, gdy znasz [begin .. end]indeksy. Dlaczego miałbyś określać [1..200]zasięg, skoro wiesz, że jest tam tylko 100 zdjęć [1..100]. Myślę, że możesz wypróbować GNU paralleldla jednoczesnych żądań, aby przyspieszyć ten proces.
SparKot,
1
@SparKot key Kluczem jest to, że nie wiem, że na serwerze jest tylko 100 obrazów, chcę, aby skrypt pobierał jak najwięcej obrazów z serii, dopóki nie zorientuje się, gdzie jest koniec.
IQAndreas

Odpowiedzi:

9

Jeśli jesteś zadowolony z pętli:

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

Będzie działał wgetdla każdego adresu URL w rozwinięciu, dopóki się nie powiedzie, a następnie zniknie breakz pętli.

Jeśli potrzebujesz dwóch awarii z rzędu, staje się to nieco bardziej skomplikowane:

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

Możesz to trochę zmniejszyć za pomocą &&i ||zamiast if, ale robi się dość brzydko.

Nie wierzę, że wgetma coś wbudowanego w to.

Michael Homer
źródło
Czy mogę zasugerować użycie, elifaby wyjaśnić drugi przykład? Może coś takiego? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (dodaje tylko jedną dodatkową linię, nie włączając umieszczania thens na tej samej linii co ifs)
IQAndreas
Słusznie. Tłumaczenie jednowierszowe nie jest teraz tak proste, ale i tak nie jest zbyt dobre.
Michael Homer
9

Możesz użyć tej $?zmiennej, aby uzyskać kod powrotu wget. Jeśli jest niezerowy, oznacza to, że wystąpił błąd i sumujesz go, aż osiągnie próg, a następnie może wyjść z pętli.

Coś takiego z góry mojej głowy

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

Pętlę for można trochę wyczyścić, ale można zrozumieć ogólny pomysł.

Zmiana $threshold -eq 16do -eq 24oznaczałoby to nie 3 razy, zanim to stop, ale nie będzie się dwa razy z rzędu, to jest, gdy nie udało się dwukrotnie w pętli.

Powodem, 16i 24są stosowane jest to suma kodów zwrotnych.
wget odpowiada kodem powrotu, 8kiedy otrzymuje kod odpowiedzi, który odpowiada błędowi z serwera, a zatem 16jest sumą po 2 błędach.

Zatrzymanie, gdy awarie występują tylko dwa razy z rzędu, można wykonać, resetując próg za każdym razem wget, gdy się powiedzie, tj. Gdy kod powrotu wynosi 0


Listę kodów powrotu wget można znaleźć tutaj - http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html

Lawrence
źródło
2
Chociaż można wywnioskować z odpowiedzi, warto wyraźnie podkreślić, że błąd 404 zwraca kod zakończenia 8, stąd liczb magicznych z 16i 24.
IQAndreas,
1
Zaktualizowałem swoją odpowiedź
Lawrence
1
Dzięki za $?! Bardzo przydatne!
neverMind9,
2

W przypadku GNU Parallel powinno to działać:

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

Od wersji 20140722 możesz prawie mieć swoje „dwa z rzędu” - błąd: - zatrzymanie 2% pozwoli na awarię 2% zadań:

parallel --halt 2% wget ::: 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'
Ole Tange
źródło
1

IMO, koncentrując się na wgetkodzie wyjścia / statusie, może być zbyt naiwny w niektórych przypadkach użycia, więc tutaj jest taki, który bierze pod uwagę również kod statusu HTTP dla niektórych szczegółowych decyzji.

wgetzapewnia -S/--server-responseflagę do wydrukowania nagłówków odpowiedzi HTTP na STDERRkomendzie - którą możemy wyodrębnić i podjąć działania.

#!/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
źródło
-1

W Pythonie możesz to zrobić

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)

Zapoznaj się z dokumentacją dotyczącą podprocesu, jeśli chcesz zrobić więcej https://docs.python.org/2/library/subprocess.html

briankip
źródło
Chyba że check_output że w pobliżu jest jakaś magia, wgetżeby wykryć 404- nie sądzę, że są tu odpowiednie kontrole, więc tak naprawdę nie odpowiada na pytanie.
shalomb
Tak, przeczytaj dokumenty. Sprawdza wyjście w stdout lub stderr. wget ma określony kod dla 404
briankip