Extraire le texte de plusieurs balises <p>

WRInaute passionné
Saut à tous,

Je cherche à extraire le texte d'une longueur de plus de 15 mots qui est dans les balises <p></p> contenu dans ma variable $html ci-dessous.

Voici mon code :
PHP:
$html = '<p class="ma_class1">C\'est mon premier paragraphe qui compte 8 mots.</p><a href="#" title="mon titre" class="ma_class2">mon lien</a><p class="ma_class1">C\'est mon deuxième paragraphe qui est un peu plus long et qui compte 15 mots.</p><p class="ma_class1">C\'est mon troisième paragraphe qui est beaucoup plus long que les 2 premiers et qui compte au total 20 mots.</p>';

$nombre_mots = 15;

preg_match_all("/<p[^>]*>(.*)<\/p>/", $html, $result, PREG_PATTERN_ORDER);

for($i=0;$i<count($result[0]);$i++){
    $compte_mots = str_word_count($result[0][$i]);
    if($compte_mots>$nombre_mots){$contenu = $result[0][$i];}
}
echo $contenu;

Ce code php me retourne tout ce qui est contenu dans ma variable $html (même le lien contenu dans la balise <a>), qu'est-ce qui est faux dans mon code ?

Il devrait me retourner uniquement le texte contenu dans ma dernière balise <p>, soit : C'est mon troisième paragraphe qui est beaucoup plus long que les 2 premiers et qui compte au total 20 mots.

Merci pour votre aide.
 
WRInaute accro
PHP:
<?php
ini_set('display_errors', 1);

$html = '<p class="ma_class1">C\'est mon premier paragraphe qui compte 8 mots.</p><a href="#" title="mon titre" class="ma_class2">mon lien</a><p class="ma_class1">C\'est mon deuxième paragraphe qui est un peu plus long et qui compte 15 mots.</p><p class="ma_class1">C\'est mon troisième paragraphe qui est beaucoup plus long que les 2 premiers et qui compte au total 20 mots.</p>';

$words = 15;

$doc = new DOMDocument();
$doc->loadHTML($html);

$paragraphs = $doc->getElementsByTagName('p');

foreach ($paragraphs as $paragraph) {
    $count = str_word_count($paragraph->nodeValue);
    if ($count > $words) {
        var_dump($paragraph->nodeValue);
    }
}
 
WRInaute impliqué
@spout a raison de proposer autre chose que du regex pour exploiter du HTML. Je suis très amateur des expression rationnelles, mais elles montrent leurs limites pour traiter un document complexe comme du SGML.

Ce code php me retourne tout ce qui est contenu dans ma variable $html (même le lien contenu dans la balise <a>), qu'est-ce qui est faux dans mon code ?

Le code devrait retourner tout ce qui est entre <p class="ma_class1"> et </p> à l'exceptions de ces deux contenus, donc pas tout $html. C'est le comportement attendu par rapport à ta requête. Il n'y a qu'un seul match.

La raison est que l’opérateur * d'une regex est "gourmand" (greedy) par défaut, c'est à dire qu'il va capturer tout ce qu'il peut tant que ça satisfait à la regex. Il n'a donc aucune raison de s'arrêter au premier </p> alors qu'il en a un autre après. Il est possible de modifier ce comportement en rendant la regex ungreedy (drapeau U) :
PHP:
preg_match_all("/<p[^>]*>(.*)<\/p>/U", $html, $result, PREG_PATTERN_ORDER);
 
WRInaute passionné
PHP:
<?php
ini_set('display_errors', 1);

$html = '<p class="ma_class1">C\'est mon premier paragraphe qui compte 8 mots.</p><a href="#" title="mon titre" class="ma_class2">mon lien</a><p class="ma_class1">C\'est mon deuxième paragraphe qui est un peu plus long et qui compte 15 mots.</p><p class="ma_class1">C\'est mon troisième paragraphe qui est beaucoup plus long que les 2 premiers et qui compte au total 20 mots.</p>';

$words = 15;

$doc = new DOMDocument();
$doc->loadHTML($html);

$paragraphs = $doc->getElementsByTagName('p');

foreach ($paragraphs as $paragraph) {
    $count = str_word_count($paragraph->nodeValue);
    if ($count > $words) {
        var_dump($paragraph->nodeValue);
    }
}
Merci Spout, comme d'habitude ton code marche parfaitement bien ;)
 
WRInaute passionné
@spout a raison de proposer autre chose que du regex pour exploiter du HTML. Je suis très amateur des expression rationnelles, mais elles montrent leurs limites pour traiter un document complexe comme du SGML.



Le code devrait retourner tout ce qui est entre <p class="ma_class1"> et </p> à l'exceptions de ces deux contenus, donc pas tout $html. C'est le comportement attendu par rapport à ta requête. Il n'y a qu'un seul match.

La raison est que l’opérateur * d'une regex est "gourmand" (greedy) par défaut, c'est à dire qu'il va capturer tout ce qu'il peut tant que ça satisfait à la regex. Il n'a donc aucune raison de s'arrêter au premier </p> alors qu'il en a un autre après. Il est possible de modifier ce comportement en rendant la regex ungreedy (drapeau U) :
PHP:
preg_match_all("/<p[^>]*>(.*)<\/p>/U", $html, $result, PREG_PATTERN_ORDER);
Merci à toi aussi emualliug pour ta réponse.

Il me manquait pas grand chose pour que ça fonctionne, juste un « U » à la fin de ma regex. J'ai testé mon code en ajoutant ce U et ça marche très bien.
 
WRInaute passionné
Petite question supplémentaire, dans la variable ci-dessous il y a des Hex code &#x27; à la place des apostrophes.
PHP:
$texte = '<p>Il est temps de comprendre comment ça marche car l&#x27;utilité de ce système est démontré.</p>';
Existe-t-il une fonction php qui remplace automatiquement tous les Hex code du type &#x27; par leurs équivalents html (remplacer &#x27; par une apostrophe classique « ' ») ou faut-il passer par un str_replace("&#x27;", "'", $texte); ?
 
WRInaute passionné
Merci pour ta réponse emualliug, mais sauf erreur de ma part, la fonction htmlspecialchars_decode() ne convertit pas les Hex code du type &#x27; par leurs équivalents html (apostrophe dans ce cas).
ChatGPT nous donne la réponse avec ce code :
PHP:
$text = preg_replace_callback('/&#x([0-9a-fA-F]+);/', function ($match) {
        return mb_convert_encoding('&#x'.$match[1].';', 'UTF-8', 'HTML-ENTITIES');
    }, $text);
Tu lui poses la question et il te donne le code.... totalement délirant cet IA :cool:
 
Discussions similaires
Haut