[Résolu] HTML::Parser, balises avec attribut

HTML::Parser, balises avec attribut [Résolu] - Perl - Programmation

Marsh Posté le 23-06-2011 à 13:50:07    

Bonjour,
 
Je dois parser un fichier HTML afin de récupérer les valeurs entre <td>, mais seulement quand le td est suivi de l'attribut align="left" --> <td align="left">.
J'ai récupéré sur le web un code utilisant HTML::Parser et tenté de l'adapter mais en vain.
Il me retourne le contenu de tous les td et pas seulement ceux contenant l'attribut left.
 
Voici une partie du code :

Code :
  1. #!$(which perl) -w
  2. use HTML::Parser;
  3. sub parsing {
  4.    my $state = '';
  5.    # @personnes = ( [nom,adresse,tel,apb], [nom,adresse,tel,apb] );
  6.    my @personnes = ();
  7.    # ("nom", "adresse", "tel", "apb" )
  8.    my @personne = ();
  9.    # "nom"
  10.    my $item = '';
  11.    # Initialisation du parser.
  12.    my $p = HTML::Parser->new(api_version => 3);
  13.    # Ouverture d'une balise.
  14.    $p->handler(
  15.       start => sub {
  16.          my ($tag, $args) = shift;
  17.          $state = 'TABLE' if $tag eq 'table';
  18.          $state = 'TR' if $tag eq 'tr';
  19.          $state = 'TD' if $tag eq 'td';
  20.          if ($state eq 'TD' and defined($args->{align}) and $args->{align} eq 'left')
  21.          {
  22.             $item .= shift;
  23.          }
  24.       }, "tagname,text"
  25.    );
  26.    $p->handler(
  27.       default => sub {
  28.          $item .= shift if $state eq 'TD';
  29.       }, "text"
  30.    );
  31.    # Fermeture d'une balise.
  32.    $p->handler(
  33.       end => sub {
  34.          my $tag = shift;
  35.          $state = '' if $tag eq 'table';
  36.          if ($tag eq 'td')
  37.          {
  38.             $state = 'TR';
  39.             push @personne, $item;
  40.             $item = '';
  41.          }
  42.          if ($tag eq 'tr')
  43.          {
  44.             $state = 'TABLE';
  45.             push @personnes, [@personne];
  46.             @personne = ();
  47.          }
  48.       }, "tagname"
  49.    );
  50.    # Recupere le code HTML passe en parametre.
  51.    undef $/;
  52.    my $data = $_[0];
  53.    ## parse it (this calls each handler as necessary)
  54.    $p->parse($data);
  55.    ## now dump out the Perl structures
  56.    use Data::Dumper;
  57.    for my $row (@personnes)
  58.    {
  59.       print Dumper($row);
  60.    }
  61. }
  62. &parsing();


 
L'HTML :

Code :
  1. <table border="0">
  2. <tr>
  3. <td>
  4.  <div id="title" align="center">
  5.    <h2><b>Garde</b></h2>
  6.  </div>
  7.  <div id="info"></div>
  8.    <div id="map" style="width: 730px; height: 550px">
  9.    </div>
  10.   </td>
  11. </tr>
  12. <tr>
  13. <td align="center">
  14.   <div id="directions"></div>
  15. <table><tr><td height="1" class="LineGrey"></td></tr><tr><td align="left"><br /><span class="styleGreen">garde&nbsp;</span><br /><br /><b>Tata</b>&nbsp;(Tata)&nbsp;&nbsp;&nbsp;&nbsp;Chaussée de Tata 41&nbsp;&nbsp;&nbsp;&nbsp;1360 TxxxxxxxxS&nbsp;<br /><b>Tel. :</b> xxx/xxxxxx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>Numéro :</b> xxxxxx<br /><br /></td></tr><tr><td height="1" class="LineGrey"></td></tr><tr><td align="left"><br /><span class="styleGreen">&nbsp;garde&nbsp;</span><br /><br /><b>Tbtb (Tbtb)</b>&nbsp;(Tbtb)&nbsp;&nbsp;&nbsp;&nbsp;Rue Tbtb 43&nbsp;&nbsp;&nbsp;&nbsp;5020 Txxxxxx&nbsp;<br /><b>Tel. :</b> xxxxxxxxx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>Numéro :</b> xxxxxx<br /><br /></td></tr><tr><td height="1" class="LineGrey"></td></tr><tr><td align="left"><br /><span class="styleGreen">&nbsp;garde&nbsp;</span><br /><br /><b>Tctc</b>&nbsp;(Tctc)&nbsp;&nbsp;&nbsp;&nbsp;Rue Tctc 22&nbsp;&nbsp;&nbsp;&nbsp;1450 Cxxxxxxx&nbsp;<br /><b>Tel. :</b> xxxxxxxxx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>Numéro :</b> xxxxxx<br /><br /></td></tr><tr><td height="1" class="LineGrey"></td></tr><tr><td align="left"><br /><span class="styleGreen">&nbsp;garde&nbsp;</span><br /><br /><b>Tdtd</b>&nbsp;(Tdtd)&nbsp;&nbsp;&nbsp;&nbsp;Chaussée Tdtd 40&nbsp;&nbsp;&nbsp;&nbsp;5140 Sxxxxxxxxx&nbsp;<br /><b>Tel. :</b> xxxxxxxxx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>Numéro :</b> xxxxxx<br /><br /></td></tr><tr><td height="1" class="LineGrey"></td></tr><tr><td align="left"><br /><span class="styleGreen">&nbsp;garde&nbsp;</span><br /><br /><b>Tete</b>&nbsp;(Tete)&nbsp;&nbsp;&nbsp;&nbsp;Rue Tete 30&nbsp;&nbsp;&nbsp;&nbsp;5020 Vxxxxxxxxxxx&nbsp;<br /><b>Tel. :</b> xxxxxxxxx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>Numéro :</b> xxxxxx<br /><br /></td></tr><tr><td height="1" class="LineGrey"></td></tr><tr><td height="1" class="LineGrey"></td></tr></table><br /><br />
  16.   <script>
  17.      document.getElementById('directions').style.display = 'none'
  18.   </script>
  19. </td>
  20. </tr>
  21. </table>


 
Pouvez-vous m'aider s'il vous plait ?
Merci :jap:


Message édité par Gavrinis le 28-06-2011 à 10:22:52
Reply

Marsh Posté le 23-06-2011 à 13:50:07   

Reply

Marsh Posté le 23-06-2011 à 16:50:06    

Bon, j'ai fait des modifs et un test rapide:

Code :
  1. #!$(which perl) -w
  2. use HTML::Parser;
  3.  
  4. sub parsing {
  5.   my $state = '';
  6.   # @personnes = ( [nom,adresse,tel,apb], [nom,adresse,tel,apb] );
  7.   my @personnes = ();
  8.   # ("nom", "adresse", "tel", "apb" )
  9.   my @personne = ();
  10.   # "nom"
  11.   my $item = '';
  12.   # Initialisation du parser.
  13.   my $p = HTML::Parser->new(api_version => 3);
  14.   # Ouverture d'une balise.
  15.   $p->handler(
  16.      start => sub {
  17.         my $tag = shift;
  18.         my $args = shift;
  19.         $state = 'TABLE' if $tag eq 'table';
  20.         $state = 'TR' if $tag eq 'tr';
  21.         $state = 'TD' if $tag eq 'td';
  22.         if ($state eq 'TD' and defined($args->{align}) and $args->{align} eq 'left')
  23.         {
  24.         $state = 'TD1';
  25.         }
  26.      }, "tagname, attr"
  27.   );
  28.   $p->handler(
  29.      default => sub {
  30.         $item .= shift if $state eq 'TD1';
  31.      }, "text"
  32.   );
  33.   # Fermeture d'une balise.
  34.   $p->handler(
  35.      end => sub {
  36.         my $tag = shift;
  37.         $state = '' if $tag eq 'table';
  38.         if ($tag eq 'td')
  39.         {
  40.         if ($state eq 'TD1') {push @personne, $item;}
  41.            $state = 'TR';
  42.            $item = '';
  43.         }
  44.         if ($tag eq 'tr')
  45.         {
  46.            $state = 'TABLE';
  47.         if (@personne+0) {
  48.           push @personnes, [@personne];
  49.         }
  50.            @personne = ();
  51.         }
  52.      }, "tagname"
  53.   );
  54.   # Recupere le code HTML passe en parametre.
  55.   undef $/;
  56.   my $data = $_[0];
  57.   ## parse it (this calls each handler as necessary)
  58.   $p->parse($data);
  59.   ## now dump out the Perl structures
  60.   use Data::Dumper;
  61.   for my $row (@personnes)
  62.   {
  63.      print Dumper($row);
  64.   }
  65. }
  66. &parsing();
 

1) Si tu voulais les attributs, il fallait passer  "tagname, attr" au handler de start.
2) Faut faire un shift pour chacun de tes arguments, un (a, b) = shift ne devrait remplir que a a priori je crois.
3) Faut ajouter un cas particulier quand tu as le TD avec l'attribut voulu pour detecter ça et accumuler le texte dans le handler par défaut (ici: TD1)

 

Il y a peut être d'autres moyens de faire ça avec ce module, mais je ne le connais pas bien.

 

Bon, il te reste plus qu'à parser correctement chaque ligne de @personnes pour en extraire les champs.

 

A+,

 


Message édité par gilou le 23-06-2011 à 16:56:23

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 23-06-2011 à 21:43:54    

Ce serait pas plus simple avec un sed, awk ou autre?

Reply

Marsh Posté le 23-06-2011 à 22:11:00    

On voit bien que tu n'y connais rien.  
Perl est beaucoup plus puissant et pratique que sed awk et autres (le jours ou j'ai découvert Perl, il y a plus de 20 ans, j'ai pu réécrire en un seul fichier simple une usine à gaz en script, sed, awk et autres dont plein de fichiers temporaires, que je faisais pour l'INRIA).
Pour parser du texte structuré ou pas, et la, c'est ce qu'il fait, il y a pas plus pratique que Perl.
A+,

Message cité 1 fois
Message édité par gilou le 23-06-2011 à 22:15:52

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 23-06-2011 à 22:21:58    

gilou a écrit :

On voit bien que tu n'y connais rien.  
Perl est beaucoup plus puissant et pratique que sed awk et autres (le jours ou j'ai découvert Perl, il y a plus de 20 ans, j'ai pu réécrire en un seul fichier simple une usine à gaz en script, sed, awk et autres dont plein de fichiers temporaires, que je faisais pour l'INRIA).
Pour parser du texte structuré ou pas, et la, c'est ce qu'il fait, il y a pas plus pratique que Perl.
A+,


 
Hmm pas la peine de le prendre personnellement... Je posais juste une question.
 
Et puis c'est normal qu'avec un "vrai langage" on ait plus de fonctionnalité et d'alternative qu'avec une simple commande  (ou du sh) :) Mais dans beaucoup de cas un sed suffit, pas besoin de re-coder un parser a la main.


Message édité par gueuledange le 23-06-2011 à 22:28:57
Reply

Marsh Posté le 23-06-2011 à 22:40:35    

Ah désolé si c'était juste une question. Je pensais que c'était une remarque sur un ton ironique.
 
Mais la il ne recode pas un parser, il en utilise un tout fait (use HTML::Parser) il associe juste quelques actions certains tokens reconnus par le parser.
 
Et dans les cas ou appeler sed suffit, bien souvent, c'est plus facile a coder en perl sur une ligne et a appeler comme on ferait pour sed. :D
 
 
A+,


Message édité par gilou le 23-06-2011 à 22:44:03

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 24-06-2011 à 12:49:12    

Merci pour ton aide et tes explications Gilou, je n'en demandais pas autant, ça fonctionne parfaitement.

Reply

Marsh Posté le 24-06-2011 à 13:42:54    

Juste un point de détail, évites d'appeler une routine perl avec &f();
f() est la manière moderne (et a moins que tu utilises un perl préhistorique, ça doit marcher), et l'appel &f(); désactive certaines fonctionnalités (la vérification des types des paramètre passés si la routine a un prototype à sa déclaration par exemple).
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 24-06-2011 à 14:09:04    

Ok merci :jap:.
 
J'ai encore un soucis ... j'aimerais supprimer tous les &nbsp des éléments de mon tableau.
J'ai essayé ceci mais cela ne va pas :(.

Code :
  1. @personne = map { $_ =~ s/\&nbsp//g; $_ } @personne;

Reply

Marsh Posté le 24-06-2011 à 14:35:01    

Chez moi, ça marche, donc ton pb est ailleurs.

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4.  
  5. my @personne;
  6. $personne[0] = '<b>Tata</b>&nbsp;(Tata)&nbsp;&nbsp;&nbsp;&nbsp;Chaussée de Tata 41&nbsp;';
  7. $personne[1] = "40&nbsp;&nbsp;&nbsp;&nbsp;5140 ";
  8.  
  9. @personne = map { s/\&nbsp;//g; $_} @personne; # pas besoin du $_ =~ ici
  10.  
  11. print $personne[0], "\n\n", $personne[1];


Fais aussi attention au fait que ta transformation va transformer 40&nbsp;&nbsp;&nbsp;&nbsp;5140 en 405140, ce qui n'est peut être pas ce que tu désires.
s/\&nbsp;(\&nbsp;)*/ /g; serait peut être plus adapté.
A+,


Message édité par gilou le 24-06-2011 à 14:39:58

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 24-06-2011 à 14:35:01   

Reply

Marsh Posté le 24-06-2011 à 14:47:42    

Encore merci, c'était tout bêtement une faute de frappe.
Pas de problème pour la transformation, car je supprime les &nbsp (pas le ; qui me servira de délimiteur).

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed