IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Extraction d'un paragraphe


Sujet :

Langage Perl

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut Extraction d'un paragraphe
    Bonjour à tous ,

    Je reviens vous voir pour vous poser - malheureusement encore - une question de débutant. Voilà mon "problème" : je cherche à extraire un paragraphe situé entre deux balises. Après quelques recherches sur internet, j'ai trouvé "ce qu'il fallait" et j'ai adapté tout cela à ma sauce avec un code perso.

    Cependant, la chose fonctionne uniquement lorsque le texte à trouver se situe entre deux balises placées sur une même ligne (type : <test>chaine</test>)... mais pas lorsqu'il s'agit d'un paragraphe sur plusieurs lignes, par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    <p class="tenor">
              Voici l'exemple du texte à extraire.
              Comme vous le voyez, l'ensemble se situe sur plusieurs lignes.
              Il faut que je prenne l'ensemble de ce paragraphe, que je supprime les  
              balises qu'il pourrait y avoir dedans du type <gras>he ho</gras>
              et enfin que j'enlève les sauts de lignes, pour stoquer le tout dans 
              une chaine (type $texte).
    </p>
    Voici mon code actuel (il marche pour les balises "simples" - sans saut de ligne -, ça peut donc déjà donner des idées à d'autres :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    #############
    ### DEBUT ###
    #############
     
    #!/usr/local/bin/perl
    use POSIX;
    use CGI qw/:all /;
     
    my $texte;
    my $date;
    my $titre;
     
    open (FICH, 'test.html') || die ("pas de fichier 1\n"); # fichier à traiter
     
    while (<FICH>)
    {
    	if (/<h3>(.*?)<\/h3>/)		# date
    	{
     
    		print "$1\n";
    		$date = "$1";
    	}
     
    	if (/<p class="reg">(.*?)<\/p>/)		# titre
    	{
    		print "$1\n";
    		$titre = "$1";
    	}
     
    	if (/<p class="tenor">(.*?)<\/p>/)		# texte
    	{
    		print "$1\n";
    		$texte = "$1";
    	}
    }
    close FICH;
    C'est donc la dernière partie qui ne fonctionne pas (d'ailleurs je comprends bien pourquoi : le fichier est scanné ligne par ligne... ). Mais je ne sais pas comment m'en sortir. Qu'en pensez-vous s'il vous plaît ? Si quelqu'un à une idée, ce serait plus que bienvenu. En vous remerciant tous par avance pour toute réponse.
    Bien cordialement.

  2. #2
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    Si on ne veut ou peut pas utiliser les modules conçus pour le traitement de fichiers HTML (ce qui serait quand même plus propre), pour ce genre de problème il faut utiliser l'opérateur d'étendue en contexte scalaire.

    Exemple avec un one-liner :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -lne'print if m{<p[^>]*>}..m{</p>}' fichier.html
    Évidemment, ça ne fait pas tout ce que tu veux, mais ça marche pour extraire les paragraphes. Tu devrais pouvoir te débrouiller pour le reste.

    EDIT : Mais si ton fichier est compatible XML (si c'est du XHTML bien formé), appliquer une feuille XSLT qui va bien est une possibilité à considérer.
    There's nothing like $HOME!

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut
    Merci beaucoup Schmorgluck pour ta réponse sympathique.
    Malheureusement, je ne suis pas certain d'avoir bien compris. Je suis en effet débutant. Je n'utilise presque pas de regex, et je ne suis pas vraiment programmeur (comme tu as pu le remarquer)...

    Je veux bien utiliser un Parser... mais franchement, j'ai passé quelques heures sur différents sites sans vraiment comprendre comment adapter la chose à mon cas...

    Enfin, j'ai essayé d'intégrer ta ligne à mon code (merci encore), sans succès. Bien entendu, le problème est que je ne comprend pas le code que tu présentes, donc je ne peux pas l'adapter. Pourrais-tu s'il te plaît m'en dire plus ? Par ce que là, je suis paumé...

    Merci encore cependant.
    ps : je précise que j'avais déjà posé une question du même type (http://www.developpez.net/forums/d78...-fichier-html/). Iblis m'avait sympathiquement répondu... mais c'était justement sur les Parser et le Regex pour un texte entre deux balises, mais seulement sur une même ligne (ces réponses m'ont d'ailleurs servies à élaborer une partie du code ci-dessus).

  4. #4
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Pour comprendre l'opérateur d'étendue .. (double point), il faut le voir comme un opérateur logique à mémoire : il devient VRAI dès que le premier opérande est VRAI, et redevient FAUX après que le deuxième opérande soit devenu VRAI (détail avec perldoc perlop).

    En fait, il n'est pas seulement un opérateur logique, mais aussi numérique. En effet, il retourne une valeur numérique contenu le nombre réalisation VRAIES de l'opérateur, en commençant par 1, et en terminant par un nombre à notation scientifique terminé par E0 (E0 pour x10^0, c'est à dire x 1). Cette astuce permet de détecter quel est la dernière ligne de l'étendue recherchée.

    Pour tester la présence du premier tag et du tag de fin, on peut utiliser une expression régulière : m{<p[^>]>} et m{</p>}
    Si l'on a les deux tags sur la même ligne, l'opérateur retournera alors 1E0.
    Si le premier tag est sur une ligne, et le tag de fin 2 lignes plus loin, l'opérateur retournera 1 pour la première ligne, 2 pour la deuxième ligne, et 3E0 pour la 3e ligne, puis repassera à 0 s'il ne trouve pas le premier tag.

    Pour extraire un bloc, tu peux alors procéder en remplissant une table :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    my @bloc = ();
    while (my $ligne = <STDIN>) {
     
      # On cherche la présence d'un bloc
      my $in_bloc = ($ligne =~ m{<p[^>]*>}i) .. ($ligne =~ m{</p>}i);
     
      # Si l'on est dans un bloc, on remplit la table avec le nouvelle ligne
      push @bloc, $ligne if $in_bloc;
     
      # Si l'on a trouvé le tag de fin, on traite le bloc
      if ($in_bloc =~ /E0$/ {
        # traitement du bloc
        # puis vidage du bloc pour repartir sur une nouveau bloc
        @bloc = ();
      }
    }
    Je précise que je n'ai ni testé ni compilé ce bout de script.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  5. #5
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    Je comprends. L'utilisation de l'opérateur d'étendue en contexte scalaire est loin d'être une matière facile. C'est une de ces fonctionnalités de Perl qu'on peut qualifier de très puissantes, mais très subtiles.

    Et là je suis tenté de te renvoyer vers une page qui explique bien les choses (les explications de perldoc laissent à désirer), mais je cherche à m'entraîner à l'expliquer moi-même pour proposer un article pour la FAQ.

    En contexte scalaire, l'opérateur d'étendue .. est bistable, et ses deux opérandes sont évalués comme des booléens.

    Bistable, c'est à dire qu'il a deux valeurs possibles, vrai ou faux, et qu'il n'en change que dans des cas très précis, lié à l'évaluation booléenne de ses opérandes.

    Sa syntaxe est <expression1>..<expression2>. Dans un premier temps, lorsque cet opérateur est évalué, il renvoie faux, sauf si expression1 est évalué à vrai, à partir de quoi il renvoie toujours vrai, sans réévaluer expression1, jusqu'à ce que expression2 soit évalué à vrai. À ce moment-là, il renvoie vrai une dernière fois, et repasse à l'état initial, renvoyant faux.

    Typiquement, on utilise cet opérateur pour lire un fichier ligne par ligne et selectionner du texte situé entre deux balises. Lorsqu'on tombe sur la balise ouvrante (généralement détectée au moyen d'une expression régulière utilisée comme premier opérande), l'opérateur devient vrai, jusqu'à ce que l'on tombe sur la balise fermante (généralement détectée au moyen d'une expression régulière utilisée comme deuxième opérande), auquel cas l'opérateur devient faux après avoir envoyé vrai.

    Je ne sais pas si je suis clair.
    There's nothing like $HOME!

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut
    Merci Philou67430, Merci Schmorgluck.
    Grâce à vos explications, je comprends mieux la direction générale (en particulier, ayant fait un peu d'assembleur dans une autre vie, le côté "push @bloc" me dit vaguement quelque chose ). Donc pour l'idée de l'algorithme, merci.

    Cependant, en pratique, après quelques tests, la chose ne semble toujours pas fonctionner... je commence un peu à désespérer. Pour tout vous dire, mon domaine est plutôt les sciences humaines, et je ne peux malheureusement pas autant investir - en terme de temps - dans cette histoire qu'il le faudrait - du moins pour le moment - (il est clair que je devrais plutôt reprendre un manuel et le travailler de A à Z).

    Pour le moment, j'ai ajusté les choses de cette manière... malheureusement sans résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
     
    #!/usr/local/bin/perl
    use POSIX;
    use CGI qw/:all /;
     
    my $texte;
    my $date;
    my $titre;
     
    my @bloc = ();
     
    open (FICH, 'test.html') || die ("pas de fichier 1\n"); # fichier à traiter
     
    while (<FICH>) 
     
    {
     
    	if (/<h3>(.*?)<\/h3>/)		# date
     
    	{
     
    		print "$1\n";
    		$date = "$1";
     
    	}
     
    	if (/<p class="reg">(.*?)<\/p>/)		# titre
     
    	{
     
    		print "$1\n";
    		$titre = "$1";
     
    	}	
     
    	my $in_bloc = ($ligne =~ m{<p class="tenor"[^>]*>}i) .. ($ligne =~ m{</p>}i);
    	push @bloc, $ligne if $in_bloc;
    		if ($in_bloc =~ /E0$/) 
    				{
    				@bloc = ();
    		        }
     
    	print @bloc;
    }
     
    close FICH;
    Je n'ai pas de message d'erreur... mais aucun résultat supplémentaire non plus. Auriez-vous une idée s'il vous plaît ???
    En vous remerciant à nouveau.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut
    Je me suis rendu compte que de toute façon le code précédent ne pouvait pas fonctionner... j'ai donc aussi essayé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    #!/usr/local/bin/perl
    use POSIX;
    use CGI qw/:all /;
     
    my $texte;
    my $date;
    my $titre;
     
    my @bloc = ();
     
    open (FICH, 'test.html') || die ("pas de fichier 1\n"); # fichier à traiter
     
    while ($ligne = <FICH>) 
     
    {
    	my $in_bloc = ($ligne =~ m{<p class="tenor"[^>]*>}i) .. ($ligne =~ m{</p>}i);
    	push @bloc, $ligne if $in_bloc;
    		if ($in_bloc =~ /E0$/) 
    				{
    				@bloc = ();
    		        }
     
    	print @bloc;		
     
    }
     
    close FICH;
    Mais le code suivant ne fait que m'afficher presque l'ensemble du fichier... en tout cas pas la totalité, ni seulement, ce qui est contenu entre les deux balises. Avez vous une idée s'il vous plaît ? Vraiment désolé de toutes ces questions...

  8. #8
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    Houla, je crois que tu t'embrouilles un peu dans ton code. Et le fait que ton domaine soit les sciences humaines n'est pas une excuse valable : le créateur de Perl, Larry Wall, est linguiste de formation. Non mais.

    Alors, pour être clair, le test que je t'ai donné dans mon post précédent t'extraie de façon parfaitement nette et prévisible un sous-ensemble bien déterminé du fichier fourni en entrée: pour chaque début du type de bloc attendu, la ligne contenant le début du bloc ; plus toutes les lignes suivantes, jusqu'à ce que la séquence de fin de bloc soit trouvée.

    Et là, plus j'y réfléchis, et plus je me dis "non". Non, faut pas déconner, là. Le langage Perl est flexible, le langage Perl est puissant, ce que tu cherches à faire est probablement possible sans avoir recours à des modules spécialisés, mais comme lesdits modules existent et sont librement utilisables, essayer de faire ce que tu cherches à faire sans avoir recours à ceux-ci est purement et simplement stupide. J'ai essayé de m'intéresser à ton problème en termes très généraux parce que j'aime bien me prendre la tête sur des problèmes à la con, mais là non, j'en peux plus. Soit tu utilises les bibliothèques qui vont bien, soit tu passes par XSLT (et tu utilises les bibliothèques qui vont bien), mais arrête de nous demander comment faire ça en Perl de base (ou alors, demande poliment, avec du "s'il vous plait" et tout).
    There's nothing like $HOME!

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut
    Oulala !
    Je crois que tu t'emportes un peu de manière injustifiée :

    1. Je suis TRES poli : pas de doute. Il y a bien du "s'il te / vous plaît" comme tu le dis "si bien" et vraiment agressivement, dans mes messages (si, si, regardes !). [mais aussi : "En vous remerciant tous par avance pour toute réponse." "Bien cordialement." ; "Merci beaucoup Schmorgluck pour ta réponse sympathique." ; "Pourrais-tu s'il te plaît m'en dire plus ?" ; "Merci encore cependant." ; "Auriez-vous une idée s'il vous plaît ???" ; "En vous remerciant à nouveau." ; "Avez vous une idée s'il vous plaît ? Vraiment désolé de toutes ces questions..."). Hum ! A tout prendre, je crois que c'est toi qui est malpoli pour le coup, lorsque tu prétends me donner des leçons de politesse : et ce n'est pas un problème "de qui pose la question"... Un échange ça se fait toujours à deux, la politesse est un code de communication, et personne n'a à ramper devant l'autre !

    2. "une excuse valable" : qui parle de s'excuser ? Pas pour ça en tout cas. J'explique seulement ma situation - et cela permet de mieux comprendre pourquoi je demande de l'aide. Tu ne peux pas prétendre que je suis "malpoli" (sic) / direct, et en même temps me reprocher de présenter mon cas. Surtout, pour ce programme ce n'est pas un problème de difficulté : si j'avais toutes mes journées pour faire de la programmation, tu penses bien que l'aporie serait déjà résolue depuis longtemps. Enfin, je suis bien placé pour savoir que le créateur de perl était linguiste... c'est exactement pour cela que je "m'y suis mis".

    3. "de façon parfaitement nette et prévisible un sous-ensemble bien déterminé du fichier fourni en entrée" => Ben, non, sur mon fichier, ton code ne marche pas. Tu peux prétendre le contraire si tu veux, mais c'est faux

    4. "essayer de faire ce que tu cherches à faire sans avoir recours à ceux-ci est purement et simplement stupide." => Encore une fois, je ne suis pas certain que tu ai bien lu mes messages (mais tant pis). J'ai DEJA essayé d'utiliser un parser : mais je n'y suis pas arrivé (pour le moment).

    Je crois qu'il était tard et que tu t'es un peu lâché sur moi sans raison aucune : je dois admettre que c'est assez désagréable et injuste... Sans rancune.

  10. #10
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    Je suis vraiment vraiment désolé, je n'aurais pas dû poster si tard, mon post est effectivement injustement agressif et insultant. Je te présente mes excuses.

    Mais sur le fond, le problème que j'ai avec ce que tu demandes, c'est que j'entrevois plusieurs solutions possibles, mais toutes crades et alambiquées. Il serait mieux de recourir aux modules idoines.

    Enfin, je viens de relire ton code plus attentivement, et j'ai l'impression que tu as mal compris ce que nous avons dit à propos de l'opérateur d'étendue. Les détails que philou a donné sur la valeur de retour de l'opérateur sont intéressants, je ne les connaissais pas moi-même (et je n'en ai jamais eu besoin), mais vraiment la plupart du temps on se contente de l'évaluer comme un booléen, et donc de l'utiliser dans un if au sein d'une boucle.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while ($ligne = <FICH>) 
    {
    	print if(m{<p +class="tenor"[^>]*>}..m{</p>});
    }
    There's nothing like $HOME!

  11. #11
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Citation Envoyé par Schmorgluck Voir le message
    Les détails que philou a donné sur la valeur de retour de l'opérateur sont intéressants, je ne les connaissais pas moi-même (et je n'en ai jamais eu besoin), mais vraiment la plupart du temps on se contente de l'évaluer comme un booléen, et donc de l'utiliser dans un if au sein d'une boucle.
    En fait, la valeur de retour est très utile lorsque l'on a un traitement global à faire sur un bloc (et non pas une traitement individuel sur chaque ligne, comme le print de l'exemple si dessous).
    Ainsi, rechercher la fin du bloc est utile pour réaliser le traitement de ce bloc, puisqu'il signifie que la totalité du bloc a été parsé et mémorisé.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while ($ligne = <FICH>) 
    {
    	print if(m{<p +class="tenor"[^>]*>}..m{</p>});
    }
    Attention, le code ci-dessus ne fonctionne pas comme on le souhaiterais (voir dans mon exemple plus haut la bonne écriture). En effet, ici, chaque ligne est stockée dans $ligne, alors que les opérandes de l'opérateur d'étendue utilisent la variable automatique $_.
    Pour être correcte, il faudrait écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print if ($ligne =~ m{p +class="tenor"[^>]*>}) .. ($ligne =~ m{</p>});
    Sancti_Eyes, reprend ton dernier code et place le
    dans le bloc du test
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if ($in_bloc =~ /E0/) {
      print @bloc;
      @bloc = ();
    }
    Bien entendu, si tu souhaites faire autre chose avec @bloc avant de l'afficher, tu devras le faire aussi dans ce bloc de code. Par exemple, tu voudrais peut-être supprimer de la première ligne, tout ce qui précède <p class="tenor"> (car ce tag n'est peut-être pas situé au début de la ligne).
    De même sur la dernière ligne, tu voudras peut-être supprimer ce qui se situe après le </p>.

    Ceci étant dit, si ton code HTML contient des imbrications de balises <p> ... </p>, le code proposé ne sera pas suffisamment robuste pour extraire correctement les paragraphes. Sous peine d'une complexité démesurée, il faudra alors te résoudre à choisir la solution d'un parser HTML, qui reste dans tous les cas la solution la plus sure (et pas forcément la plus complexe à apprendre). Je suis sûr que tu trouveras aussi de l'aide pour faire marcher le code du parser que tu as tenté d'utiliser (en le postant ici, par exemple ).
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  12. #12
    Membre confirmé Avatar de iblis
    Inscrit en
    Janvier 2007
    Messages
    510
    Détails du profil
    Informations personnelles :
    Âge : 57

    Informations forums :
    Inscription : Janvier 2007
    Messages : 510
    Points : 570
    Points
    570
    Par défaut
    Extraire le contenu d'une balise HTML avec un parser n'est pas aussi difficile que tu le crois.

    Pour t'en persuader je te montre deux exemples.

    Le premier utilise HTML::TreeBuilder::XPath.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #!/usr/bin/env perl
    use strict; use warnings;
     
    use HTML::TreeBuilder::XPath; 
    my $tree= HTML::TreeBuilder::XPath->new;
    $tree->parse_file( 'path/to/your.html' );
    my @paragraphs = map { $_->as_text } $tree->findnodes( '//p[@class="tenor"]' );
    Rien de bien sorcier. Le parser construit une représentation en arbre de notre HTML. Le chemin //p[@class="tenor"] décrit tout les noeuds correspondant à une balise <p class="tenor"> et se trouvant à un niveau quelconque en partant de la racine (c'est le sens des //). Pour chacun des HTML::Elements renvoyés, on prend le texte figurant entre les balises.

    Si tu trouves la syntaxe des XPATH confuse, tu peux aussi naviguer toi-même dans l'arbre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    use HTML::TreeBuilder; 
    my $tree= HTML::TreeBuilder->new;
    $tree->parse_file( '/Users/omay/Desktop/test.html' );
    my @paragraphs = 
        map { $_->as_text } 
            $tree->look_down( '_tag' => 'a', 'class' => 'u' );
    Le deuxième utilise HTML::Parser. Là, je te montre le cas général d'extrait du contenu d'une balise.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    use HTML::Parser;
     
    my $parser = HTML::Parser->new( api_version => 3,
                             start_h => [\&start,"tagname"],
                             text_h => [\&text,"text"],
                             end_h => [\&end,"tagname"],
                                 );
     
    my $tag = 'p';
    my $intag_flag = 0;
    my @intags;
     
    sub start {
        my ($tagname) = @_;
        $intag_flag = 1 if $tagname eq $tag;
    }
    sub text {
        my ($text) = @_;
        if ($intag_flag) {
            push @intags, $text;
        }
    }
    sub end {
        my ($tagname) = @_;
        $intag_flag = 0 if $tagname eq $tag;
    }
     
     
    $parser->parse_file( 'path/to/your.html' );
    $parser->eof;    
     
    print join "\n", @intags;
    Là, la démarche est différente, on indique au parser ce qu'il doit faire quand il rencontre un début, un contenu et une fin de balise. On utilise un drapeau pour savoir où on est, et le tour est joué.

    Utiliser un parseur permet une grande souplesse et il en faut peu de plus pour extraire tous les paragraphes qui contiennent des liens et se terminant pas une balise </br>, par exemple.

    Ma préférence va le plus souvent vers XPATH et TreeBuilder.

    À toi de voir.

  13. #13
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut
    Waou !

    Merci à tous pour votre aide très très très précieuse

    => Aucun problème Schmorgluck : tu es tout excusé, bien évidement. Ce sont des choses qui arrivent souvent - surtout avec la fatigue - et ce n'est pas grave du tout. De toute façon, c'est parfois dur d'aider les gens - surtout lorsqu'on attend rien en retour - : honnêtement, je trouve le forum admirable. Donc encore une fois : aucun problème et merci sincèrement pour ton message.

    En fait je suis parti de la dernière indication de Schmorgluck, pour "programmer" hier en fin d'après midi (et je n'avais pas encore vu les messages, tout à fait intéressants, de Philou67430 et d'Iblis). En définitive, ton dernier bout de code marchait parfaitement Schmorgluck ; j'ai donc fini par obtenir la chose suivante, qui fonctionne à 90% :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
     
    #############
    ### DEBUT    ###
    #############
     
    #!/usr/local/bin/perl
    use POSIX;
    use CGI qw/:all /;
    package HTMLStrip;
    use base "HTML::Parser";
     
    my $texte;
    my $date;
    my $titre_doc;
    my $i = 0;
    my @bloc;
    my @bloc2;
    my @bloc3;
    my @blocfinal;
    my @table;										# table des fichiers
    my $nmtable = 0;								# n° du fichier HTML dans la table des fichiers
     
    @table = glob('*.html');						# recup. la liste des fichiers HTML	
     
    $mode = '>';
    $nmfichout = "temp.txt";
    $nmfichout2 = "temp2.txt";
    $nmfichout4 = "final.txt";
     
    # DEBUT DE LA BOUCLE
     
    while ($i < 10)
    {
     
    # TRAITEMENT
     
    open (FICH, @table[$nmtable]) || die ("pas de fichier html dans le répertoire\n");
     
    while (<FICH>)
     
    {
     
    	if (/<h3>(.*?)<\/h3>/)					# recup. date
    	{
    		#print "$1\n";
    		$date = "$1";
    	}
     
    	if (/<p class="reg">(.*?)<\/p>/)			# recup. titre
    	{
    		#print "$1\n";
    		$titre_doc = "$1";
    	}
     
    	if (m{<p +class="tenor"[^>]*>}..m{</p>})	# recup. texte
    	{		
    		$texte = $_;		
    		push @bloc, $texte;
    	}
     
    }
     
    #print "$date\n";
    #print "$titre\n";
    #print "@bloc";
     
    open(TEMP1, $mode, $nmfichout) or die("open: $!");
    	print TEMP1 @bloc;
    close (TEMP1);
     
    #######################
    # TRAITEMENT DU TEXTE #
    #######################
     
    # 2 - RETIRE LES BALISES
     
    open (FICH2, 'temp.txt') || die ("pas de fichier 1\n"); 
     
    sub text 
    	  {
              my ($self, $text) = @_;
              print $text;
    		  push @bloc2, $text;
          }
     
          my $p = new HTMLStrip;
     
    	  while (<FICH2>) 
    	  {
              $p->parse($_);		  
          }	  	  
     
    	$p->eof;
     
    open(TEMP2, $mode, $nmfichout2) or die("open: $!");
    	print TEMP2 @bloc2;
    close (TEMP2);
     
    close (FICH2);
     
     
    # 2 - SUPPRIME LES SAUTS DE LIGNE
     
    open (FICH3, 'temp2.txt') || die ("pas de fichier 1\n"); # fichier à traiter
     
    while ($ligne = <FICH3>)
    {
    	$ligne =~ s/\n//gs;	
    	#$ligne =~ s/(^[ \t]+)|([ \t]+$)//gm;	
    	push @bloc3, $ligne;
    }
     
    # FERMETURE DES FICHIERS 0 et 3
     
    close (FICH3);
    close (FICH);
     
    # PLACER DANS LE BLOC LES VARIABLES RECUPEREES
     
    @blocfinal = $date.'#'.$titre_doc.'#'."@bloc3\n";
     
    # RECOMMENCE LA BOUCLE
     
    unlink ( "temp.txt" );
    unlink ( "temp2.txt" );
    #sleep(1);
     
    $nmtable++;
    $i++;
    }
     
    ##############################################
    # ECRITURE FINAL DANS LE FICHIER 'final.txt' #
    ##############################################
     
    open(OUT, $mode, $nmfichout4) or die("open: $!");	
    	print OUT @blocfinal;	
    close (OUT);
    Mais de toute façon je garde précieusement les codes de Philou67430 et d'Iblis... j'en aurai besoin à un moment où à un autre. J'ai aussi employé un autre code trouvé quelque part - puis modifié - pour éliminer les balises du fichier "temp.txt". [Pour tout vous dire, j'ai essayé plusieurs solutions pour l'ensemble, dont le HTMLarser, pendant plusieurs heures, sans résultat vraiment intéressant... mais je suis sûr que les choses vont changer avec le code d'Iblis et avec un peu de pugnacité de ma part // en particulier le 1er exemple, qui semble à la fois très court et efficace]. Merci 1000 fois donc.

    Il reste néanmoins un dernier gros défaut à mon programme - et que je n'ai pas réussi à éliminer malgré plusieurs heures de tentative de debuggage -... mais j'ai un peu peur d'abuser en vous demandant à nouveau une aide . J'explique cependant : à la fin, j'obtiens dans $titre le titre extrait comme il faut du fichier initial, dans $date, la date qui m'intéresse, et dans @bloc3 le texte qui m'intéresse, sans balise et sans saut de ligne (ou presque). Je place tout cela dans @blocfinal [avec @blocfinal = $date.'#'.$titre_doc.'#'."@bloc3\n";]. Et j'envoie ce @blocfinal dans un fichier ("final.txt"). Le but est d'empiler les fichiers - 1 fichier = 1 ligne du fichier final - dans un seul et unique fichier. Malheureusement, je dois être maladroit : il semble qu'au lieu d'empiler les fichiers les uns après les autres, chacun sur une ligne dans le tableau @blocfinal, j'obtiens plutôt quelque chose qui accumule les textes les uns après les autres. Plus clairement, la structure finale de "final.txt" est :

    date1#titre1#texte1
    date2#titre2#texte2 texte1
    date3#titre3#texte3 texte2 texte 1
    date4#titre4#texte4 texte3 texte2 texte 1
    etc.

    alors que je voudrais :
    date1#titre1#texte1
    date2#titre2#texte2
    date3#titre3#texte3
    etc.

    Je suppose que mon problème vient de @bloc3 et / ou de "temp2.txt". J'ai essayé de les nettoyer (@bloc3 = '' ou de les effacer (unlink ( "temp2.txt" )... sans plus de succès.

    Est-ce que vous auriez une idée s'il vous plaît pour que je puisse me débarrasser de ce dernier bug qui empêche le fonctionnement de toute mon idée ?? Qu'en pensez-vous s'il vous plaît ?

    Surtout, en vous remerciant à nouveau beaucoup, tous, pour votre aide précieuse. Les codes de Philou et d'Iblis me seront aussi très précieux par la suite, car c'est une série d'opérations que je vais beaucoup réemployer par la suite...

    MERCI / MERCI / MERCI

  14. #14
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Ton programme souffre de quelques défauts, et le premier est la présentation (indentation), qui rend difficile son analyse et la recherche des bugs. On découvre ainsi, en indentant correctement, que beaucoup de choses se situent dans la boucle while ($i < 10) { ... }, et notamment la définition de la fonction test (ce n'est pas illégal, mais c'est totalement inutile, et cela lève des risques d'effet de bord).
    Ensuite, on voit que @bloc, @bloc2 et @bloc3 sont remplis successivement pour chaque valeur de $i, sans jamais être "vidé" entre temps. Ceci explique sans doute l'accumulation des valeurs dans @blocfinal.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  15. #15
    Membre à l'essai
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 37
    Points : 12
    Points
    12
    Par défaut
    Merci beaucoup Philou67430, j'ai fait comme tu as dit
    En définitive, "tout est bon" et le code fonctionne.

    Merci à tous pour votre aide, j'ai beaucoup après grâce à ce sujet.
    Très cordialement.

Discussions similaires

  1. Extraction de mots clés
    Par Olive1808 dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 01/02/2016, 20h49
  2. Extraction d'une paragraphe avec BeautifulSoup
    Par fraisa1985 dans le forum Général Python
    Réponses: 2
    Dernier message: 06/10/2013, 11h59
  3. [WD-2010] Extraction paragraphe Word vers un autre document Word
    Par josephhd dans le forum VBA Word
    Réponses: 1
    Dernier message: 09/05/2012, 13h22
  4. Extraction de paragraphes avec condition
    Par ctiguidou dans le forum Linux
    Réponses: 1
    Dernier message: 07/05/2012, 17h35
  5. [RegEx] Extraction des mot dans un paragraph avec les REGEX
    Par geforce dans le forum Langage
    Réponses: 0
    Dernier message: 29/03/2010, 18h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo