Como você para 'wget' depois que recebe um 404?

12

Se você usar a expansão entre chaves wget, poderá buscar imagens numeradas em seqüência com facilidade:

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

Ele busca os 10 primeiros arquivos numerados 90.jpgpara 99.jpgficarem bem, mas 100.jpgretorna um erro 404: Arquivo não encontrado (tenho apenas 100 imagens armazenadas no servidor). Esses arquivos inexistentes se tornam mais "um problema" se você usar um intervalo maior, como {00..200}, com 100 arquivos inexistentes, aumenta o tempo de execução do script e pode até se tornar um pequeno fardo (ou pelo menos aborrecimento) em o servidor.

Existe alguma maneira de wgetparar depois de receber seu primeiro erro 404? (ou melhor ainda, duas em uma linha, caso haja um arquivo ausente no intervalo por outro motivo) A resposta não precisa usar a expansão entre chaves; loops também são bons.

IQAndreas
fonte
1
Em um cenário em tempo real, convém acessar todos os URLs para saber o status. 1, 2 or even n failuresnão é o caminho certo quando você conhece [begin .. end]índices. Por que você especificaria o [1..200]alcance quando sabe que existem apenas 100 imagens [1..100]? Eu acho que você pode tentar o GNU parallelpara solicitações simultâneas para acelerar o processo.
SparKot
1
@SparKot ॐ A chave é que eu não sei se existem apenas 100 imagens no servidor. Quero que o script faça o download de tantas imagens quanto possível na série até descobrir onde está o fim.
IQAndreas

Respostas:

9

Se você está feliz com um loop:

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

Isso será executado wgetpara cada URL da sua expansão até que ela falhe e breaksaia do loop.

Se você deseja duas falhas consecutivas, fica um pouco mais 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

Você pode diminuir um pouco com &&e em ||vez de if, mas fica bem feio.

Eu não acredito que wgettenha algo embutido para fazer isso.

Michael Homer
fonte
Posso sugerir o uso elifpara tornar o segundo exemplo mais claro? Algo assim talvez? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (ele só adiciona uma linha extra, não incluindo colocando as thens na mesma linha que o ifs)
IQAndreas
Justo. A tradução de uma linha não é tão direta agora, mas não é muito boa de qualquer maneira.
22714 Michael Homer
9

Você pode usar a $?variável para obter o código de retorno do wget. Se for diferente de zero, significa que ocorreu um erro e você o contabiliza até atingir um limite, e pode sair do circuito.

Algo assim em cima da minha cabeça

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

O loop for pode ser um pouco limpo, mas você pode entender a ideia geral.

Mudar $threshold -eq 16para -eq 24significa que falhará três vezes antes de parar, no entanto, não ocorreria duas vezes seguidas, seria se falhasse duas vezes no loop.

A razão pela qual 16e 24são usados ​​é que é o total dos códigos de retorno.
O wget responde com um código de retorno 8quando recebe um código de resposta que corresponde a um erro do servidor e, portanto, 16é o total após 2 erros.

É possível parar quando as falhas ocorrem apenas duas vezes seguidas, redefinindo o limite sempre que wgetbem-sucedido, ou seja, quando o código de retorno é 0


Uma lista de códigos de retorno do wget pode ser encontrada aqui - http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html

Lawrence
fonte
2
Embora possa ser deduzido da resposta, você pode apontar explicitamente que um erro 404 retorna um código de saída de 8, portanto, os números mágicos de 16e 24.
IQAndreas
1
Eu atualizei minha resposta
Lawrence
1
Obrigado por $?! Muito útil!
neverMind9
2

Com o GNU Parallel, isso deve funcionar:

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

Na versão 20140722, você quase pode ter o seu "dois em fila" - falha: --halt 2% permitirá que 2% dos trabalhos falhem:

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

A IMO, focando no wgetcódigo / status de saída da saída, pode ser muito ingênua para alguns casos de uso, então aqui está um que considera o Código de Status HTTP e também para algumas tomadas de decisão granulares.

wgetfornece um -S/--server-responsesinalizador para imprimir os cabeçalhos de resposta HTTP STDERRdo comando - sobre os quais podemos extrair e 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
fonte
-1

Em python você pode fazer

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)

Confira a documentação do subprocesso, se você quiser fazer mais https://docs.python.org/2/library/subprocess.html

briankip
fonte
A menos check_outputque haja alguma mágica por perto wgetpara detectar uma 404- eu não acredito que haja verificações adequadas aqui e, portanto, realmente não responda à pergunta.
shalomb
Leia os documentos. Ele verifica a saída em stdout ou stderr. wget tem um código específico para 404 de
briankip