Как остановить wget после того, как он получит 404?

12

Если вы используете расширение скобок с помощью wget, вы можете легко получать изображения с последовательными номерами:

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

Он извлекает первые 10 файлов , пронумерованных 90.jpgв 99.jpgтолько штраф, но 100.jpgи далее возвращает 404: Файл не найдено ошибку ( у меня только 100 изображений , хранящихся на сервере). Эти несуществующие файлы становятся более «проблемой», если вы используете больший диапазон, например {00..200}, при наличии 100 несуществующих файлов это увеличивает время выполнения скрипта и может даже стать небольшим бременем (или, по крайней мере, раздражением) для сервер.

Есть ли способ для wgetостановки после получения первой ошибки 404? (или даже лучше, два подряд, если в диапазоне отсутствовал файл по другой причине). В ответе не нужно использовать скобку; петли тоже хорошо.

IQAndreas
источник
1
В сценарии в реальном времени вы можете нажать каждый URL, чтобы узнать статус. 1, 2 or even n failuresэто не правильный путь, когда вы знаете [begin .. end]индексы. Зачем вам указывать [1..200]диапазон, если вы знаете, что в нем только 100 изображений [1..100]? Я думаю, вы можете попробовать GNU parallelдля одновременных запросов, чтобы ускорить процесс.
SparKot
1
@SparKot ॐ Суть в том, что я не знаю, что на сервере всего 100 изображений, я хочу, чтобы скрипт загружал столько серий, сколько он может в серии, пока не выяснил, где конец.
IQAndreas

Ответы:

9

Если вас устраивает цикл:

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

Это будет работать wgetдля каждого URL в вашем расширении, пока не произойдет сбой, а затем breakиз цикла.

Если вы хотите два сбоя подряд, это будет немного сложнее:

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

Вы можете уменьшить это немного с &&и ||вместо if, но это становится довольно уродливым.

Я не верю wget, что для этого есть что-то встроенное.

Майкл Гомер
источник
Могу ли я предложить использовать, elifчтобы сделать второй пример более понятным? Может быть, что-то подобное? gist.github.com/IQAndreas/84cae3f0193b67691ff2 (он добавляет только одну дополнительную строку, не считая thens в той же строке, что и ifs)
IQAndreas
Справедливо. Однострочный перевод сейчас не так прост, но в любом случае он не очень хорош.
Майкл Гомер
9

Вы можете использовать $?переменную, чтобы получить код возврата wget. Если он ненулевой, то это означает, что произошла ошибка, и вы подсчитываете ее до тех пор, пока она не достигнет порога, тогда она может выйти из цикла.

Как-то так с моей головы

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

Цикл for можно немного очистить, но вы можете понять общую идею.

Изменение , $threshold -eq 16чтобы -eq 24означало бы оно не будет в 3 раза , прежде чем он остановится, однако она не была бы в два раза подряд, это было бы , если он не в два раза в цикле.

Причина, почему 16и 24используются, состоит в том, что это сумма кодов возврата.
wget отвечает кодом возврата, 8когда он получает код ответа, который соответствует ошибке с сервера, и, таким образом, 16представляет собой сумму после 2 ошибок.

Остановка, когда сбои происходят только два раза подряд, может быть выполнена путем сброса порогового значения при каждом успешном завершении wget, т. Е. Когда код возврата равен 0


Список кодов возврата wget можно найти здесь - http://www.gnu.org/software/wget/manual/html_node/Exit-Status.html.

Лоренс
источник
2
Хотя это может быть выведено из ответа, вы можете явно указать, что ошибка 404 возвращает код выхода 8, поэтому магические числа из 16и 24.
IQAndreas
1
Я обновил свой ответ
Лоуренс
1
Спасибо за $?! Очень полезный!
neverMind9
2

С GNU Parallel это должно работать:

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

Начиная с версии 20140722, вы можете получить ошибку «два в ряд»: --halt 2% допустит сбой 2% заданий:

parallel --halt 2% wget ::: 'http://www.iqandreas.com/sample-images/100-100-color/'{90..110}'.jpg'
Оле Танге
источник
1

IMO, фокусировка на wgetкоде выхода / статусе может быть слишком наивной для некоторых вариантов использования, поэтому вот тот, который рассматривает код состояния HTTP также для некоторых детальных решений.

wgetобеспечивает -S/--server-responseфлаг для распечатки заголовков ответа HTTP STDERRкоманды - который мы можем извлечь и действовать.

#!/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
источник
-1

В питоне вы можете сделать

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)

Ознакомьтесь с документацией для подпроцесса, если вы хотите сделать больше https://docs.python.org/2/library/subprocess.html

briankip
источник
Если только check_outputне происходит какое-то волшебство, wgetчтобы обнаружить ... 404Я не верю, что здесь есть адекватные проверки, и поэтому на самом деле не отвечает на вопрос.
Шаломб
Это делает, прочитайте документы. Он проверяет вывод в stdout или stderr. У wget есть специальный код для 404-х
briankip