[LinuxFocus-icon]
Hogar  |  Mapa  |  Indice  |  Busqueda

Noticias | Arca | Enlaces | Sobre LF
convert to palmConvert to GutenPalm
or to PalmDoc

[Photo of the Author]
por Guido Socher (homepage)

Sobre el autor:

A Guido le gusta Perl porque es un lenguaje de scripting muy flexible y rápido. Le gusta el lema de Perl "Hay más de un camino para hacerlo" el cual refleja la libertad y las posibilidades que tienes cuando usas código abierto.



Taducido al español por:
María Pérez <lupeagui(at)hotmail.com>

Contenidos:

 

Administrando HTML con Perl, HTML::TagReader

[Illustration]

Resumen:

Si quieres administrar un sitio web con mas de 10 páginas HTML pronto averiguarás que necesitas algunos programas para ayudarte.
La mayoría del software tradicional lee ficheros línea a línea (o carácter a carácter). Desafortunadamente las líneas no tienen significado en ficheros SGML/XML/HTML. Los ficheros SGML/XML/HTML estan basados en etiquetas. HTML::TagReader es un módulo ligero para procesar un fichero mediante etiquetas.

En este artículo se supone que se conoce Perl bastante bien. Mira mis tutoriales sobre Perl (Enero 2000) si quieres aprender Perl.

_________________ _________________ _________________

 

Introducción

Tradicionalmente los ficheros han estado basado en líneas. Ejemplos son los ficheros de configuración de linux tales como /etc/hosts, /etc/passwd... Hay incluso viejos sistemas operativos donde tienes funciones en el sistema operativo para recuperar y escribir datos línea a línea.
Los ficheros SGML/XML/HTML estan basados en etiquetas, las líneas no tienen significado aquí, sin embargo los editores de texto y las personas utilizan las líneas.

Especialmente los ficheros largos de HTML por regla general constan de varias líneas de código HTML. Hay incluso herramientas tales como "Tidy" para sangrar html y hacerlo legible. Usamos líneas aunque HTML está basado en etiquetas y no en líneas. Puedes compararlo con el código C. Teóricamente puedes escribir el código entero en una sola línea. Nadie hace esto. Sería ilegible.
Por lo tanto esperas que un corrector sintáctico de HTML escriba "ERROR:línea.." en vez de "ERROR despues de la etiqueta 4123". Esto es porque tu editor de texto te permite saltar facilmente a una línea dada en el fichero.

Lo qué se necesita aqui es una manera buena y fácil para procesar un fichero de HTML etiqueta a etiqueta y mantener los números de las líneas.  

Una posible solución

El modo normal de leer un fichero en Perl es usando el operador while(<FILEHANDLE>). Este leerá los datos línea a línea y pasará cada línea a la variable $_. ¿Por qué Perl hace esto así? Perl tiene una variable interna llamada INPUT_RECORD_SEPARATOR ($RS o $/) donde está definido "/n" como el final de línea. Si pones $/=">" entonces Perl usará ">" como final de línea. La siguiente línea de comando del script de Perl reformateará el texto de html para que ">" sea siempre el final de línea:

perl -ne 'sub BEGIN{$/=">";} s/\s+/ /g; print "$_\n";' file.html

Un fichero de html que aparece como

<html><p>some text here</p></html>
se transformará en
<html>
<p>
some text here</p>
</html>
Lo importante no es sin embargo la legibilidad. Para los desarrolladores de software es importante que el dato sea pasado a las funciones en su código etiqueta a etiqueta. Con esto será fácil encontrar "<a href= ... incluso si el original html tenía "a" y "href" en líneas separadas.

Cambiando el valor de la variable "$/" (INPUT_RECORD_SEPARATOR) no causa desbordamiento y es muy rápido. Es también posible usar el operador match y expresiones regulares como un iterador y procesar el fichero con expresiones regulares. Esto es un poco más complicado y más lento pero también muy usado frecuentemente.

¿Dónde está el problema? El título de este artículo decía HTML::TagReader pero hasta ahora he estado hablando todo el tiempo sobre una solución muy simple que no requiere módulos extras. Debe haber algo mal en esta solución: Dicho de otra manera, solamente es posible cambiar el valor de la variable "$/"(INPUT_RECORD_SEPARATOR) en casos especiales.

Todavía tengo un útil programa de ejemplo que nos permita seguir discutiendo. El asigna sin embargo a la variable "$/" el valor "<" porque los navegadores no pueden manejar un "<" equivocado de la misma manera que ">" y por lo tanto hay menos páginas web con "<" equivocadas que con ">" equivocadas.El programa se llama tr_tagcontentgrep (click para verlo) y puedes también ver en el código como mantiene el número de línea. tr_tagcontentgrep puede ser usado para buscar una cadena de carácteres (por ejemplo "img") en una etiqueta incluso si la etiqueta ocupa varias líneas. Algo como:

tr_tagcontentgrep -l img file.html
index.html:53: <IMG src="../images/transpix.gif" alt="">
index.html:257: <IMG SRC="../Logo.gif" width=128 height=53>

tr_tagcontentgrep -l img file.html
index.html:53: <IMG src="../images/transpix.gif" alt="">
index.html:257: <IMG SRC="../Logo.gif" width=128 height=53>

 

HTML::TagReader

HTML::TagReader resuelve los dos problemas creados con la modificación de INPUT_RECORD_SEPARATOR y ofrece también una forma más sencilla para separar el texto de las etiquetas. No es tan pesado como un analizador completo de HTML y ofrece lo que tú quieres cuando procesas código html: Un método para leer etiqueta a etiqueta.

Demasiadas palabras. Aqui esta como usarlo. Primero debes escribir
use HTML::TagReader;
en tu programa para cargar el módulo. Entonces llama
my $p=new HTML::TagReader "filename";
para abrir el fichero "filename" y almacena el objeto referenciado en $p.Ahora puedes llamar $p->gettag(0) o $p->getbytoken(0) para coger la próxima etiqueta. gettad devuelve solamente etiquetas (lo que hay entre < y >) mientras que getbytoken además de darte el texto entre < y > te dice que és (etiqueta o texto). Con estas funciones es muy fácil procesar ficheros html. Imprescindible para mantener un sitio web grande. Una completa descripción sintáctica puede ser encontrada en la página de ayuda de HTML::TagReader.

Aqui esta un ejemplo real de un programa. Imprime los títulos de los documentos para un número de documentos:
#!/usr/bin/perl -w
use strict;
use HTML::TagReader;
#
die "USAGE: htmltitle file.html [file2.html...]\n" unless($ARGV[0]);
my $printnow=0;
my ($tagOrText,$tagtype,$linenumber,$column);
#
for my $file (@ARGV){
 my $p=new HTML::TagReader "$file";
# read the file with getbytoken:
 while(($tagOrText,$tagtype,$linenumber,$column) = $p->getbytoken(0)){
 if ($tagtype eq "title"){
$printnow=1;
 print "${file}:${linenumber}:${column}: ";
next;
}
 next unless($printnow);
if ($tagtype eq "/title" || $tagtype eq "/head" ){
$printnow=0;
 print "\n";
next;
 }
 $tagOrText=~s/\s+/ /; #kill newline, double space and tabs
 print $tagOrText;
 }
}
# vim: set sw=4 ts=4 si et:
Cómo trabaja? Leemos el fichero de html con $p->getbytoken(0) cuando encontramos <title> o <Title> o <TITLE> (son devueltos como $tagtype eq "title") entonces activamos un indicador ($printnow) para que empiece la impresión y cuando encontramos </title> paramos la impresión.
Usa el programa asi:

htmltitle file.html somedir/index.html
file.html:4:programa de Perl
somedir/index.html:9: página personal de Joe

Por supuesto es posible implementar el tr_tagcontentgrep desde dentro con HTML::TagReader. Es pequeño y fácil de escribir:

#!/usr/bin/perl -w
use HTML::TagReader;
die "USAGE: taggrep.pl searchexpr file.html\n" unless ($ARGV[1]);
my $expression = shift;<
my @tag;
for my $file (@ARGV){
 my $p=new HTML::TagReader "$file";
 while(@tag = $p->gettag(0)){
 # $tag[0] is the tag (e.g <a href=...>)
 # $tag[1]=linenumber $tag[2]=column
 if ($tag[0]=~/$expression/io){
  print "$file:$tag[1]:$tag[2]: $tag[0]\n";
 }
}
}



El script es pequeño y no tiene demasiado manejo de errores pero aparte de esto es totalmente funcional. Para buscar por etiquetas que contienen la cadena "gif" escribe:

taggrep.pl gif file.html
file.html:135:15: <img src="images/2doc.gif" width=34 height=22>
file.html:140:1: <img src="images/tst.gif" height="164" width="173">

Algún ejemplo más? Esto es un programa que quitará todas las etiquetas <font...> y </font> del código html. Estas etiquetas de fuentes algunas veces son usadas en grandes cantidades por algún editor de html pobre en diseño gráfico y causa muchos problemas cuando son mostradas las páginas en diferentes navegadores y con diferente tamaño de pantalla. Esta sencilla versión quita todas las etiquetas de fuentes. Puedes cambiarlo para borrar solamente el tipo de fuente o tamaño y dejar el color sin cambiar.
#!/usr/bin/perl -w
use strict;
use HTML::TagReader;
# strip all font tags from html code but leave the rest of the
# code un-changed.
die "USAGE: delfont file.html > newfile.html\n" unless ($ARGV[0]);
my $file = $ARGV[0];
my ($tagOrText,$tagtype,$linenumber,$column);
#
my $p=new HTML::TagReader "$file";
# read the file with getbytoken:
while(($tagOrText,$tagtype,$linenumber,$column) = $p->getbytoken(0)){
 if ($tagtype eq "font" || $tagtype eq "/font"){
print STDERR "${file}:${linenumber}:${column}: deleting $tagtype\n";
 next;
 }
 print $tagOrText;
}
# vim: set sw=4 ts=4 si et:


Como puedes ver es fácil escribir programas útiles con pocas líneas.
El paquete del código fuente de HTML::TagReader (ver referencias) contiene algunas aplicaciones de HTML::TagReader: tr_xlnk y tr_staticssi son muy útiles cuando quieres hacer un CDROM de un sitio web. En el servidor web por ejemplo te aparecerá http://www.linuxfocus.org/index.html incluso si escribes solamente http://www.linuxfocus.org/ (sin el index.html). Si tu solamente grabas todos los ficheros y directorios en un CD y accedes al CD con el navegador web directamente (file:/mnt/cdrom) entonces veras solamente un listado del directorio en vez de index.html. La compañía que hizo el primer LinuxFocus CD cometió este error y fue horrible usar el CD. Ahora que ellos cogen el dato via tr_xlnk los CDs funcionan.

Estoy seguro que encontraras HTML::TagReader útil. ¡Feliz programación!  

Referencias

 

Formulario de "talkback" para este artículo

Cada artículo tiene su propia página de "talkback". A través de esa página puedes enviar un comentario o consultar los comentarios de otros lectores
 Ir a la página de "talkback" 

Contactar con el equipo de LinuFocus
© Guido Socher, FDL
LinuxFocus.org
Información sobre la traducción:
en --> -- : Guido Socher (homepage)
en --> es: María Pérez <lupeagui(at)hotmail.com>

2003-01-01, generated by lfparser version 2.34