Параллельный wget в Bash

Я получаю кучу относительно небольших страниц с веб-сайта, и мне было интересно, могу ли я как-то сделать это параллельно в Bash. В настоящее время мой код выглядит так, но его выполнение занимает некоторое время (я думаю, что меня тормозит, так это задержка соединения).

for i in {1..42}
do
    wget "https://www.example.com/page$i.html"
done

Я слышал об использовании xargs, но ничего об этом не знаю, а справочная страница очень сбивает с толку. Любые идеи? Можно ли вообще это делать параллельно? Есть ли другой способ атаковать это?


person Jonathon Vandezande    schedule 28.09.2011    source источник


Ответы (5)


Намного предпочтительнее отодвигать wget в фон с помощью & или -b, вы можете использовать xargs с тем же эффектом, но лучше.

Преимущество в том, что xargs будет правильно синхронизироваться без дополнительных усилий. Это означает, что вы можете безопасно получить доступ к загруженным файлам (при условии, что ошибок не произойдет). Все загрузки будут завершены (или завершены неудачно) после xargs выхода, и по коду выхода вы узнаете, все ли прошло успешно. Это предпочтительнее, чем ожидание с sleep и тестирование завершения вручную.

Предполагая, что URL_LIST - это переменная, содержащая все URL-адреса (может быть построена с помощью цикла в примере OP, но также может быть вручную сгенерированным списком), запустите это:

echo $URL_LIST | xargs -n 1 -P 8 wget -q

будет передавать один аргумент (-n 1) в wget и выполнять не более 8 параллельных wget процессов за раз (-P 8). xarg возвращается после завершения последнего порожденного процесса, и это именно то, что мы хотели знать. Никаких дополнительных уловок.

Выбранное мною «магическое число» 8 параллельных загрузок не высечено на камне, но, вероятно, это хороший компромисс. Есть два фактора в "максимизации" серии загрузок:

Один из них - заполнение «кабеля», то есть использование доступной полосы пропускания. Предполагая «нормальные» условия (сервер имеет большую пропускную способность, чем клиент), это уже имеет место при одной или максимум двух загрузках. Добавление большего количества подключений к проблеме приведет только к отбрасыванию пакетов и включению контроля перегрузки TCP, а также к N загрузкам с асимптотической пропускной способностью 1 / N каждая с тем же чистым эффектом. (минус отброшенные пакеты, минус восстановление размера окна). Отбрасывание пакетов - нормальное явление в IP-сети, именно так должен работать контроль перегрузки (даже при одном подключении), и обычно влияние практически нулевое. Однако наличие неоправданно большого количества подключений усиливает этот эффект, поэтому он может стать заметным. В любом случае быстрее ничего не делает.

Второй фактор - это установление соединения и обработка запроса. Здесь действительно помогает наличие нескольких дополнительных стыковок в полете. Проблема, с которой приходится сталкиваться, - это задержка двух циклов приема-передачи (обычно 20-40 мс в одной и той же географической области, 200-300 мс между континентами) плюс нечетные 1-2 миллисекунды, которые фактически необходимы серверу для обработки запроса и отправки ответа. к розетке. Это не так уж и много времени как таковое, но умноженное на несколько сотен / тысяч запросов, быстро складывается.
Наличие от полдюжины до дюжины запросов на лету скрывает большую часть или вся эта задержка (она все еще присутствует, но поскольку она перекрывается, она не суммируется!). В то же время наличие всего нескольких одновременных подключений не имеет неблагоприятных последствий, таких как чрезмерная перегрузка или принуждение сервера к разветвлению новых процессов.

person Damon    schedule 07.08.2012
comment
Это определенно лучший способ, поскольку он использует универсальный инструмент xargs, и этот метод можно применить ко многим другим командам. - person SineSwiper; 11.08.2012
comment
При загрузке нескольких файлов через HTTP wget может повторно использовать HTTP-соединение благодаря механике Keep-Alive. Когда вы запускаете новый процесс для каждого файла, этот механизм нельзя использовать, и соединение (тройное рукопожатие TCP) необходимо устанавливать снова и снова. Поэтому я предлагаю увеличить параметр -n примерно до 20 или около того. В конфигурации по умолчанию HTTP-сервер Apache будет обслуживать только до 100 запросов в одном поддерживаемом сеансе, поэтому, вероятно, нет смысла переходить через сотню здесь. - person user7610; 18.05.2013
comment
Отличный ответ, но что, если я хочу передать в wget два значения переменных? Я хочу указать путь назначения, а также URL-адрес. Возможно ли это еще с техникой xargs? - person Ricky; 09.05.2014
comment
@Ricky: xargs просто перенаправляет все после параметров и имени исполняемого файла в исполняемый файл, так что это должно работать. - person Damon; 09.05.2014
comment
@Damon Замыкание этого цикла ни к чему не привело для меня: xargs -n 1 -P 8 wget -P $localFileURL $cleanURL - person Ricky; 12.05.2014
comment
У вас есть правильный ответ. Вот что я выбрал: URLS=$(cat ./urls) && echo "$URLS" | xargs -n 1 -P 8 wget --no-cache --no-cookies --timeout=3 --retry-connrefused --random-wait --user-agent=Fetchr/1.3.0 - person Justin; 25.02.2016
comment
Я знаю, что это немного устарело, но есть ли способ объединить группы выходов? Как в, группировать вывод параллельно? В настоящее время вывод выполняется тем, что было запущено первым, поэтому информацию можно разделить. - person DomainsFeatured; 14.09.2016
comment
@Ricky С помощью GNU Parallel вы можете: parallel wget -O {1} {2} ::: file1 file2 ::: + url1 url2 - person Ole Tange; 02.10.2016
comment
@DomainsFeatured Для xargs нет возможности сделать это. Это одна из причин разработки GNU Parallel. - person Ole Tange; 02.10.2016
comment
Зачем использовать -n 1 и разветвлять процесс для каждого отдельного элемента? Просто оставьте это, используя только -P 8, и xargs втиснет столько элементов, сколько может поместиться в командной строке для 8 различных одновременных процессов wget. - person Hitechcomputergeek; 01.06.2017
comment
@Justin Вы можете просто использовать cat ./urls | xargs -n 1 -P 8 wget [...] или даже лучше xargs -a ./urls -n 1 -P 8 wget [...] вместо чтения файла в переменную. - person Hitechcomputergeek; 01.06.2017
comment
Как мне создать URL_LIST в переменной цикла for, чтобы xargs мог анализировать его как отдельные URL-адреса? Мне добавить или \ n? - person Jim; 03.09.2018
comment
Какая версия wget используется в примере? Из моей справочной страницы wget (1.19.4): -P prefix --directory-prefix = prefix Установить префикс каталога в качестве префикса. Префикс каталога - это каталог, в котором будут сохранены все другие файлы и подкаталоги, то есть верхняя часть дерева поиска. По умолчанию это. (текущий каталог). - person ka3ak; 29.09.2018
comment
@Damon есть ли способ показать ход этого процесса во время работы? - person Blademaster; 22.07.2021

Простое выполнение заданий в фоновом режиме не является масштабируемым решением: если вы получаете 10000 URL-адресов, вы, вероятно, захотите получить только несколько (скажем, 100) параллельно. GNU Parallel создан для этого:

seq 10000 | parallel -j100 wget https://www.example.com/page{}.html

Дополнительные примеры см. На странице руководства: http://www.gnu.org/software/parallel/man.html#example__download_10_images_for_each_of_the_past_30_days

person Ole Tange    schedule 02.10.2011
comment
Извините, мне сейчас нечего скачивать, но обязательно в будущем. Предполагая, что я запустил seq 30 | parallel -j5 mkdir / tmp / {} Следует ли создавать 30 папок / tmp / 1 / tmp / 2 и т. д.? Если да, то в моей системе этого не происходит. - person ka3ak; 29.09.2018
comment
@ ka3ak Возможно, вы нашли ошибку. Следуйте: gnu.org/software/parallel/man.html#REPORTING -BUGS - person Ole Tange; 29.09.2018
comment
@OleTange Похоже, в моей системе был предустановлен еще один инструмент с таким же именем. У него даже была опция -j для заданий. Я просто запустил sudo apt install параллельно, чтобы установить нужный. - person ka3ak; 30.09.2018

Вы можете использовать -b вариант:

wget -b "https://www.example.com/page$i.html"

Если вам не нужны файлы журнала, добавьте опцию -o /dev/null.

-o FILE  log messages to FILE.
person uzsolt    schedule 28.09.2011
comment
Нет, все в порядке - проверьте страницу руководства ('-o logfile ...'). - person uzsolt; 25.11.2013
comment
Извините, я не правильно прочитал. Я думал, вы сказали, что если вам не нужны выходные файлы, добавьте параметр -o. Потому что я так и сделал, и в итоге в / root оказались сотни тысяч файлов. Спасибо за разъяснения. - person arrayown; 26.11.2013

Добавление амперсанда к команде заставляет ее работать в фоновом режиме

for i in {1..42}
do
    wget "https://www.example.com/page$i.html" &
done
person Jack Edmonds    schedule 28.09.2011

wget версии 2 реализует несколько подключений.

https://github.com/rockdaboot/wget2

person user9869932    schedule 23.09.2016