#!/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); }