84 votes

Parallèle à wget en Bash

Je récupère un tas de pages relativement petites à partir d'un site web et je me demandais si je pouvais le faire en parallèle avec Bash. Actuellement, mon code ressemble à ceci, mais il prend un certain temps à s'exécuter (je pense que ce qui me ralentit est la latence de la connexion).

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

J'ai entendu parler de l'utilisation de xargs, mais je n'y connais rien et la page de manuel est très confuse. Des idées ? Est-il possible de faire cela en parallèle ? Y a-t-il une autre façon de s'attaquer à ce problème ?

208voto

Damon Points 26437

C'est bien mieux que de pousser wget dans l'arrière-plan à l'aide de & o -b vous pouvez utiliser xargs au même effet, et mieux.

L'avantage est que xargs volonté se synchroniser correctement sans travail supplémentaire. Cela signifie que vous pouvez accéder en toute sécurité aux fichiers téléchargés (en supposant qu'aucune erreur ne se produise). Tous les téléchargements seront terminés (ou auront échoué) une fois que xargs sort, et le code de sortie permet de savoir si tout s'est bien passé. Cette solution est de loin préférable à l'attente laborieuse avec sleep et en testant l'achèvement manuellement.

En supposant que URL_LIST est une variable contenant toutes les URL (peut être construite avec une boucle dans l'exemple de l'OP, mais peut aussi être une liste générée manuellement), en exécutant ceci :

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

passera un argument à la fois ( -n 1 ) à wget et exécutent au maximum 8 wget à la fois ( -P 8 ). xarg revient après la fin du dernier processus créé, ce qui est exactement ce que nous voulions savoir. Aucune astuce supplémentaire n'est nécessaire.

Le "nombre magique" de 8 téléchargements parallèles que j'ai choisi n'est pas figé, mais il s'agit probablement d'un bon compromis. Deux facteurs permettent de "maximiser" une série de téléchargements :

La première consiste à remplir "le câble", c'est-à-dire à utiliser la largeur de bande disponible. Dans des conditions "normales" (le serveur a plus de bande passante que le client), c'est déjà le cas pour un ou deux téléchargements au maximum. Le fait d'ajouter des connexions au problème ne fera qu'entraîner l'abandon de paquets et la mise en œuvre du contrôle de congestion TCP. N avec des téléchargements asymptotiquement 1/N de bande passante chacun, pour le même effet net (moins les paquets abandonnés, moins la récupération de la taille de la fenêtre). L'abandon de paquets est une chose normale dans un réseau IP, c'est ainsi que le contrôle de la congestion est censé fonctionner (même avec une seule connexion), et normalement l'impact est pratiquement nul. Cependant, le fait d'avoir un nombre déraisonnablement élevé de connexions amplifie cet effet, de sorte qu'il peut devenir perceptible. Dans tous les cas, cela ne rend rien plus rapide.

Le deuxième facteur est l'établissement de la connexion et le traitement des demandes. Ici, le fait d'avoir quelques connexions supplémentaires en vol aide vraiment . Le problème auquel on est confronté est la latence de deux allers-retours (typiquement 20-40 ms dans la même zone géographique, 200-300 ms entre continents) plus les quelques 1-2 millisecondes dont le serveur a besoin pour traiter la demande et envoyer une réponse à la socket. Ce n'est pas beaucoup de temps en soi mais multiplié par quelques centaines/milliers de demandes, il s'additionne rapidement.
Le fait d'avoir entre une demi-douzaine et une douzaine de demandes en vol masque en grande partie ou totalement cette latence (elle est toujours présente, mais comme elle se chevauche, elle ne s'additionne pas !) En même temps, le fait de n'avoir que quelques connexions simultanées n'a pas d'effets négatifs, tels qu'une congestion excessive ou l'obligation pour un serveur d'ouvrir de nouveaux processus.

5 votes

C'est certainement la meilleure méthode, car elle utilise xargs, un outil universel, et cette méthode peut être appliquée à de nombreuses autres commandes.

7 votes

Lors du téléchargement de plusieurs fichiers via HTTP, wget peut réutiliser la connexion HTTP grâce au mécanisme Keep-Alive. Lorsque vous lancez un nouveau processus pour chaque fichier, ce mécanisme ne peut pas être utilisé et la connexion (TCP triple way handshake) doit être établie encore et encore. Je suggère donc d'augmenter le paramètre -n à environ 20. Dans la configuration par défaut, le serveur HTTP Apache ne sert que jusqu'à 100 requêtes dans une session maintenue en vie, il n'y a donc probablement pas lieu de dépasser la centaine ici.

2 votes

Excellente réponse, mais que se passe-t-il si je veux passer deux valeurs variables à wget ? Je veux spécifier le chemin de destination ainsi que l'URL. Est-ce toujours possible avec la technique xargs ?

66voto

Ole Tange Points 4907

L'exécution des tâches en arrière-plan n'est pas une solution évolutive : Si vous récupérez 10000 urls, vous ne voudrez probablement en récupérer que quelques-unes (disons 100) en parallèle. GNU Parallel est fait pour cela :

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

Voir la page de manuel pour plus d'exemples : http://www.gnu.org/software/parallel/man.html#example__download_10_images_for_each_of_the_past_30_days

0 votes

Désolé, je n'ai rien à télécharger pour l'instant, mais il faudra que je le fasse à l'avenir, c'est certain. En supposant que je lance seq 30 | parallel -j5 mkdir /tmp/{} Cela devrait-il créer 30 dossiers /tmp/1 /tmp/2 etc. ? Si c'est le cas, il ne le fait pas sur mon système.

0 votes

@ka3ak Vous avez peut-être trouvé un bug. Veuillez suivre : gnu.org/software/parallel/man.html#REPORTING-BUGS

1 votes

@OleTange Il semble qu'un autre outil portant le même nom ait été préinstallé sur mon système. Il avait même l'option -j pour les emplois. J'ai simplement lancé sudo apt install parallel pour installer le bon outil.

9voto

uzsolt Points 1303

Vous pouvez utiliser -b option :

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

Si vous ne voulez pas de fichiers journaux, ajoutez l'option -o /dev/null .

\-o FILE  log messages to FILE.

1 votes

Non, c'est bon - vérifiez la page de manuel ('-o logfile...').

0 votes

Désolé, je n'ai pas lu correctement. Je pensais que vous aviez dit "si vous ne voulez pas de fichiers de sortie, ajoutez l'option -o". Car c'est ce que j'ai fait et je me suis retrouvé avec des centaines de milliers de fichiers dans /Root. Merci de m'avoir éclairé.

6voto

Jack Edmonds Points 10264

L'ajout d'une esperluette à une commande permet de l'exécuter en arrière-plan

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

3voto

julianromera Points 401

wget version 2 met en œuvre des connexions multiples.

https://github.com/rockdaboot/wget2

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X