lunes, 28 de diciembre de 2009

Crawler en Perl

Viendo la ilusión que tiene mi hija en Navidad me he puesto nostálgico recordando viejos tiempos, y como siempre que me pongo nostálgico, pues desempolvo algo del pasado...

En esta ocasión le ha tocado al Perl. No es que haya sido nunca un experto del Perl, (lenguaje interpretado, generalmente poco eficiente en cuanto a recursos) más bien me tiraba por la rama contraria, el ASM (lenguaje compilado, muy eficiente en cuanto a recursos) pues lo que más me apasionaba era conseguir grandes resultados con recursos limitados.

Aunque hoy en día las cosas han cambiado, los recursos que antiguamente eran caros (el hardware) en la actualidad no lo son tanto, además de incrementar increiblemente su potencia, y en cambio el mayor coste de un proyecto tecnológico se va a los recursos humanos.

Es por ello que dónde antes mirabas al C y al ASM, en la actualidad quizás interesa más mirar al Perl y similares.

Y qué mejor para desempolvar el Perl que intentar hacer un sencillo Crawler, algo que ya vimos en el post ¿Cómo se hace... un crawler? y que ya implementamos en C#.

Veamos aquí un sencillo código para extraer los enlaces de una página Web dada para poder luego recorrerlos recursivamente como hacíamos en aquél artículo:


#!/usr/bin/perl
use LWP::Simple;
use LWP::UserAgent;
use HTTP::Request;
use HTTP::Response;
use HTML::LinkExtor;


$URL = shift(@ARGV);

$contents = get($URL);

$browser = LWP::UserAgent->new();
$browser->timeout(10);

my $request = HTTP::Request->new(GET => $URL);
my $response = $browser->request($request);
if ($response->is_error()) {printf "%s\n", $response->status_line;}

$contents = $response->content();

my ($page_parser) = HTML::LinkExtor->new(undef, $URL);
$page_parser->parse($contents)->eof;
@links = $page_parser->links;

foreach $link (@links)
{
$mylink = "$$link[2]";

if (IsWebPage($mylink))
{
print $mylink."\n";
}
# print "$$link[2]\n";
#print "$$link[2]"."\n";
}

sub IsWebPage
{
$myLink = $_[0];

if($mylink =~ /mailto:/i){return 0;}
if($mylink =~ /\.gif/i){return 0;}
if($mylink =~ /\.jpg/i){return 0;}
if($mylink =~ /\.pdf/i){return 0;}
if($mylink =~ /\.bmp/i){return 0;}
if($mylink =~ /\.jpeg/i){return 0;}
if($mylink =~ /javascript:/i){return 0;}
if($mylink =~ /\.css/i){return 0;}
if($mylink =~ /\.js/i){return 0;}
if($mylink =~ /\.cgi/i){return 0;}
if($mylink =~ /\.ico/i){return 0;}
if($mylink =~ /\.g/i){return 0;}
if($mylink =~ /clsid:/i){return 0;}
if($mylink =~ /file:/i){return 0;}
if($mylink =~ /\.rar/i){return 0;}
if($mylink =~ /\.zip/i){return 0;}
if($mylink =~ /\.doc/i){return 0;}
if($mylink =~ /\.ppt/i){return 0;}
if($mylink =~ /\.pps/i){return 0;}
if($mylink =~ /\.png/i){return 0;}

return 1;
}




Algunas cosas a comentar son el procedimiento IsWebPage, que se puede completar mucho más, que se basa en una serie de if (no existe una estructura switch en perl, aunque sí en módulos externos) dónde se comprueba la aparición, mediante sencillas expresiones regulares case insensitive, de determinadas cadenas.

Otra cosa interesante son las librerías de LWP y HTTP que como veis permite el acceso a las páginas y a su contenido de manera extremadamente sencilla, solucionando algunos problemas que aparecen con otros lenguajes, por ejemplo C#, con las rutas relativas obtenidas dentro del html, y que Perl devuelve como rutas absolutas. Esto es, por ejemplo en la página http://www.corex.es puede haber un enlace del tipo /zonacomercial.aspx que es relativa a http://www.corex.es pero que un crawler como el visto en C# puede devolver cosas varias como /zonacomercial.aspx o about:blank//zonacomercial.aspx, siendo la primera no rastreable y la segunda incluso errónea, y que hay que tratar manualmente. En cambio el script Perl devolvería http://www.corex.es/zonacomercial.aspx

Con este sencillo código que podemos ejecutar en cualquier terminal Linux únicamente grabándolo con un editor de texto (pe. llamándolo crawler) y dándole permisos de ejecución (pe. chmod 777 crawler), ejecutándolo posteriormente pasándole como parámetro la página a descargar, por ejemplo:

./crawler http://grupofivasa.blogspot.com

Esperemos que sirva de base para poder crear scripts más completos y complejos como veis con una sencillez exquisita.

2 comentarios:

  1. No está mal, aunque leyendo la documentación de los módulos queda más corto...

    #!/usr/bin/perl
    use strict;
    use LWP::Simple;
    use HTML::LinkExtor;

    my $URL = shift @ARGV or die;

    my $parser = HTML::LinkExtor->new( \&IsWebPage, $URL );

    $parser->parse(get $URL);

    sub IsWebPage {
    my ($tag, %attr) = @_;
    return if $tag ne 'a';
    print "$attr{href}\n";
    }

    ResponderEliminar
  2. Permíteme que te dija Joaquín que eres un crack.

    Si ya con mi código pretendía mostrar la sencillez de esta tarea en Perl frente a otros lenguajes como C# (también Java o C++), con tu código aún queda más clara esta diferencia.

    ¡Gracias por tu comentario!

    ResponderEliminar