Come smetti di "wget" dopo aver ottenuto un 404?


12

Se si utilizza l'espansione del controvento con wget, è possibile recuperare facilmente le immagini numerate in sequenza:

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

Si va a prendere i primi 10 file numerati 90.jpgper 99.jpgbene, ma 100.jpge poi restituire un 404: file non trovato errore (ho solo 100 immagini memorizzate sul server). Questi file inesistenti diventano più "problematici" se si utilizza un intervallo più ampio, ad esempio {00..200}con 100 file inesistenti, aumenta il tempo di esecuzione dello script e potrebbe persino diventare un leggero onere (o almeno un fastidio) per il server.

C'è un modo per wgetfermarsi dopo aver ricevuto il suo primo errore 404? (o meglio, due di fila, nel caso in cui ci fosse un file mancante nell'intervallo per un altro motivo) La risposta non ha bisogno di usare l'espansione del controvento; anche i loop vanno bene.


1
In uno scenario in tempo reale, potresti voler colpire ogni URL per conoscere lo stato. 1, 2 or even n failuresnon è il modo giusto quando conosci gli [begin .. end]indici. Perché dovresti specificare l' [1..200]intervallo quando sai che ci sono solo 100 immagini in [1..100]. Immagino che tu possa provare GNU parallelper richieste simultanee per accelerare il processo.
SparKot,

1
@SparKot ॐ La chiave è che non so ci sono solo 100 immagini sul server, voglio che lo script scarichi quante più immagini possibile nella serie fino a quando non ha capito dove si trova la fine.
IQAndreas,

Risposte:


9

Se sei soddisfatto di un loop:

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

Ciò verrà eseguito wgetper ciascun URL dell'espansione fino a quando non ha esito negativo, quindi breakfuori dal ciclo.

Se vuoi due guasti di seguito diventa un po 'più complicato:

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

Puoi ridurlo un po 'con &&e ||invece di if, ma diventa piuttosto brutto.

Non credo che ci wgetsia qualcosa per farlo.


Posso suggerire di usare elifper rendere più chiaro il secondo esempio? Qualcosa del genere forse? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (aggiunge solo una riga aggiuntiva, senza includere la thens sulla stessa riga della ifs)
IQAndreas

Giusto. La traduzione di una riga non è così semplice ora, ma non è comunque molto buona.
Michael Homer,

9

È possibile utilizzare la $?variabile per ottenere il codice di ritorno di wget. Se è diverso da zero, significa che si è verificato un errore e lo si calcola fino a raggiungere una soglia, quindi potrebbe uscire dal ciclo.

Qualcosa del genere dalla cima della mia testa

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

Il ciclo for può essere ripulito un po ', ma puoi capire l'idea generale.

La modifica di $threshold -eq 16to -eq 24significherebbe che fallirebbe 3 volte prima che si fermasse, tuttavia non sarebbe due volte consecutive, sarebbe se fallisse due volte nel loop.

Il motivo per cui 16e 24vengono utilizzati è che è il totale dei codici di ritorno.
wget risponde con un codice di ritorno di 8quando riceve un codice di risposta che corrisponde a un errore dal server, e quindi 16è il totale dopo 2 errori.

L'arresto quando si verificano errori solo due volte di seguito può essere ripristinato ripristinando la soglia ogni volta che ha wgetesito positivo, ovvero quando il codice di ritorno è 0


Un elenco di codici di ritorno wget è disponibile qui: http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html


2
Sebbene possa essere dedotto dalla risposta, potresti voler sottolineare esplicitamente che un errore 404 restituisce un codice di uscita di 8, quindi i numeri magici di 16e 24.
IQAndreas,

1
Ho aggiornato la mia risposta
Lawrence il

1
Grazie per $?! Molto utile!
neverMind9

2

Con GNU Parallel questo dovrebbe funzionare:

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

Dalla versione 20140722 puoi quasi avere i tuoi "due di fila" -failure: --halt 2% consentirà di fallire il 2% dei lavori:

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

1

L'IMO, concentrandosi sul wgetcodice / stato di uscita potrebbe essere troppo ingenuo per alcuni casi d'uso, quindi eccone uno che considera anche il codice di stato HTTP per alcune decisioni granulari.

wgetfornisce un -S/--server-responseflag per stampare le intestazioni di risposta HTTP sul STDERRcomando - su cui possiamo estrarre e agire.

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

-1

In Python puoi farlo

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)

Consulta la documentazione per i sottoprocessi se vuoi fare di più https://docs.python.org/2/library/subprocess.html


A meno che check_outputnon ci sia un po 'di magia in giro wgetper rilevare un 404- non credo che ci siano controlli adeguati qui e quindi non risponde davvero alla domanda.
Shalomb,

Lo fa, leggi i documenti. Controlla l'output in stdout o stderr. wget ha un codice specifico per 404's
briankip
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.