[LinuxFocus-icon]
LinuxFocus article number 269
http://linuxfocus.org

[Photo of the Author]
Guido Socher (homepage)

L'autore:

Guido adora Perl per la sua flessibilità e per la sua rapidità come linguaggio per script. Apprezza il motto di Perl: "C'è più di un modo per fare le cose" che rispecchia la libertà e le possibilità offerte dal mondo dell'opensource.



Tradotto in Italiano da:
Toni Tiveron <toni(at)amicidelprosecco.com>

Gestire i documenti HTML con Perl, HTML::TagReader

[Illustration]

Premessa:

Se volete gestire un sito web con più di 10 pagine HTML, presto vi renderete conto che averete la necessità di ricorrere ad un programma che vi possa aiutare.
La maggior parte del software tradizionale processa riga per riga (o addirittura carattere per carattere). Sfortunanatamente le righe di un file non hanno alcun valore in SGML/XML/HTML. Questo tipo di file si basa sui tag (ovvero parole chiave) HTML::TagReader è un modulo che richede risorse moderare per processare un file costituito con dei tag.
Questo articolo parte dal presupposto che voi conosciare il linguaggio Perl abbastanza bene. Potete dare un'occhiata ai miei articoli sul Perl per prendervi confidenza.

_________________ _________________ _________________

 

Introduzione

Tradizionalmente i file si sono sempre basati sulla riga. Un classico esempio possono essere i file di configurazione di Unix, come per esempio, /etc/hosts, /etc/passwd,... Esistono anche sistemi operativi datati che hanno specifiche funzioni per estrarre il contenuto di un file riga per riga.
SGML/XML/HTML sono invece bastati sulle tag, e quindi la semplice riga perde di significato, tuttavia gli editor di testi ed il pensiero umano sono ancora in qualche modo legati al concetto di riga.

Questo è particolarmente vero di grossi file HTML che sono costituiti da svariate righe di codice. Esistono strumenti come "Tydy" per permettere di avere una formattazione del codice HTML e renderlo più leggibile. Tuttavia essi ricorrono sempre all`idea di riga e non di tag ed l'HTML è per l'appunto basato du tag! Il codice HTML potrebbe essere paragonato al codice C. Teoricamente potreste scrivere l'intero codice su di una sola riga, ma nessuno lo fa. Divvrebbe illegibile!
Tuttavia voi vi aspettate che un sistema di controllo della sintassi di un file HTML vi scriva "ERRORE: alla riga nuermo ...." piuttosto che "ERRORE al tag 4123". Questo in quanto l'editor che andate ad utilizzare vi permette facilmente di raggiungere la riga in questione.

Quello che ci serve, quindi, è un semplice e leggero sistema di controllo per controllare il file HTML tag per tag e, nello stesso tempo, tenere traccia della riga che stiamo analizzando .  

Una possibile soluzione

Il modo convezionale di leggere un file in Perl è quello di ricorrere al'operatore while(<FILEHANDLE>). Questo permette di leggere i dati riga per riga e di assegnarne il contentuto alla variabile $_. Perchè il Perl si comporta così? Perchè ricorre ad una variabile interna detta INPUT_RECORD_SEPARATOR ($RS o $/) ove all'interno della stessa viene definito "\n" come carattere di fine riga. Se definite $/=">" allora Perl utilizzerà ">" come carattere di fine riga. Il seguente script in Perl formatterà il testo hmtl in modo che ogni riga termini sempre con ">":

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

un file html che apparirà nel seguente modo

<html><p>il vostro testo qui</p></html>
diverrà
<html>
<p>
il vostro testo qui</p>
</html>
Un aspetto molto importante di questo modo di procedere non è di certo la sua più lineare leggibilità. Per lo svlippatore software è più importante che ie dati sia correttamente trattari dalle funzioni, tag per tag. Questo rende molto semplice la possbilità di ricerche di tag, come per esemio "<a href="..." anche se nel codice originale le parole chiavi "a" ed "html" fossero su righe diverse.

Cambiando il valore di "$/" (INPUT_RECORD_SEPARATOR) non si avrà nessun sovraccarico del sistema ed il tutto sarà molto veloce. Sarebbe anche possibile ricorrere all'operatore di uguaglianza ed utilizzare le espressioni (ovvero più stringhe) come iteratori e quindi processare il file per mezzo di espressioni predefinite. Questo è ovviamente un poco più complesso e meno veloce nell'esecuzione, ma nonostante tutto, molto utilizzato.

Ma allora qual'è il problema? Il titolo di questo articolo recita: "HTML::TagReader". Noi fino ad ora abbiamo parlato di soluzioni molto semplici che non richiedono alcun modulo esterno. Ci deve essere, quindi, qualcosa che non va nella soluzione che ho proposto fino ad ora: In altre parole, vi sono pochissimi casi in sui si possa ricorrere all'utilizzo della viariabile "$/" (INPUT_RECORD_SEPARATOR).

Ho alcuni programmi d'esempio per voi che sfruttano il sistema fino a qui descritto. Ho comunque perferito assegnare alla variabile "$/" il valore "<" in quanto i browser web non sono in grado di gestire il carattere "<" se posizionato erroneamente al contrario di quanto avviene con il carattere ">", e comunque sono molte meno le pagine web con il carattere "<" posizionato erroneamente che non quelle con il carattere ">". Questo semplice programma l'ho chiamato tr_tagcontentgrep (un click per vederlo) e all'interno del medesimo potete anche vedere il codice che ho utilizzato per tenere traccia del numero di riga. tr_tagcontentgrep può anche essere utilizzato per cercare una stringa di un tag all'interno del file medesimo (per esempio "img"). La ricerca di parte del tag può anche espandersi su più righe. Vediamo un esempio:

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 risolve i due problemi che si hanno quando si ricorre alla modifica della variabile INPUT_RECORD_SEPARATOR ed offre un sistema molto più apprezzabile per separare il testo dai tag. Oltretutto non è avido di risorse come HTML:Parser ed offre anche la possibilità di interfacciarsi meglio con noi: ci fornisce un metodo per leggere da tag a tag.

Abbiamo divagato fin troppo. Ecco come usarlo. Prima di tutto dovrete scrivere
use HTML::TagReader;
all'interno del vosto codice per poter caricare in memoria questo modulo. Per utilizzarlo dovrete semplicemente scrivere
my $p=new HTML::TagReader "filename";
per analiazzare il file "filename" ed otterrete un oggetto nella variabile $p. Ora potete chiamare la variabile $p->gettag(0) o $p->getbytoken(0) per ottenere il prossimo tag. gettag ci restituisce un solo tag (quella strana scritta tra < e >), mentre getbytoken ci restituisce il testo contenuto e ci dice anche se si tratti di testo o di un ulteriore tag. Con queste funzioni risulta molto facile processare file html. Ecco che diviene uno strumento essenziale per mantere grossi siti web. La sintassi completa e una spiegazione più esaustiva può essere trovata sulla pagina del manuale di HTML::TagReader.

Eccoci ora ad un vero esempio di funzionamento del programma. Ci faremmo stampare i titoli dei documenti html che gli sottoporremo:
#!/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:
Come funziona? Andiamo al leggere il file html per mezzo di $p->getbytoken(0) e quando troviamo la stringa <title> o <Title> o <TITLE> (ci sono restituiti come $tagtype equivalente a "title") andremmo ad assegnare un flag ($printnow) per iniziare la stampa del titolo e non appena troveremo la stringa </title> interromperemo la stampa.
Utilizzare il programma come qui di seguito mostrato:

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

Certo tutto questo lo possiamo integrare anche con il programma tr_tagcontentgrep. Sicuramente un poco più breve e più semplice da scrivere:

#!/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";
    }
  }
}
Lo script ora è molto breve e non ha particolari strumenti per gestire gli errori, ma è completamente funzionale. Per cercare i tag che contengano al loro interno la stringra "gif" dovremmo digitare:

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 ulteriore esempio? Qui abbiamo un programma che rimuove il tag inerente la modifica dei caratteri, ovvero i tag: <font...> e </font>. Questo tipo di tag viene spesso utilizzato in maniera esagerata da alcuni scadenti edito grafici per html e spesso questo massiccio uso crea problemi guardando le pagine con diversi browser e con diverse risoluzioni video. Questa versione semplificata rimuove tutti i tag "font". Nessuno vi vieta di modificarla per far si che rimuova solo determinate caratteristiche dei caratteri (il tipo, il colore, la dimensione) lasciando il resto del tag intatto.
#!/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:
Come potete notare è molto semplice scrivere programmi molto utili con ppchissime righe di codide.
Il sorgente del pacchetto HTML::TagReader contiene alcuni esempi ed applicazioni del pacchetto stesso (trovate i riferimenti a ciò alla fine dell'articolo): tr_xlnk and tr_staticssi sono molto utili quando vogliate creare dei cdrom partendo da un sito web. Per esempio, quando digitate http://www.linuxfocus.org/ il server web vi farà vedere il seguente indirizzo http://www.linuxfocus.org/index.html (caricherà automaticamente la pagina index.html). Se tuttavia copiare tutti i file del sito web sul cdrom ed accedete al cdrom con il vostro browser web, vedete solo una lista di file e cartelle invece del file index.html. La prima società che fece la prima edizione su cdrom di LinuxFocus fece questo errore e fù assai scomodo consulatare il cdrom. Ora utilizzano tr_xlnk per ottenere i dati da mettere sul cdrom, e, difatti, ora i cdrom funzionano perfettamente.

Sono sicuro che troverete HTML::TagReader uno strumento molto ultile I am sure you will find HTML::TagReader useful. Happy programming!  

Bibliografie e riferimenti



Webpages maintained by the LinuxFocus Editor team
© Guido Socher
"some rights reserved" see linuxfocus.org/license/
http://www.LinuxFocus.org
Translation information:
en --> -- : Guido Socher (homepage)
en --> it: Toni Tiveron <toni(at)amicidelprosecco.com>

2005-01-14, generated by lfparser_pdf version 2.51