Version HTML du script detection-entites.pl

Pour télécharger le script : cliquez ici

#!/usr/bin/perl

<<DOC; 
Axel COURT & Marjorie SEIZOU
AVRIL 2010

usage : perl entites.pl	<FichierCordial> <FichierEntites>

DOC

use Encode;
use utf8;

# Ouverture d'un fichier étiqueté à traiter
open(TXT, "<:encoding(utf8)", "$ARGV[0]") or die "Probleme a l'ouverture du fichier texte : $!\n";

# Ouverture du fichier comprenant l'ensemble des entités nommées reconnues
open(ENT, "<:encoding(utf8)", "$ARGV[1]") or die "Probleme a l'ouverture du fichier d'entites : $!\n";

# On crée quatre sorties : un premier fichier HTML qui tagge toutes les entités nommées avec liens vers Wikipédia
# Un deuxième fichier HTML qui présente, pour chaque entité nommée, ses caractéristiques + les contextes d'apparition dans le texte
# Un fichier textuel qui répertorie les contextes pour chaque entité reconnue
# Un fichier XML qui représente l'extraction des entités comme le fait l'API Alchemy
open(HTML1,">:encoding(utf8)","entites_et_liens.html");
open(CSS,">:encoding(utf8)","style.css");
open(HTML2,">:encoding(utf8)","entites_et_informations.html");
open(CSS2,">:encoding(utf8)","style2.css");
open(OUTTXT,">:encoding(utf8)","entites_et_contextes.txt");
open(OUTXML,">:encoding(utf8)","decodage-entites-wikipedia.xml");

# Initialisation des variables
%lien_entites;
%nombre_entites;
%type_entites;
%carac_entites;
%contexte_entites;
$dumpHTML1 = "";

&lectureENT;
&parcoursTXT;
&contextes;
&printHTML1;
&printHTML2;
&printOUTTXT;
&printOUTXML;

exit;

###########################################################################

# Lecture du fichier des entités nommées, et stockage des informations en interne via des hash
sub lectureENT {
	print "Parcours du fichier d'entites nommees...\n";
	my $partie = "";
	while (my $ligne = <ENT>) {
		if ($ligne !~ /^-+\n$/) {
			$partie .= $ligne;
		}
		if ($ligne =~ /^-+$/) {
			$partie =~ s/\n/\t/g;
			my $entite = "";
			if ($partie =~ /([^\t]+)\t([^\t]+)\t([^\t]+)/) {
				$lien_entites{$1} = $3;
				$nombre_entites{$1} = $2;
				$entite = $1;
			}
			if ($partie =~ /\[\[Type=(.+?)\]\]/) {
				my $type = $1;
				$type_entites{$entite} = $type;
			}
			while ($partie =~ /\{\{(.+?)=(.+?)\}\}/g) {
				push(@{$carac_entites{$entite}},"<span class=\"att\">$1</span> : <span class=\"val\">$2</span>");
			}
			$partie = "";
			$entite = "";
		}
	}
}

# Parcours du fichier de Cordial et création du contenu pour la première sortie HTML
sub parcoursTXT { 
	print "Parcours du fichier Cordial !\n";
	while (my $ligne = <TXT>) {
		if ($ligne =~ /([^\t]+)\t([^\t]+)\t(NP.+)/) {
			$name .= $1." ";
		}
		elsif ($ligne !~ /([^\t]+)\t([^\t]+)\t(NP.+)/) {
			if ($name eq "") {
				if ($ligne =~ /([^\t]+)\t([^\t]+)\t([^\t]+)/) {
					my $mot = $1;
					$dumpHTML1 .= "<span class=\"forme\">$mot</span> \n";
				}
			}
			elsif ($name ne "") {
				my $mot = "";
				if ($ligne =~ /([^\t]+)\t([^\t]+)\t([^\t]+)/) {
					$mot = $1;	
				}
				$name =~ s/^(.+)\s$/$1/g;
				$name =~ s/-/ /g;
				$name =~ s/  +/ /g;
				$name =~ s/ /_/g;
				$name =~ s/^_(.+)$/$1/g;
				if ($name =~ /^[A-Z]'(.+)$/) {
					my $temp = $1;
					if ($temp =~ /[A-Z].+/) {
						$name = $temp;
					}
					else { 
						$dumpHTML1 .= "<span class=\"forme\">$name</span> \n<span class=\"forme\">$mot</span> \n";
						$name = ""; 
					}
				}
				if ($name ne "") {
					$entite = $name;
					$entite =~ s/_/ /g;
					if (exists($type_entites{$entite})) {
						$dumpHTML1 .= "<a href=\"$lien_entites{$entite}\"><span class=\"lien\">$entite</span></a> 
						<span class=\"type\">[ $type_entites{$entite} ]</span> \n<span class=\"forme\">$mot</span> \n";
					}
					elsif (exists($nombre_entites{$entite})) {
						$dumpHTML1 .= "<a href=\"$lien_entites{$entite}\"><span class=\"lien\">$entite</span></a> 
						<span class=\"type\">[ NONE ]</span> \n<span class=\"forme\">$mot</span> \n";
					}
					else {
						$dumpHTML1 .= "<span class=\"forme\">$entite</span> \n<span class=\"forme\">$mot</span> \n";
					}
					$name = "";
				}
			}
		}
	}
	close(TXT);
}

# Détermination des contextes avant et après chaque entité identifiée (méthode n-grams)
sub contextes {
	print "Determination des contextes avant et apres chaque entite\n";
	my @liste_entites = keys %nombre_entites;
	my @liste = ();
	foreach $entite (@liste_entites) {
		my @nb = split(/ /, $entite);
		push(@liste, $#nb+1);
	}
	my %listengramstemp  = map { $_, 1 } @liste;
	my @listedesngrams = keys %listengramstemp;
	my @liste_mots = ();
	# Parcours du fichier de Cordial (oui, c'est contre-productif de le ré-ouvrir !)
	open(TXT, "<:encoding(utf8)", "$ARGV[0]") or die "Probleme a l'ouverture du fichier texte : $!\n";
	while (my $ligne = <TXT>) {
	    if ($ligne =~ /^[^\t]+\t[^\t]+\t[^\t]+$/) {
			chomp($ligne);
			@malignesegmentee = split(/\t/, $ligne);
			push(@liste_mots, $malignesegmentee[0]);
	    }
	}
	close(TXT);
	foreach my $n (@listedesngrams) {
		$n = $n-1;
		my $j = 0;	
		until ($j+$n > $#liste_mots) {
			my $ngram = join(" ", @liste_mots[$j .. $j+$n]);
			if (exists $nombre_entites{$ngram}) {
				my $contexte = join(" ", @liste_mots[($j-3) .. ($j+$n+3)]);
				push(@{$contexte_entites{$ngram}},$contexte);
			}
			$j++;
		}
	}
}

# Impression du premier fichier HTML
sub printHTML1 {
	print HTML1 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
	<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"fr\" xml:lang=\"fr\">
	<head>
	<title>Master pluriTAL - Programmation et projet encadré 2 - Bà0 - Reconnaissance d'entités nommées</title>
	<meta http-equiv=\"Content-Language\" content=\"Français\" /><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />
	<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" media=\"screen\" />
	</head>
	<body>
	<div id=\"page\">
	<div id=\"titre\">
	<h1>Texte extrait des fils RSS du Monde (année 2009) - Rubrique \"A la Une\"</h1>
	</div>
	<div id=\"tableau\">
	<table>
	<tr id=\"titretr\">
	<th class=\"td\"><span class=\"c1\">Chaque entité nommée identifiée pointe vers son lien Wikipédia. 
	Le type d'entité est précisé quand il est connu</span></th></tr>
	<tr bgcolor=\"white\">
	<td class=\"td\">";
	print CSS "body {
		font-family: \"Calibri\", Arial, Verdana, Sans-Serif;
		text-align: center;
		margin: 0;
	}
	h1 {
		text-align: center;
		font-size: 1.6em;
		font-variant: small-caps ;
		color: #900505 ;
	}
	.c1 {
		font-size: 14px;
		color: #303030;
		font-weight : bold;
		font-style: italic;
	}
	th {
		background-color: #EBECED;
	}
	.td {
		font-size: 12px;
		vertical-align : top;
	}
	#page {
		background-color:#F9F9F9;
		padding: 25px;
	}
	#titre {
		padding-top: 10px;
		padding-bottom: 20px;
		height:40px;
		background-image: -moz-linear-gradient(top, #EBECED, #F9F9F9);
		background-image: -webkit-gradient(linear, left top, left bottom, from(#EBECED), to(#F9F9F9));
		filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#EBECED,endColorstr=#F9F9F9);
		-ms-filter: \"progid:DXImageTransform.Microsoft.gradient(startColorstr=#EBECED,endColorstr=#F9F9F9)\";
		-moz-border-radius: 5px;
	}
	#titretr {
		background: #EBECED;
	}
	#tableau {
		padding-top: 20px;
		padding-bottom: 20px;
		line-height: 1.5em ;
		border-collapse: collapse;
	}
	.type {
		font-size: 12px;
		color: #A9A9A9;
		margin-left: 5px;
		margin-right: 15px;
	}
	.lien {
		font-size: 14px;
		font-weight : bold;
		color: #900505;
		margin-left: 15px;
	}
	a {
		text-decoration: none;
		outline: none;
	}
	.lien:hover { text-decoration: underline; }
	.forme {
		font-size: 14px;
		color: #336699;
	}";
	close(CSS);
	print HTML1 $dumpHTML1;
	print HTML1 '</td>
	</tr>
	</table>
	</div>
	</div>
	</body>
	</html>';
	close(HTML1);
}

# Impression du deuxième fichier HTML
sub printHTML2 {
	print "Ecriture de la deuxieme sortie HTML\n";
	print CSS2 'body {
		font-size: 12px;
		padding: 10px 0 0 10px;
		background-color: #ffffff;
		width: 1180px;
	}
	i {
		color: green;
	}
	h1 {
		font-size: 22px;
		color: #ffffff;
		font-weight: 150;
		letter-spacing: -1px;
		font-variant: small-caps;
		font-family: "Trebuchet MS", Helvetica, Sans-serif;
	}
	h2 {
		font-size: 14px;
		color: #555;
		font-weight: normal;
		letter-spacing: -1px;
		font-family: "Trebuchet MS", Helvetica, Sans-serif;
	}
	a { text-decoration: none; color: #ffffff; }
	a:hover { text-decoration: underline; }
	.content {
		font-family: "Trebuchet MS", Helvetica, Sans-serif;
		padding: 20px;
		border: 2px solid #FE7F0E;
		width: 100%;
		margin-bottom: 20px;
	}
	.titre {
		background-color: #FE7F0E;
		width: 800px;
		padding: 10px 20px 10px 20px;
	}
	.entite {
		font-weight: bold;
		color: #FE7F0E;
		font-size: 16px;
		margin-left: 15px;
		margin-right: 15px;
	}
	.att {
		font-weight: bold;
		color: #900505;
	}
	.val {
		color: #336699;
		margin-right: 30px;
	}
	.carac {
		margin: 10px;
		margin-top: 30px;
		font-family: "Trebuchet MS", Helvetica, Sans-serif;
		color: #900505;
		font-size: 14px;
	}';
	close(CSS2);
	$dumpHTML2 = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/>
<link rel=\"stylesheet\" type=\"text/css\" href=\"style2.css\" media=\"screen\" />
<title>Master pluriTAL - Programmation et projet encadré 2 - Bà0 - Reconnaissance d'entités nommées</title>
</head><body>";
		@keys = sort {$#{$contexte_entites{$b}} <=> $#{$contexte_entites{$a}}} keys %contexte_entites;
		foreach $entite (@keys) {
			if ($entite ne "") {
				$dumpHTML2 .= "<div class=\"titre\"><h1>".$entite."</h1>\n<h2>Type : ";
				if (exists($type_entites{$entite})) { 
					$dumpHTML2 .= $type_entites{$entite};
				}
				else { 
					$dumpHTML2 .= "INCONNU";
				}	
				$dumpHTML2 .= ' </h2><h2>Nombre d\'occurrences : '.($#{$contexte_entites{$entite}}+1).' </h2>
				<h2>Lien Wikipédia : <a href="'.$lien_entites{$entite}.'">'.$lien_entites{$entite}.'</a></h2><div class="carac">';
				foreach my $carac (@{$carac_entites{$entite}}) {
					# Correction des petites erreurs dans les prélèvements d'infos sur Wikipédia : quelques attributs HTML qui trainent...
					$carac =~ s/(<!--)//g;
					$carac =~ s/<div.+?>//g;
					$carac =~ s/^<span class=\"att\">.*(width|align|bgcolor).+$//g;
					$carac =~ s/^<span class=\"att\">.+mérimée.+$//g;
					$dumpHTML2 .= $carac;
				}
				$dumpHTML2 .= "</div>\n</div>\n<div class=\"content\">";
			foreach my $contexte (@{$contexte_entites{$entite}}) {
				if ($contexte ne "") {
					$contexte2 = $contexte;
					$contexte2 =~ s/$entite/<span class="entite">$entite<\/span>/g;
					$dumpHTML2 .= $contexte2."<br />";
				}
			}
			$dumpHTML2 .= "</div>";
		}
	}
	print HTML2 $dumpHTML2."</body></html>";
	close(HTML2);
}

# Impression du fichier .txt
sub printOUTTXT {
	print "Impression du fichier de sortie textuel\n";
	@keys = sort {$#{$contexte_entites{$b}} <=> $#{$contexte_entites{$a}}} keys %contexte_entites;
	foreach $key (@keys) {
		if ($key ne "") {
			print OUTTXT "-----------------------------------------------------------------------------------\n";
			print OUTTXT $key."\n";
			foreach my $contexte (@{$contexte_entites{$key}}) {
				print OUTTXT $contexte."\n";
			}
		}
	}	
	close(OUTTXT);
}

# Impression du fichier XML
sub printOUTXML {
	print "Generation de la sortie XML !\n";
	print OUTXML '<?xml version="1.0" encoding="UTF-8"?>
<results>
    <status>Détection entités nommées - méthode "manuelle" Wikipédia - OK</status>
   	<language>french</language>
    <entities>
';
    @keys = sort {$#{$contexte_entites{$b}} <=> $#{$contexte_entites{$a}}} keys %contexte_entites;
    	foreach $key (@keys) {
		if ($key ne "") {
			print OUTXML "<entity>\n<type>";
			if (exists($type_entites{$key})) { 
					print OUTXML "$type_entites{$key}";
			}
			else { 
					print OUTXML "INCONNU";
			}
			print OUTXML "</type>\n<count>".($#{$contexte_entites{$key}}+1)."</count>\n<text>".$key."</text>\n</entity>\n";
		}
	}
	print OUTXML '</entities>
</results>';
	close(OUTXML);
}