Retrouvez cet article dans : Linux Magazine 88
Le site
http://search.cpan.org/ fournit une interface particulièrement agréable vers CPAN.
Prenons un exemple précis : si un module a été maintenu par plusieurs auteurs au fil du temps, les différentes distributions seront disséminées dans les répertoires de chacun de ceux qui les auront déposées sur CPAN. Sur Search CPAN, une URL de la forme
http://search.cpan.org/dist/Maypole/ (distribution dont plusieurs personnes se sont occupées au fil du temps) pointera vers la version la plus récente, mais la page obtenue pointera également vers les pages de toutes les autres versions, quel qu’en soit l’auteur.
Si vous disposez déjà d’un mini-CPAN (voir les Perles 8 et 9, parues il y déjà quelque temps), vous avez déjà chez vous les toutes dernières versions de chaque distribution. Le script qui suit va vous permettre de récupérer les versions plus anciennes qui n’ont pas encore été supprimées du CPAN.
Visite automatisée de Search CPAN
Si
NOM est le nom de la distribution recherchée, alors la page qui la concerne sur Search CPAN est http://search.cpan.org/dist/NOM. Cette page est générée automatiquement par un programme, nous pouvons donc être sûrs que le format sera identique pour toutes les pages, quelle que soit la valeur de
NOM.
En prenant par exemple
Maypole, la page en question contient :
- un lien Download qui pointe vers la version la plus à jour de la distribution ;
<tr>
<td class=label>This Release</td>
<td class=cell>Maypole-2.11_pre1</td>
<td><small> [<a href="/CPAN/authors/id/T/TE/TEEJAY/Maypole-2.11_pre1.tar.gz">Download</a>]
[<a href="/src/TEEJAY/Maypole-2.11_pre1/">Browse</a>] </small></td>
<td><small>14 Apr 2006</small>
</td>
</tr>
- et un formulaire avec une liste de toutes les autres versions disponibles de la distribution.
<tr>
<td class=label>Other Releases</td>
<td class=cell colspan=3>
<form action="/redirect">
<select name="url">
<option value="/~teejay/Maypole-2.10/">Maypole-2.10 -- 19 Jul 2005</option>
<option value="/~simonflk/Maypole-2.09/">Maypole-2.09 -- 25 Jan 2005</option>
<option value="/~simonflk/Maypole-2.08/">Maypole-2.08 -- 24 Jan 2005</option>
...
</form></td></tr>
Il n’y a pas besoin de beaucoup expérimenter pour trouver que l’expression régulière
m!href="/(CPAN/.*?)"!g capturera le lien de téléchargement, et
m!<option value="/(.*?)"!g les liens vers les pages des autres versions de la distribution sur Search CPAN.
En testant avec la distribution
perl, j’ai pu constater la présence de la distribution
perl-5.6-info qui n’est pas une distribution Perl, mais la conversion de la documentation de Perl 5.6 au format TeXinfo. Afin d’éviter les téléchargements inutiles (et des problèmes avec grep, voir plus loin), j’ai donc ajouté une option de filtrage qui prend en paramètre une expression régulière et qui ne téléchargera pas les URL correspondantes ni n’imprimera les liens vers les archives associées.
#!/usr/bin/perl
use strict;
use warnings;
use LWP::Simple;
use List::Util qw( first );
use Getopt::Long;
# option de ligne de commande
my %conf = ( filter => ‘^$’ );
GetOptions( \%conf, ‘filter=s’ )
or die 'Usage: all_dist [--filter re] dist ...';
my $base = 'http://search.cpan.org';
$|++; # vidage automatique du tampon de sortie
my %tgz;
for my $name (@ARGV) {
# le point de départ de la distribution
$tgz{"dist/$name"} ||= ‘’;
while ( my $dist
= first { !$tgz{$_} && !/$conf{filter}/o } keys %tgz )
{
# récupère le contenu de la page
my $content = get("$base/$dist");
# le lien de téléchargement (avec filtrage)
print grep { !/$conf{filter}/o }
map {"$base/$_\n"} ( $tgz{$dist} )
= $content =~ m!href="/(CPAN/.*?)"!g;
# les “autres” liens
$tgz{$_} ||= '' for $content =~ m!<option value=”/(.*?)”!g;
}
}
Le script est finalement assez simple : la table de hachage
%tgz a pour clés les URL des pages sur search.cpan.org et pour valeurs les fichiers pointés par le lien Download de chaque page. La condition de la boucle
while() récupère la première clé qui ne correspond pas à l’expression de filtrage (stockée dans
$conf{filter}) et dont la valeur est la chaîne vide, c’est-à-dire les pages dont on n’a pas encore récupéré l’URL du lien
Download.
Au début de chaque tour de boucle sur
@ARGV, on initialise donc
%tgz avec
$tgz{"dist/$name"} ||= ‘’ (c’est-à-dire qu’on s’assure que la clé existe en lui mettant une valeur vide, sauf si une valeur vraie existe déjà).
get("$base/$dist") récupère le contenu de la page avec
LWP::Simple, sur lequel on peut ensuite utiliser nos deux expressions régulières. La première est utilisée pour extraire le lien de téléchargement, la seconde (l’expression de filtrage stockée dans
$conf{filter}) permet de vérifier s’il lui correspond afin de ne pas l’afficher. Notez l’utilisation de
m//g en contexte de liste pour récupérer le résultat de la capture. La première fois, le contexte est forcé avec les parenthèses dans
( $tgz{$dist} ) = $content =~ m!href="/(CPAN/.*?)"!g), puis c’est ensuite le for qui impose un contexte de liste.
Une petite remarque sur un détail qui m’a causé pas mal de problèmes lors de la création de ce script :
$base ne contient pas de
/ et les expressions régulières ne capturent pas le premier /. D’une part, cela améliore la lisibilité de "$base/$dist"), et d’autre part un double slash (//) dans l’URL va poser problème à Search CPAN. En effet, si pour lui http://search.cpan.org/dist/Maypole et http://search.cpan.org//dist/Maypole sont identiques, en revanche http://search.cpan.org//~simonflk/Maypole-2.08/ (notez le //) provoque une erreur 500 (et le rigolo message suivant du serveur : Whoops, it seems that I choked while trying to process your request. Please forgive me its (sic) the cook here, he is still rather new.).
Utilisation du script
J’ai voulu tester l’installation de toutes les versions de Perl disponibles, et j’ai donc utilisé ce script de la façon suivante :
Pour télécharger les fichiers sans avoir à attendre que Perl ait fini de tout récupérer, il faut forcer chaque élément de la chaîne à afficher ou à lire les lignes disponibles dès que possible. On passe donc en mode de tampon de ligne (line-buffered), pour Perl avec $|++, pour grep avec l’option --line-buffered et en forçant xargs à passer les paramètres un par un à wget.
C’est pour éviter de passer par ce grep --line-buffered que j’ai ajouté l’option --filter. La commande devient donc :
Pour lancer plusieurs wget en parallèle, on peut utiliser l’option -P de xargs, qui permet de définir le nombre de processus lancés en parallèle (par défaut 1, 0 signifiant " pas de limite "). On peut également jouer avec l’option -n pour passer plusieurs URL à une même instance de wget. Bien sûr, on peut également sauver toutes ces URL dans un fichier perl.lst, faire un peu de tri manuellement et appeler wget -i perl.lst. Notez bien que les URL renvoyées par Search CPAN redirigeront vos requêtes vers les divers miroirs CPAN existant dans votre région (j’ai ainsi eu l’occasion de passer par à peu près tous les miroirs français de CPAN quand j’ai récupéré toutes les distributions de Perl disponibles).
Le fichier en question donne les liens vers 37 versions de Perl, chez 12 auteurs différents.
À vous !
Envoyez vos perles à perles@mongueurs.net. Elles seront peut-être publiées dans un prochain numéro de Linux Magazine.