Descargando Páginas

La librería LWP (The World-Wide Web library for Perl) proporciona clases y funciones que permiten escribir clientes WWW. La librería también contiene clases y métodos que dan soporte a la construcción de servidores HTTP sencillos.

La versión mas sencilla de esta librería la proporciona el módulo LWP::Simple . Como su nombre indica, provee una versión simplificada de la librería. Esto la hace especialmente indicada para ejecutar one-liners: programas Perl que pese a ejecutarse en una sóla línea, normalmente desde la línea de comandos, pueden llegar a realizar tareas de considerable complejidad. Por ejemplo para descargar una página web en el fichero ejercicio.html podemos ejecutar el siguiente comando:

$ perl -MLWP::Simple -e 'getstore("http://nereida.deioc.ull.es/~lhp/perlexamples/node55.html", \
                                   "ejercicio.html")'
$ ls -ltr | tail -3
-rw-r--r--  1 lhp lhp  7957 2005-06-30 10:09 node343.html
-rw-r--r--  1 lhp lhp 18099 2005-06-30 10:09 node211.html
-rw-r--r--  1 lhp lhp  7093 2005-06-30 10:31 ejercicio.html
Si lo que queremos es descargar un buen número de páginas puede ser una buena idea paralelizar la descarga para así aumentar el ancho de banda. Para ello podemos usar el módulo de threads presentado en el capítulo 18 o cualquier otro que provea las funcionalidades requeridas, por ejemplo el módulo Parallel::ForkManager. Veamos un ejemplo de uso:
$ cat -n parfork.pl
 1  #!/usr/bin/perl -w
 2  use Parallel::ForkManager;
 3  use LWP::Simple;
 4  my $pm=new Parallel::ForkManager(10);
 5  for my $link (@ARGV) {
 6    $pm->start and next;
 7    my ($fn) = $link =~ /^.*\/(.*?)$/; # contexto de lista
 8    if (!$fn) { # $fn  es $1
 9      warn "Cannot determine filename from $fn\n";
10    } else {
11      # Se encontró una cadena despúes del último /
12      print "Getting $fn from $link\n";
13      my $rc=getstore($link,$fn);
14      print "$link downloaded. response code: $rc\n";
15    };
16    $pm->finish;
17  };

En la línea 4 se crea un objeto gestor de procesos para un máximo de 10 procesos. En la línea 6 se hace uso de $pm->start para hacer el fork. El método devuelve 0 al proceso hijo y el PID del hijo al padre. Asi pues, al ser evaluada la expresión en circuito corto, el next hace que el proceso padre se salte la tarea de descarga (líneas 7-14). La llamada pm->start produce la muerte prematura del programa si falla el fork. El valor retornado por la llamada a getstore es el código HTTP de respuesta proporcionado por el servidor. Por último la llamada a finish en la línea 16 termina el proceso hijo. Es posible usar el método wait_all_children para esperar por la terminación de todos los procesos que han sido arrancados por el objeto $pm.

Sigue un ejemplo de ejecución:

$ parfork.pl 'http://nereida.deioc.ull.es/~pl/pl0405/node24.html'\
             'http://nereida.deioc.ull.es/~lhp/perlexamples/node343.html'
Getting node24.html from http://nereida.deioc.ull.es/~pl/pl0405/node24.html
Getting node343.html from http://nereida.deioc.ull.es/~lhp/perlexamples/node343.html
http://nereida.deioc.ull.es/~lhp/perlexamples/node343.html downloaded. response code: 200
http://nereida.deioc.ull.es/~pl/pl0405/node24.html downloaded. response code: 200
$ ls -ltr | tail -4
drwxr-xr-x  2 lhp lhp  4096 2005-06-02 14:22 05
-rwxr-xr-x  1 lhp lhp   420 2005-06-30 10:55 parfork.pl
-rw-r--r--  1 lhp lhp  7957 2005-06-30 11:05 node343.html
-rw-r--r--  1 lhp lhp  7744 2005-06-30 11:05 node24.html
$

Casiano Rodríguez León
2012-02-29