Як зупинити '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? (а ще краще, два поспіль, якщо в діапазоні з іншої причини був файл, що відсутній) У відповіді не потрібно використовувати розширення дужок; петлі теж тонкі.


1
У сценарії в реальному часі ви можете скористатися кожною URL-адресою, щоб дізнатися про стан. 1, 2 or even n failuresце не правильний шлях, коли ви знаєте [begin .. end]індекси. Чому б ви вказали [1..200]діапазон, коли знаєте, що в ньому всього 100 зображень [1..100]. Я думаю, ви можете спробувати GNU parallelдля одночасних запитів, щоб прискорити процес.
SparKot

1
@SparKot key Ключ у тому, що я не знаю, що на сервер є лише 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 ви можете майже мати "два поспіль" -відмови: - 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

-1

У python можна зробити

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


Якщо check_outputякимось чином не wgetвиявляю 404ніякої магії - я не вірю, що тут є адекватні перевірки, і це дійсно не відповідає на питання.
шалом

Так, читайте документи. Він перевіряє вихід у stdout або stderr. wget має специфічний код для 404-х
briankip
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.