Búsqueda en formularios con get

Nuestro primer ejemplo considera el buscador de la Universidad de La Laguna (ULL), el cual permite la búsqueda de personal vinculado a la universidad.

En primer lugar, visite la página en la que esta ubicado: http://www.ccti.ull.es/bd/. La figura 10.1 muestra la interfaz que percibe un usuario humano.

Figura 10.1: El buscador de personas de la ULL
\begin{figure}\centerline{\epsfig{file=search_ull.eps, height=12cm}}\end{figure}

La estructura de la página que contiene al formulario es como sigue:

<form method="GET" action="index.php3">
<font face="Verdana" size="2"><span class="normal">Buscar </span></font>
<input class="ObjetosTexto" type="text" name="cadena" size="20" maxlength="20" 
       value ="" title="Cadena que desea buscar">
<font face="Verdana" size="2"><span class="normal"> en </span></font>
<font class="ObjetosSelect">
<select title="Concepto por el que se busca la información" 
        class ="ObjetosSelect" name="donde" size="1">    
  <option  value="1">el correo eléctrónico</option>    
  <option selected value="2">el nombre y apellidos</option>    
  <option  value="3">la dirección de trabajo</option>    
  <option  value="4">la unidad organizativa</option>    
  <option  value="5">la relación con la unidad organizativa</option>
</select>
</font>
<input type="submit" value="Buscar" name="BotonBuscar" class="ObjetosBoton"></p>
</form>

El atributo method indica si usamos GET o POST al enviar el formulario. El atributo action determina la URL que recibirá los datos. Los componentes de un formulario son text-fields, listas drop-down, checkboxes, etc. cada uno identificado con un name. Aqui el campo input define la cadena a buscar cuando se emita el formulario al seleccionar el botón Buscar. La etiqueta select permite crear un menú desplegable utilizando la lista de elementos etiquetados con option. Nos concentraremos en la selección 2 del menú donde para nuestra búsqueda: buscar por nombre y apellidos.

Una vez realizada la búsqueda, la estructura de la página de salida es tal que contiene al menos tres tablas cuando se encuentran resultados. La tercera tabla es la que contiene los resultados. Una fila tiene un aspecto parecido al siguiente:

<table border="0" width="100%">
<tr>
  <td>
  <a class="all" href="mailto:xxxx@ull.es" title="Póngase en contacto con el usuario">
  xxxx@ull.es</a>
  </td>
  <td> María Teresa .....  ....... </td>
  <td> ** Dirección de trabajo ** </font>
  </td>
  <td> Departamento de Anatomía, Anatomía Patológica e Histología </td>
  <td> Docentes y personal investigador </td>
  <td> <a name="L1724"> </a><a href="#L1724"> Teléfonos:</a><br>
    1er tlfn contacto: 922319444<br>
    Fax trabajo: 922319999<br>
  </td>
</tr>

Solución:

#! /usr/bin/perl -w

use URI; 
use LWP::UserAgent;
use HTML::TreeBuilder 3;

die "Uso $0 \"nombre\"\n" unless @ARGV == 1; 
my $name = $ARGV[0]; 
my $url = URI->new('http://www.ccti.ull.es/bd/index.php3'); 
$url->query_form('cadena'=>$name, 'donde'=>'2', 'BotonBuscar'=>'Buscar'); 
#
#print $url, "\n";
Un URL nos dice como obtener algo: utiliza HTTP con este servidor y pídele esto o bien conéctate por ftp a esta máquina y carga este fichero. La llamada anterior

$url = URI->new('http://www.ccti.ull.es/bd/index.php3')

crea un objeto URI representando una URL.

La forma en la que enviamos el formulario usando LWP depende de si se trata de una acción GET o POST. Si es un GET se procede como en el ejemplo: se crea una URL con los datos codíficados usando el método $url->query_form('cadena'=>$name, 'donde'=>'2', 'BotonBuscar'=>'Buscar'). y después se procede a llamar al método get. En el código que nos ocupa, se crea el agente y se llama al método get en una sóla expresión:

 
my $response = LWP::UserAgent->new->get( $url ); 
die "Error: ", $response->status_line unless $response->is_success;

El constructor new de la clase LWP::UserAgent nos permite utilizar un objeto de la clase agente el cual nos permite gestionar conexiones HTTP y realizar peticiones a los servidores HTTP.

Si el método es POST se debe llamar al método $browser->post() pasándole como argumento una referencia al vector de parámetros del formulario.

De hecho, si estuvieramos buscando por una cadena fija, digamos Casiano, la línea anterior podría ser sustituida por:

$response = LWP::UserAgent->new->get(http://www.ccti.ull.es/bd/index.php3?cadena=Casiano&donde=2)

Acto seguido construimos el árbol de análisis sintáctico:

my $root = HTML::TreeBuilder->new_from_content($response->content);

#$root->dump;

my @tables = $root->find_by_tag_name('table');
La llamada HTML::TreeBuilder->new_from_content($response->content) crea el árbol sintáctico del HTML proporcionado por el objeto agente y que fué dejado en el objeto $response. El objeto $response es de la clase HTTP::Response.

En general la sintáxis es:

HTML::TreeBuilder->new_from_content([ string, ... ])

Si usamos en vez new_from_file construiríamos el árbol para el fichero pasado como argumento.

El método find_by_tag_name devuelve la lista de nodos etiquetados con el correspondiente tag pasado como argumento. En el ejemplo, la llamada $root->find_by_tag_name('table') devuelve una lista con los nodos del árbol que estan etiquetados con la marca table.

Si se descomenta la línea #$root->dump; se produce un volcado del árbol sintáctico del HTML. La estructura del volcado (abreviada) es similar a esto:

<html> @0
  <head> @0.0
    <title> @0.0.0
     ...... 
    <link href="styles.css" name="style1" rel="stylesheet" type="text/css"> @0.0.1
  <body alink="#800000" link="#000080" vlink="#800000"> @0.1
     ......
Cada nodo aparece etiquetado con su nivel de anidamiento.

a continuación el programa comprueba que existen tres tablas en el árbol y procede a recorrer el subárbol de la segunda tabla:

if (@tables > 2) {
  my $result = $tables[2];

  my @stack = ($result);
  while (@stack) {
    my $node = shift @stack;
    if (ref $node) {
      unshift @stack, $node->content_list;
    } else {
      print $node,"\n";
    }
  }
} elsif ($root->as_text =~ /Consulta demasiado/) {
  print "Consulta demasiado extensa para $name\n";
}
else {
  print "No se encontró $name";
}
$root->delete;
Como se ha dicho la tercera tabla (my $result = $tables[2]) es la que contiene los resultados. La llamada al método $node->content_list devuelve la lista de los hijos del nodo. Asi pues, la orden unshift coloca dichos nodos al comienzo de @stack (por cierto, que debería haberse llamado @queue ya que es usada como una cola). Si el nodo $node no es una referencia es que se trata de una hoja, en cuyo caso contiene información y la mostramos.

Ejemplo de ejecución:

$ ./search_ull.pl Coromoto
xxxxx@ull.es
Lourdes Coromoto Pérez González
** Dirección de trabajo **
Departamento de Filos. de la Memoria  y Propología.
Docentes y personal investigador
Teléfonos:
1er tlfn contacto: 922444444
Fax trabajo: 922555555


yyyyy@ull.es
(Visite su página)
Coromoto González Pérez
c/Astrofísico Paco. Sánchez s/n.
Departamento de FitoPato, Postulo. y Pomputación
Docentes y personal investigador
Teléfonos:
1er tlfn contacto: 922406090
Fax trabajo: 922789341

Sigue el código completo del programa:

 1 #! /usr/bin/perl -w
 2 
 3 use URI; 
 4 use LWP::UserAgent;
 5 use HTML::TreeBuilder 3;
 6 
 7 die "Uso $0 \"nombre\"\n" unless @ARGV == 1; 
 8 my $name = $ARGV[0]; 
 9 my $url = URI->new('http://www.ccti.ull.es/bd/index.php3'); 
10 $url->query_form('cadena'=>$name, 'donde'=>'2', 'BotonBuscar'=>'Buscar'); 
11 #
12 #print $url, "\n"; 
13  
14 my $response = LWP::UserAgent->new->get( $url ); 
15 die "Error: ", $response->status_line unless $response->is_success; 
16 
17 my $root = HTML::TreeBuilder->new_from_content($response->content);
18 
19 #$root->dump;
20 
21 my @tables = $root->find_by_tag_name('table');
22 
23 if (@tables > 2) {
24   my $result = $tables[2];
25 
26   my @stack = ($result);
27   while (@stack) {
28     my $node = shift @stack;
29     if (ref $node) {
30       unshift @stack, $node->content_list;
31     } else {
32       print $node,"\n";
33     }
34   }
35 } elsif ($root->as_text =~ /Consulta demasiado/) {
36   print "Consulta demasiado extensa para $name\n";
37 }
38 else {
39   print "No se encontró $name";
40 }
41 $root->delete;

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