Je travaillais pour quelqu'un qui avait un gros intranet sur lequel on trouvait de nombreux fichiers HTML mal décrits et des fichiers PDF sans propriétés spécifiques car ils provenaient directement de mopieurs (purs PDFs imagesà. Lors des préparatifs de la mise en place d'un système de GED et d'un moteur de recherche, il est évidement apparu que l'absence de réelles méta données allait poser problème. Sans méta données, tout ce fonds allait avoir un rendement informationnel très faible.
Mon idée était qu'avant de demander aux documentalistes et autres producteurs de contenus de travailler à renseigner efficacement ces métas, on pouvait utiliser l'information déjà en place pour en déduire un certain nombre de règles. La localisation d'un fichier peut être interprétée comme une information réutilisable dans la mesure où, la plupart du temps, le nom du dossier dans lequel il se trouve n'est pas choisi au hazard. Quand il y a un lien vers un fichier, le texte dans l'ancre donne une information de premier ordre quant au fichier destination. Le titre du fichier où le lien trouve son origine donne lui aussi de l'information réutilisable par son fils.
Nous allons voir dans un premier temps un petit script qui va analyser le corpus en place et déterminer la méta situation, puis nous verrons comment lire, mettre à jour et écrire des méta données dans des fichiers HTML et PDF.
MetaStat est un outil d'audit assez simple. Il parcours récursivement un volume à la recherche de fichiers HTML et dresse des statistiques relatives à la présence ou l'absence de méta données. Il ne fait qu'observer l'existant pour aider à déterminer une stratégie ultérieure.
#!/usr/bin/perl
# Code par HAbeTT
# statisticien des méta données
# 2005, Creative Commons, certains droits réservés
use File::Find;
$hits = 0;
$| = true;
# répertoire à analyser
find(\&process, "f:/habett");
# Sortie
print "\n\nMetaStat\n$hits fichiers analysés\n";
# ratio de titrage
$mt = 100*$iztitle/$hits;
printf ("\n%02d pourcennt titrés\n", $mt);
# 5 titres les plus fréquents
@basta = sort {$titles{$b} <=> $titles{$a}} keys %titles;
for $i (0..4) {
print " ".($i+1)." : \"".$basta[$i]."\" = ".$titles{$basta[$i]}."\n";
}
# ratio de description
$md = 100*$izdescription/$hits;
printf ("\n%02d pourcent décrits\n", $md);
# 5 descriptions les plus fréquentes
@basta = sort {$descriptions{$b} <=> $descriptions{$a}} keys %descriptions;
for $i (0..4) {
print " ".($i+1)." : \"".$basta[$i]."\" = ".$descriptions{$basta[$i]}."\n";
}
# ratio de mots-clés
$mk = 100*$izkeywords/$hits;
printf ("\n%02d pourcent avec mots clés\n", $mk);
# 5 chaînes de mots-clés les plus fréquentes
@basta = sort {$keywords{$b} <=> $keywords{$a}} keys %keywords;
for $i (0..4) {
print " ".($i+1)." : \"".$basta[$i]."\" = ".$keywords{$basta[$i]}."\n";
}
exit(0);
sub process {
# récupération du nom de fichier
$file = $File::Find::name;
# élimination des répertoires
return if (-d $file);
# élimination des fichiers non HTML
return unless (substr($file,$perco) =~ /\.htm/io);
# ouverture du fichier cible
open (TARGET, $file);
$hits++;
print ".";
# lecture du contenu
$html = "";
while ($p = read (TARGET,$donnees,8192)) {
$html .= $donnees;
}
# fermeture du fichier cible
close(TARGET);
# récupération des métas
$title = "";
($title) = ($html =~ /.*<title>(.*)<\/title>.*/io);
$titles{$title}++;
$iztitle += 1 if ($html =~ /<title>/i);
($description) = ($html =~ /meta.*?description.*?content.*?=.*?"(.*?)"/io);
$descriptions{$description}++;
$izdescription += 1 if ($html =~ /<meta.*?description.*?content.*?>/i);
($keyword) = ($html =~ /meta.*?keywords.*?content.*?=.*?"(.*?)"/io);
$keywords{$keyword}++;
$izkeywords += 1 if ($html =~ /<meta.*?keywords.*?content.*?>/i);
}
Comme vous pouvez le voir, aucun module exotique n'a été utilisé, c'est juste la combinaison de quelques expressions régulières avec la récursivité de File::Find. Pragma systèmiste : audition puis action.
MetaCast est encore un autre script parcourant récursivement une arborescence de fichiers, une arregné qui essaye de construire un minimum de réseau sémantique. Il commence par repérer les fichiers HTML car ils sait qu'ils contiennent en général des liens hypertextes. Il prends le titre de ce document père ainsi que le nom du dossier dans lequel il se trouve, dans la perspective que cela puisse servir à l'information des fils. Ensuite, il boucle dans les liens inclus.
Pour chaque lien pointant sur le volume courant, il capture le texte dans l'ancre ansi que le répertoire dans lequel se trouve le fils-cible. Ainsi, il va considérer peu à peu comme candidats au statut de mots-clés toutes ces informations : texte de l'ancre, titre du parent, répertoire du parent et répertoire du fils.
Si c'est un fichier PDF, il va utiliser le très puissant module PDF::API2 pour lire puis écrire les méta données dans les propriétés du document. Il enrichit donc le titre, le sujet et vas ajouter les mots-clés candidats nécessaires.
Si c'est un fichier HTML, il vas utiliser une série d'expressions régulières pour déterminer s'il y a, puis évaluer, le titre, la description et les mots-clés. Il vas uniquement modifier le balisage relatif à ces métas données, injectant ce qu'il a compris comme pertinent.
#!/usr/bin/perl
# Code par HAbeTT
# Processeur de méta données
# 2005, Creative Commons, certains droits réservés
use File::Find;
use PDF::API2;
# répertoire de travail
find(\&process, "/wwwroot");
exit(0);
sub process {
# récupération du nom de fichier
$file = $File::Find::name;
# éliminiation des répertoires
return if (-d $file);
# élimination des fichiers non HTML
return unless (substr($file,$perco) =~ /html?$/io);
# lecture du contenu du fichier
open (DAFILE, $file);
print "$file\n";
$code = "";
while ($p = read(DAFILE,$donnees,8192)) {
$code .= $donnees;
}
close(DAFILE);
# extraction du nom du dossier parent
@cats = ();
(@cats) = ($file =~ /\/(.*?)\//g);
$parent = $cats[(scalar @cats)-1];
# traitement des CR/LF
$html =~ s/(?:\012\015|\012|\015)/ /go;
# extraction du titre
($titre) = ($code =~ /<title>(.*?)<\/title>/i);
# listage des liens
@links =();
(@links) = ($code =~ /<a.*?href.*?=.*?"([^"]*)"/gi);
# boucle dans les liens
foreach $candid (@links) {
# liens internet
next if ($candid =~ /^http/o);
# liens sur d'autres volumes
next if ($candid =~ /:/o);
# extraction du texte balisé dans l'ancre
($anchor) = ($code =~ /<a.*?href.*?=.*?"$candid".*?>(.*?\n?.*?)<\/a>/i);
# nom du dossier fils
@cats = ();
(@cats) = ($candid =~ /\/?(.*?)\//g);
$child = $cats[(scalar @cats)-1];
# préparation des mots-clés candidats
$seed = "$anchor $titre $parent $child";
@seeds = split(/ /,$seed);
if ($candid =~ /\.pdf$/i) {
# Liens vers des fichiers PDF
# initialisation PDF::API2
$pdf = PDF::API2->open($candid) or die "Ne peut ouvrir $candid\n";
print "=> $candid\n";
# lecture des métas
%oj = $pdf->info();
# ajout des nouveaux mots-clés
$keywords = $oj{'Keywords'};
foreach $c (@seeds) {
$keywords .= ", $c" unless ($keywords =~ /$c/);
}
# écriture des métas
$pdf->info('Title'=>$oj{'Title'}." ".$anchor,
'Subject'=>$oj{'Subject'}." ".$titre,
'Keywords'=>$keywords);
# sauvegarde
$pdf->saveas($candid);
} elsif ($candid =~ /\.html?$/i) {
# Liens vers des fichiers HTML
# ouverture de la cible
open (TARGET, $candid);
print "=> $candid\n";
# lecture du contenu
$html = "";
while ($p = read (TARGET,$donnees,8192)) {
$html .= $donnees;
}
# fermeture de la cible
close(TARGET);
# extractions des métas
$title = "";
($title) = ($html =~ /.*<title>(.*)<\/title>.*/io);
$iztitle = ($html =~ /<title>/i);
($description) = ($html =~ /meta.*?description.*?content.*?=.*?"(.*?)"/io);
$izdescription = ($html =~ /<meta.*?description.*?content.*?>/i);
($keywords) = ($html =~ /meta.*?keywords.*?content.*?=.*?"(.*?)"/io);
$izkeywords = ($html =~ /<meta.*?keywords.*?content.*?>/i);
# injection selective des mots clés
foreach $b (@seeds) {
$keywords .= ", $b" unless ($keywords =~ /$b/i);
}
# titre devient texte de l'ancre si non défini
$title = $anchor unless ($title);
# description devient titre du parent tired texte de l'ancre si non défini
$description = "$titre - $anchor" unless ($description);
# retitrage
if ($iztitle) {
$html =~ s/<title>.*<\/title>/<title>$title<\/title>/i;
} else {
$html =~ s/<\/head/<title>$title<\/title>\n<\/head/i;
}
# ajout des métas
if ($izdescription) { $html =~ s/<meta.*?description.*?content.*?>//i; }
$html =~ s/<\/head/<meta name="description" content="$description">\n<\/head/i;
if ($izkeywords) { $html =~ s/<meta.*?keywords.*?content.*?>//i; }
$html =~ s/<\/head/<meta name="keywords" content="$keywords">\n<\/head/i;
# écriture du fichier cible
open (TARGET, "> $candid") or die ("Pb mit $file");
print TARGET $html;
close (TARGET);
}
}
}