¡Ya estamos en Twitter!

Perl en Español

  1. Home
  2. Tutoriales
  3. Foro
  4. Artículos
  5. Donativos
  6. Publicidad

Creando un buscador con perl

por Uriel Lizama

Introducción

Hace tiempo navegando por la web, me tope con un tutorial Joseph Ryan que hablaba acerca de como crear un buscador con perl.

El tutorial se me hizo muy sencillo y a la vez bastante poderoso. Por eso decidí hacerle una traducción, aparte de ello he realizado algunos cambios para que pueda ser utilizado de manera correcta en un sitio web.

Al final de este tutorial sabrás como crear un buscador para tu sitio web, útil para buscar archivos que contengan ciertas palabras.

Bienvenido a CGI

Una de las muchas cosas que perl hace bien es el procesamiento de texto. Debido a esto, perl es especialmente bueno procesando datos al Internet por medio de CGI.

¿Qué es CGI? CGI son las siglas de "Common Gateway Interface". Es una manera de recibir y enviar información a un servidos. El CGI tiene muchos usos; puede hacer desde recibir información desde un database hasta crear un robot con la capacidad de navegar por toda la web.

Perl nos brinda un módulo maravilloso que se llama CGI. Este módulo CGI es parte de la distribución de perl, por lo que viene en todas las versiones de perl. Este módulo nos hace realmente sencillo el manejo de datos, y toma en consideración varios errores y problemas de seguridad que uno podría pasar por alto.

Creando nuestro HTML

Antes de nada necesitamos hacer nuestra forma. Así que abre tu editor de HTML o cualquier editor de texto que tengas y ponle la siguiente forma de HTML:

<form action="/cgi-bin/buscador.cgi" method="post">
<input type="text" name="query" size="50">
<input type="submit">
</form>

Esta es una pequeña forma que tiene un campo donde se van a poner las palabras que queremos buscar, y el botón de "Submit" que enviará nuestra información al CGI especificado en "action" dentro de la etiqueta FORM.

Dentro de nuestro script

Empecemos a trabajar ahora en nuestro CGI, al cual le vamos a poner el nombre de "buscador.cgi".

Nuestra primera línea como siempre es la ruta de nuestro perl. Si nos sabes cual es la ruta de tu perl, te recomiendo que contactes con tu administrador para que te diga cual es.

Después, necesitamos cargar los módulos y pragmas que vamos a usar. En este caso vamos a habilitar los "warnings" en perl y usar el pragma strict. De esta manera nos forzamos a crear código limpio y seguro. Y finalmente cargamos el módulo CGI. Entonces por el momento tenemos algo así:

#!perl -w

use strict;
use CGI qw(:standard);

Como ves en la primera línea donde pusimos la ruta del intérprete de perl, pusimos un símbolo de "-w", esto hace que se habiliten los "warnings" en perl.

Ahora necesitamos recoger la información enviada por medio de la forma que hicimos anteriormente. Podemos hacer esto con la ayuda de la función param de nuestro módulo CGI:

my $query = param("query");

La función para recibe un argumento, que es el nombre de la variable que queremos recibir, en este caso como en al campo de texto de la búsqueda le pusimos el nombre de "query", esto es lo que pedimos.

Al ejecutar la función, si el módulo CGI encuentra el elemento, regresa su valor y lo pone en nuestra variable "$query". Siempre es una buena idea poner el mismo nombre a tus variables que a las de tu forma, para evitarte confusiones más adelante.

Lo siguiente que vamos a hacer es inicializar nuestras variables de configuración. Estás variables contendran la información necesaria para realizar nuestra búsqueda:

my $root_dir = '/home/user/public_html';

my $url_dir = 'http://misitio.com';

A la variable $root_dir le debemos asignar la ruta absoluta al directorio en el cual deseamos realizar las búsquedas. Y a la variable $url_dir le asignamos el URL al directorio que vamos a buscar.

NOTA: En ninguno de los dos casos debemos de poner el / final.

Finalmente, vamos a imprimir el valor a nuestro navegador para checar si realmente esta funcionando nuestro script. Pero antes de hacer eso necesitamos imprimir el content-type apropiado para que el browser sepa que tipo de información esta recibiendo: html, imagen, película, etc.

Para nuestra suerte, no tenemos que poner el content type exacto, ya que el módulo lo va a hacer por nosotros usando su función para imprimir la cabeza de HTML. Estas funciones se llaman start_html() y end_html() para iniciar y finalizar un HTML respectivamente.

#!perl -w

use strict;
use CGI qw(:standard);

my $query = param("query");


#VARIABLES DE CONFIGURACIÓN
my $root_dir = '/home/user/public_html';

my $url_dir = 'http://misitio.com';


print header();
print start_html();
print $query;
print end_html();

Muy bien, ya tenemos los más básico, pero este script no es muy útil que digamos, por eso continuemos y empecemos a crear nuestro buscador.

Creado nuestro buscador

Vamos a crear un buscador sencillo para nuestro sitio, nada cercano a la complejidad que pueden tener los buscadores como Google o Yahoo. De hecho va a ser tan sencillo nuestro buscador que solo necesitamos unas pocas llamadas de funciones y listo.

Encontrando los archivos

Para que podamos hacer una búsqueda en nuestro sitio, vamos a necesitar encontrar todos los archivos que hay en todos los directorios de nuestro sitio, abriendo los archivos del tipo que necesitamos. Normalmente esto sería algo tedioso y bastante complicado, pero en Perl tenemos el módulo File:Find para que no tengamos que hacerlo de la manera ruda.

Este módulo exporta una función que se llama Find(), que hace una búsqueda de archivos a través de los directorios que existen. Find() toma dos argumentos: una subrutina, la cual ejecutará cada ves que encuentra un archivo y un directorio inicial. Del directorio inicial, Find() empezará a moverse a través de cada sub-directorio y archivo regresando un montón de información, como el nombre de archivo, ruta, etc.

Inicialicemos nuestro script:

#!perl -w

use strict;
use CGI qw(:standard);
use File::Find;

my $query = param("query");

#VARIABLES DE CONFIGURACIÓN
my $root_dir = '/home/user/public_html';

my $url_dir = 'http://misitio.com';


print header();
print start_html();

Básicamente lo mismo que antes, solamente que ahora agregamos la línea para cargar el módulo File::Find. Ahora imprimamos un título en el HTML:

print "\n<h4>Para la búsqueda $query, estos resultados se encontraron:</h4>\n<ol>\n";

Ahora, hacemos el proceso de búsqueda. Donde vamos a usar la función find:

find( sub {

},
$root_dir);

El primer argumento de la función es una referencia a una subrutina, en este caso vamos a usar lo que se llama una subrutina anónima, es decir ahí vamos a poner la subrutina completa. El segundo argumento es el directorio donde queremos empezar a buscar los archivos.

Ahora sigamos creando nuestra subrutina anónima. En primer lugar, queremos saltarnos los archivos que empiecen con un punto (como podrían ser los archivos.htaccess). También queremos saltarnos los archivos que no tengan la extensión .html o .htm.

find( sub
{
return if ($_ =~ /^\./);
return unless ($_ =~ /\.htm(l?)$/i);
},
$root_dir);


Como find hace un loop, haciendo que la función no regrese nada es como decir "muévete al siguiente archivo". Lo que hacemos es llamar dos expresiones regulares muy sencillas. La primera checa si el archivo empieza(^) con un punto (\.). La otra checa si el nombre del archivo tiene un punto (\.) seguido de (htm) con la l opcional (l?), esta letra la hacemos opcional para que el archivo pueda ser .html o .htm. El modificador i al final de esta expresión hace que no sea sensible a mayúsculas.

Probando los archivos

Después de que sabemos que los archivos si son HTML, necesitamos hacer unas verificaciones en los archivos, para eso usamos la función stat() de perl. File::Find nos brinda una variable con la ruta completa del archivo que se llama $File::Find::name, entonces eso es lo que vamos a enviar a la función stat. Después de ejecutar esta función, varias pruebas se nos hacen disponibles. Las dos pruebas que vamos a usar son -d y -r. -d checa si el archivo actual es un directorio, mientras que -r checa si el archivo se puede leer.

find( sub
{
return if ($_ =~ /^\./);
return unless ($_ =~ /\.htm(l?)/i);
stat $File::Find::name;
return if -d;
return unless -r;
},
$root_dir);


Buscando los archivos

Ahora, vamos a checar si el archivo contiene los términos que estamos buscando. Para hacer esto necesitamos abrir el archivo y poner su contenido en una variable de cadena.

undef $/;
find( sub
{
return if ($_ =~ /^\./);
return unless ($_ =~ /\.htm(l?)/i);
stat $File::Find::name;
return if -d;
return unless -r;

open(FILE, "< $File::Find::name") or return;
my $string = <FILE>;
close (FILE);
},
$root_dir);

La primera línea donde pusimos "undef $/;" nos sirve para que el contenido que leemos del archivo no los ponga en una cadena en vez de un arreglo.

El símbolo de mayor que (<) antes del nombre del archivo es para asegurarnos que el archivo se abra solamente para leerlo.

Ahora veamos si el archivo contiene nuestro término de búsqueda:

undef $/;
find( sub
{
return if ($_ =~ /^\./);

return unless ($_ =~ /\.htm(l?)/i);
stat $File::Find::name;
return if -d;
return unless -r;

open(file, "< $File::Find::name") or return;
my $string = <FILE>;
close (FILE);

return unless ($string =~ /\Q$query\E/i);
},
$root_dir);

Una sencilla expresión regular es la que usamos para determinar sí la variable $query esta en el contenido del archivo en $string. Los \Q y \E son delimitadores que hacen caracteres especiales no-seguros en nuestra cadena, seguros para poder buscarlos con nuestra expresión regular.

Mostrando los resultados

Para este momento ya sabemos si el archivo cumplió con nuestra búsqueda. Sin embargo, antes de que imprimamos el resultado, necesitamos una información adicional: un título para el link.

Primer vamos a crear una nueva variable a la cual le vamos a pone el nombre de $page_title, y poner su valor inicial al nombre del archivo actual. Sin embargo, podríamos ser más específicos, si el archivo esta en HTML entonces lo más seguro es que tenga un título que podemos capturar con una expresión regular:

undef $/;
find( sub
{
return if($_ =~ /^\./);
return unless($_ =~ /\.htm(l?)/i);
stat $File::Find::name;
return if -d;
return unless -r;

open(FILE, "< $File::Find::name") or return;
my $string = <FILE>;
close (FILE);

return unless ($string =~ /\Q$query\E/i);
my $page_title = $_;
if ($string =~ /<title>(.*?)<\/title>/is)
{
$page_title = $1;
}
},
$root_dir);

Si el archivo cumple con nuestra expresión regular el contenido estará en $1, y asignaremos ese valor a la variable $page_title. Si no se cumpliera con nuestra expresión regular, $page_title se quedaría con el valor del nombre del archivo, de esta manera tenemos un título para el link sin importar lo que pase.

Finalmente es hora de desplegar el link:

undef $/;
find( sub
{
return if($_ =~ /^\./);
return unless($_ =~ /\.htm(l?)/i);
stat $File::Find::name;
return if -d;
return unless -r;

open(FILE, "< $File::Find::name") or return;
my $string = <FILE>;
close (FILE);

return unless ($string =~ /\Q$query\E/i);
my $page_title = $_;
if ($string =~ /<title>(.*?)<\/title>/is)
{
$page_title = $1;
}

my $int_dir = $File::Find::dir;
$int_dir =~ s/$root_dir//i;
my $url = $url_dir . $int_dir . "/" . $_;

print "<li><a href=\"$url\">$page_title</a></li>\n";
},
$root_dir);


Código Final

Con la función find completada, terminamos el documento con end_html. De esta manera tenemos un buscador en cerca de 50 líneas de código:

#!perl -w

use strict;
use File::Find;
use CGI qw(:standard);
my $query = param("query");


#VARS DE CONFIGURACIÓN

#LUGAR DONDE QUIERES BUSCAR. NO USES EL / FINAL
my $root_dir = '/home/user/public_html';

#URL DEL DIRECTORIO QUE VAS A BUSCAR. SIN EL / FINAL
my $url_dir = 'http://misitio.com';


print header();
print start_html();
print "\n<p>Para la búsqueda $query, estos resultados se encontraron:</p>\n<ol>\n";

undef $/;

find( sub {
return if($_ =~ /^\./);
return unless($_ =~ /\.htm(l)?/i);
stat $File::Find::name;
return if -d;
return unless -r;

open(FILE, "< $File::Find::name") or return;
my $string = <FILE>;
close (FILE);

return unless ($string =~ /\Q$query\E/i);
my $page_title = $_;

if ($string =~ /<title>(.*?)<\/title>/is){
$page_title = $1;
}

my $int_dir = $File::Find::dir;
$int_dir =~ s/$root_dir//i;
my $url = $url_dir . $int_dir . "/" . $_;

print "<li><a href=\"$url\">$page_title</a></li>\n";
}, $root_dir);

print "</ol>\n";

print end_html();


exit(1);



Actualización

03 de Octubre del 2005

En base al siguiente post en el foro de Perl en Español el tutorial fue actualizado y el código mejorado.

Dentro de los cambios que se hicieron al código fue la correción de un error que hacía que no se buscaran archivos con extensión .htm.

Aparte de ello se optimizó el buscador para ser usado dentro de sitios web, haciendo que en los resultados se muestren links a los archivos HTML en base de URLs y no rutas absolutas.


¿Quiéres más tutoriales como este? Escribir tutoriales toma una gran cantidad de tiempo y esfuerzo. Si este tutorial te ayudó a aprender o a solucionar algo, por favor considera dejar alguna donación en apoyo a Perl en Español.

Cliquea en el botón de abajo para dejar tu donación por medio de PayPal.

Comparte:
Categorías de Tutoriales:
En Nuestros Foros:

    Software error:

    junk after document element at line 1, column 32, byte 32 at /usr/lib64/perl5/vendor_perl/XML/Parser.pm line 187.
    

    For help, please send mail to the webmaster ([email protected]), giving this error message and the time and date of the error.

  • Entra a los foros »
Socializa:
Síguenos por Twitter

Suscríbete GRATUITAMENTE al Boletín de Perl en Español

Perl en Español es mantenido con Movable Type
Todo el contenido de Perl en Español está bajo una licencia CC:
Creative Commons License