G�rer le HTML avec Perl, HTML::TagReader

ArticleCategory: [Choose a category, do not translate this]

Webdesign

AuthorImage:[Here we need a little image from you]

[Photo of the Author]

TranslationInfo:[Author + translation history. mailto: or http://homepage]

original in en Guido Socher

en to fr Georges Tarbouriech

AboutTheAuthor:[A small biography about the author]

Guido aime Perl parce que c'est un langage de script tr�s versatile et rapide. Il aime la devise de Perl "Il y a plus d'une fa�on de le faire" parce qu'elle refl�te la libert� et les possibilit�s offertes par l'"opensource".

Abstract:[Here you write a little summary]

Si vous g�rez un site web de plus de 10 pages HTML, vous vous rendrez vite compte qu'il est imp�ratif d'utiliser quelques programmes pour vous aider dans cette t�che.
La plupart des logiciels traditionnels lisent les fichiers ligne par ligne (ou caract�re par caract�re). Malheureusement les lignes n'ont aucun sens dans les fichiers SGML/XML/HTML. Les fichiers SGML/XML/HTML reposent sur des balises. HTML::TagReader est un module tr�s l�ger permettant d'examiner un fichier par balise.

Cet article suppose que vous connaissez bien Perl. Penchez-vous sur mes tutoriels Perl (Janvier 2000) si vous souhaitez apprendre ce langage.

ArticleIllustration:[This is the title picture for your article]

[Illustration]

ArticleBody:[The article body]

Introduction

Traditionnellement les fichiers ont �t� bas�s sur la ligne. Quelques exemples sont fournis par les fichiers de configuration d'Unix comme /etc/hosts, /etc/passwd .... Certains syst�mes d'exploitation plus anciens poss�daient m�me des fonctions permettant de retrouver et d'�crire des donn�es ligne � ligne.
Les fichiers SGML/XML/HTML sont bas�s sur des balises, les lignes n'ayant ici aucune signification, m�me si les �diteurs de texte et les individus sont encore d�pendants des lignes.

Les gros fichiers HTML en particulier, consistent en de nombreuses lignes de code HTML pour votre serveur. Il existe m�me des outils comme "Tidy" pour indenter le HTML et le rendre lisible. Bien qu'il repose sur des tags, nous utilisons quand m�me des lignes pour le HTML. Nous pourrions le comparer � du code C. Th�oriquement, vous pouvez �crire le code complet sur une seule ligne. Personne ne fait une chose pareille : ce serait illisible.
Par cons�quent, vous attendez d'un correcteur de syntaxe HTML qu'il �crive "ERROR: line ..." plut�t que "ERROR after tag 4123" (erreur apr�s la balise 4123). Ceci parce que votre �diteur de texte vous permet facilement de sauter � une ligne donn�e du fichier.

Il nous faut donc un moyen l�ger et efficace pour examiner un fichier HTML balise par balise tout en conservant la r�f�rence aux num�ros de lignes.

Une solution �ventuelle

La mani�re habituelle de lire un fichier avec Perl consiste � utiliser l'op�rateur while(<FILEHANDLE>). Celui-ci lit les donn�es ligne par ligne et passe chacune d'elles � la variable $_ . Pourquoi Perl proc�de-t-il ainsi ? Perl poss�de une variable interne nomm�e INPUT_RECORD_SEPARATOR ($RS ou $/) dans laquelle il est d�fini que "\n" correspond � la fin d'une ligne. Si vous d�finissez $/=">", Perl utilisera alors ">" comme "end of line" (fin de ligne). La commande de script Perl suivante reformate le texte html de mani�re � ce qu'il finisse toujours par ">":

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

Un fichier html qui ressemble �

<html><p>some text here</p></html>
deviendra
<html>
<p>
some text here</p>
</html>
Toutefois, l'essentiel n'est pas la lisibilit�. Pour le d�veloppeur, l'important c'est que les donn�es soient pass�es aux fonctions de son code balise par balise. Ainsi, il sera facile de rechercher "<a href= ..." m�me si le fichier html d'origine contient "a" et "href" sur des lignes diff�rentes.

Modifier le "$/" (INPUT_RECORD_SEPARATOR) ne provoque aucune surcharge et tout est tr�s rapide. Il est �galement possible d'utiliser l'op�rateur "match" et des expressions r�guli�res servant d'it�rateur; on peut alors examiner le fichier avec des expressions r�guli�res. C'est un peu plus compliqu� et lent mais souvent utilis�.

O� est le probl�me ?? Le titre de cet article contient HTML::TagReader, mais jusqu'� pr�sent je n'ai parl� que d'une solution plus simple qui n'a aucun besoin de modules suppl�mentaires. Quelque chose ne convient pas dans cette solution : En d'autres termes, il n'est possible que dans certains cas d'utiliser le "$/" (INPUT_RECORD_SEPARATOR).

J'ai encore un exemple de programme utile correspondant � ce dont nous avons parl� jusqu'ici. Il d�finit "$/" en "<". Les navigateurs ne g�rent pas aussi bien un "<" erron� qu'un ">" et par cons�quent il existe beaucoup moins de pages html contenant un "<" incorrect qu'un ">" mal plac�. Le programme se nomme tr_tagcontentgrep (cliquez pour le visualiser) et vous pouvez voir dans le code comment conserver le num�ro de ligne. tr_tagcontentgrep peut �tre utilis� pour rechercher ("grep") une cha�ne (par ex."img") dans une balise, y compris si la balise s'�tend sur plusieurs lignes. Comme ceci :

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 r�sout les deux probl�mes en modifiant le INPUT_RECORD_SEPARATOR et offre ainsi un moyen beaucoup plus �l�gant de s�parer le texte des balises. Il n'est pas aussi lourd qu'un v�ritable HTML::Parser (analyseur de code HTML) et offre ce dont vous avez besoin pour examiner du code html : une m�thode de lecture balise par balise.

Assez de bla-bla. Voici comment l'utiliser. Vous devez d'abord �crire
use HTML::TagReader;
dans votre code pour charger le module. Ensuite vous appelez
my $p=new HTML::TagReader "filename";
pour ouvrir le fichier "filename" et obtenir un objet r�f�rence renvoy� dans $p. Vous pouvez maintenant appeler $p->gettag(0) ou $p->getbytoken(0) pour atteindre la balise suivante. gettag ne renvoie que le contenu des balises (ce qui se trouve entre < et >) alors que getbytoken fournit �galement le texte contenu entre les balises en vous indiquant la nature (balise ou texte). Gr�ce � ces fonctions il est tr�s facile d'examiner les fichiers html. Essentiel pour g�rer un gros site. Une description compl�te de la syntaxe se trouve dans la page de manuel de HTML::TagReader.

Voici un v�ritable programme exemple. Il affiche les titres des diff�rents documents examin�s :
#!/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:
Comment fonctionne-t-il ? Nous lisons le fichier html par $p->getbytoken(0) et lorsque nous trouvons <title> ou <Title> ou <TITLE> (ils sont renvoy�s en tant que $tagtype eq "title") nous positionnons un drapeau ($printnow) pour d�marrer l'affichage et nous arr�tons ce dernier lorsque nous rencontrons </title>.
Vous utilisez ce programme de cette mani�re :

htmltitle file.html somedir/index.html
file.html:4: the cool perl page
somedir/index.html:9: joe's homepage

Bien s�r, il est possible d'activer le tr_tagcontentgrep ci-dessus avec HTML::TagReader. C'est plus court et plus facile � �crire :

#!/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";
    }
  }
}
Le script est court et ne se pr�occupe pas trop de la gestion des erreurs mais il est parfaitement fonctionnel. Pour extraire (grep) les balises contenant la cha�ne "gif", tapez :

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">

Un autre exemple ? Voici un programme qui supprime toutes les balises <font...> et </font> du code html. Ces balises de polices sont parfois utilis�es en grande quantit� par quelques �diteurs html graphiques mal �crits et posent de nombreux probl�mes lorsque les pages sont visualis�es avec des navigateurs diff�rents sur des r�solutions diff�rentes. Ce simple script �limine ces balises. Vous pouvez le modifier de fa�on � ce qu'il ne supprime que les balises d�finissant la police ou la taille tout en laissant la couleur inchang�e.
#!/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:
Comme vous le voyez, il est tr�s facile d'�crire des programmes utiles en quelques lignes.
Le paquetage de code source de HTML::TagReader (voir r�f�rences) contient quelques applications de HTML::TagReader: tr_xlnk et tr_staticssi sont tr�s pratiques lorsque vous souhaitez cr�er un CDRom � partir d'un site web. Le serveur web vous fournira par exemple http://www.linuxfocus.org/index.html m�me si vous n'avez tap� que http://www.linuxfocus.org/ (sans index.html). Toutefois, si vous vous contentez de graver tous les fichiers et r�pertoires sur un CD et que vous consultiez ce CD directement avec votre navigateur (file:/mnt/cdrom) vous obtiendrez une liste de r�pertoires au lieu d'un fichier index.html. La soci�t� qui a grav� le premier CD de _LF_ a fait cette erreur et le r�sultat �tait catastrophique. Maintenant que les donn�es sont obtenues par tr_xlnk les CD fonctionnent tr�s bien.

Je suis certain que trouverez HTML::TagReader tr�s pratique. Programmez bien !

R�f�rences