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 PHP Discussion :

Expression rationnelle pour détecter du HTML


Sujet :

Langage PHP

  1. #1
    Membre averti Avatar de chatofor
    Profil pro
    Inscrit en
    Août 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 59
    Par défaut Expression rationnelle pour détecter du HTML
    Bonjour,

    Je veux faire un script qui récupère le contenu de balises HTML paragraphes. Récupérer ce qu'il y a dans <p></p>

    Voici mon sript tout simple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $file_contents = "<p>Involontairement au d&eacute;part, plus ou moins volontairement maintenant</p><p>Je l'ai pris involontairement (je dirais m&ecirc;me que c'est les anciens &quot;amis&quot; qui m'y on emmen&eacute;).<br />
    Mais finalement la solitude me pla&icirc;t. J'ai l'impression que c'est fait pour moi.</p>";
    preg_match_all("#<p>(.*)</p>#", $file_contents, $out);
    echo 'affichage';
    Problème, tout n'est pas inclu. La première phrase oui, la deuxième pas du tout.

    Avez-vous une idée de pourquoi ?

    Merci d'avance

    edit : dans mon code les caractères spéciaux sont encodés en HTML genre é = & eacute ;

  2. #2
    Membre expérimenté Avatar de zancrows
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2016
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2016
    Messages : 159
    Par défaut
    Bonjour, déjà tu devrais échapper le slash dans ton regex
    en suite la 2ème phrase ne match pas parce qu'il le caractère 'entrer' ( \r\n ) après le
    <br />
    tu peux faire des test avec ce site https://regex101.com/r/MW8YOQ/3/

  3. #3
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Pour parser du html on utilise DOMDocument, pas les regex:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $dom = new DOMDocument;
    $dom->loadHTMLFile($yourfile);
     
    $result = [];
     
    foreach ($dom->getElementsByTagName('p') as $pNode) {
        $content = '';
        // on concatène les nœuds enfants du paragraphe
        foreach ($pNode->childNodes as $childNode) {
            $content .= $dom->saveHTML($childNode);
        }
        $result[] = $content;
    }
    démo

  4. #4
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Citation Envoyé par zancrows Voir le message
    Bonjour, déjà tu devrais échapper le slash dans ton regex
    Absolument pas, le slash n'est pas un caractère spécial. Il ne dois être échappé que lorsque le slash est utilisé comme délimiteur de pattern.

    la 2ème phrase ne match pas parce qu'il le caractère 'entrer' ( \r\n )
    'entrée' est plus une touche du clavier qu'un caractère, ensuite suivant le système utilisé, cette touche peux produire dans un éditeur l'une des séquences de caractères suivantes: \n ou \r\n ou \r. Mais effectivement le . ne matche pas le \n par défaut, pour qu'il le fasse, il faut utiliser le modificateur s.

    Mais ce n'est pas le seul problème de cette pattern.

  5. #5
    Membre expérimenté Avatar de zancrows
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2016
    Messages
    159
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2016
    Messages : 159
    Par défaut
    Citation Envoyé par CosmoKnacki Voir le message
    Absolument pas, le slash n'est pas un caractère spécial. Il ne dois être échappé que lorsque le slash est utilisé comme délimiteur de pattern.
    autant pour moi

  6. #6
    Membre averti Avatar de chatofor
    Profil pro
    Inscrit en
    Août 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 59
    Par défaut
    Ca paraît effectivement plus simple. Le code une fois adapté à mes besoins donne ceci :

    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
    <?php
    $ch = curl_init();
    $timeout = 0; // set to zero for no timeout
    curl_setopt ($ch, CURLOPT_URL, 'http://m.jeuxvideo.com/forums/42-51-55256100-1-0-1-0-le-chemin-de-la-solitude.htm');
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    $file_contents = curl_exec($ch);
    curl_close($ch);
     
     
    $dom = new DOMDocument;
    $dom->loadHTML($file_contents);
     
    $result = [];
     
    foreach ($dom->getElementsByTagName('p') as $pNode) {
        $content = '';
        foreach ($pNode->childNodes as $childNode) {
            $content .= $dom->saveHTML($childNode);
        }
        $result[] = $content;
    }
    ?>
    <pre><?php var_dump($result); ?></pre>
    Seulement la ligne 12 renvoie une erreur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Warning: DOMDocument::loadHTML(): Tag header invalid in Entity, line: 19 in /opt/lampp/htdocs/tests/yams/alsa3.php on line 12
     
    Warning: DOMDocument::loadHTML(): Tag section invalid in Entity, line: 25 in /opt/lampp/htdocs/tests/yams/alsa3.php on line 12
     
    Warning: DOMDocument::loadHTML(): Tag footer invalid in Entity, line: 421 in /opt/lampp/htdocs/tests/yams/alsa3.php on line 12
    J'ai cherché du côté de google et j'ai trouvé qu'il fallait entrer ce code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     libxml_use_internal_errors(true);
    Je n'ai pas cherché donc compris pourquoi mais je te remercie pour ton aide

    Je n'ai pas vraiment compris l'histoire du deuxième foreach avec le childNodes mais je vais y réfléchir

  7. #7
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Par défaut, lorsque libxml (la librairie qui se cache derrière les classes de manipulation du DOM en PHP) détecte une erreur dans le code html, un warning est affiché, ce qui est plutôt gênant.
    Ce que fait libxml_use_internal_errors(true), c'est de rediriger ces erreurs vers le gestionnaire d'erreurs de libxml. Ainsi, les erreurs ne sont plus affichées mais tu peux par contre les consulter via libxml_get_errors() qui renvoie un tableau de ces erreurs.
    Autre chose à savoir sur le fonctionnement de libxml_use_internal_errors(), cette fonction renvoie son réglage précédent, ce qui fait qu'on écrit couramment:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $dom = new DOMDocument;
    $state = libxml_use_internal_errors(true); // on stocke l'état précédent
    $dom->loadHTML($html); // c'est ici que la chaîne est parsée et que les erreurs sont détectés.
    libxml_use_internal_errors($state); // on restitue l'état précédent, ainsi on impacte pas les scripts éventuels qui peuvent suivre

  8. #8
    Membre averti Avatar de chatofor
    Profil pro
    Inscrit en
    Août 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 59
    Par défaut
    C'est quoi une "erreur" dans le code html ?

    Parce que j'ai déjà vu des <p></p></p> par exemple mais ça n'a pas l'air d'être considéré comme une erreur.

    Dans mon exemple précédent les trois erreurs étaient bizarrement : header, section et footer.

  9. #9
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Citation Envoyé par chatofor Voir le message
    Je n'ai pas vraiment compris l'histoire du deuxième foreach avec le childNodes mais je vais y réfléchir
    Pourquoi ce deuxième foreach?
    La méthode DOMDocument::saveHTML() renvoie la chaîne du nœud passé en argument mais avec les tags du nœud en question. Donc si $pNode correspond à <p>toto</p>, tu obtiendras <p>toto</p> et non toto.

    D'autre part, si chacun de tes paragraphes ne contenaient qu'un seul nœud texte, on pourrait très bien s'en passer et obtenir leur contenu en faisant:$content = $pNode->nodeValue; qui renvoie tout les éléments texte d'un nœud (y compris celui de ces nœuds enfant). Mais ce n'est pas le cas ici.
    Si on prend le contenu du deuxième paragraphe:
    Je l'ai pris involontairement (je dirais m&ecirc;me que c'est les anciens "amis" qui m'y on emmen&eacute;).<br />
    Mais finalement la solitude me pla&icirc;t. J'ai l'impression que c'est fait pour moi.
    On a:
    • un nœud texte:
      Je l'ai pris involontairement (je dirais m
    • une entité
      &ecirc;
    • un nœud texte:
      me que c'est les anciens "amis" qui m'y on emmen
    • une entité
      &eacute;
    • un nœud texte:
      ).
    • un élément <br />
    • un nœud texte:
      Mais finalement la solitude me pla
    • etc.


    Ces éléments sont de nature différente, donc la seule manière de récupérer le contenu d'une balise consiste à boucler sur ses nœuds enfant et à concaténer leur contenu. La propriété DOMNode::childNodes renvoie une instance de DOMNodeList qui contient les enfants directs d'un élément.

  10. #10
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Concernant tes trois erreurs: Tag header/section/footer invalid.

    La version de libxml qu'utilise PHP est ancienne et ne connaît pas les tags html5.


    Pour ce qui est de <p></p></p>, c'est bien évidemment une erreur et libxml te le signalera par un: Unexpected end tag.

  11. #11
    Membre averti Avatar de chatofor
    Profil pro
    Inscrit en
    Août 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 59
    Par défaut
    Merci pour les explications je comprends un peu mieux.

    J'imagine qu'il est envisageable de pouvoir préciser également le nom de la classe à laquelle doit appartenir l'élément qu'on recherche ?

    Exemple : Récupérer tous les paragraphes marqué <p class="ceci"></p>

    Je ne trouve pas sur la doc officielle http://php.net/manual/fr/domelement....sbytagname.php

  12. #12
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Tu peux déjà boucler sur les éléments p et vérifier si pour chacun l'attribut class existe (DOMElement::hasAttribute), puis tu peux obtenir le contenu de l'attribut (DOMElement::getAttribute) et donc vérifier ce qu'il contient. Sinon, pour obtenir directement les paragraphes p avec une classe précise, tu dois utiliser une requête XPath:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $xp = new DOMXPath($dom);
    $pNodes = $xp->query('//p[contains(concat(" ", normalize-space(@class), " "), " ceci ")]');

  13. #13
    Membre averti Avatar de chatofor
    Profil pro
    Inscrit en
    Août 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 59
    Par défaut
    Salut,

    Est-ce que tu peux me dire où tu as appris tout ça ?

    Bon j'ai avancé un peu dans mon script :

    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
    <?php
    $ch = curl_init();
    $timeout = 0; // set to zero for no timeout
    curl_setopt ($ch, CURLOPT_URL, 'http://m.jeuxvideo.com/forums/42-51-55230469-1-0-1-0-celestin-tu-possede-quoi-comme-telephone-un-xigotochi-8go-de-ram.htm');
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    $file_contents = curl_exec($ch);
    curl_close($ch);
     
     
     
     
    $dom = new DOMDocument;
     libxml_use_internal_errors(true);
    $dom->loadHTML($file_contents);
     
    $result = [];
     
    foreach ($dom->getElementsByTagName('p') as $pNode) {
        $content = '';
        foreach ($pNode->childNodes as $childNode) {
            $content .= $dom->saveHTML($childNode);
     
    		}
        $result[] = $content;
    }
     
     
    foreach ($dom->getElementsByTagName('a') as $aNode) {
        $content2 = '';
        foreach ($aNode->childNodes as $childNode) {
    		if($aNode->hasAttribute('class') == TRUE)
    		{
    			if($attribute = $aNode->getAttribute('class'))
    			{
    				if( $attribute === 'bloc-pseudo-msg text-auteur text-user' )
    				{
    			        $content2 .= $dom->saveHTML($childNode);
    				}
     
     
     
     
     
    			}
    			else
    			{
    				echo 'fail';
    			}
     
    		}
    	}
     
     
        $result2[] = $content2;
    }
     
     
    ?>
    <pre><?php var_dump($result); ?>
    </pre>
     
    <hr>
     
    <pre><?php var_dump($result2); ?>
    </pre>
     
     
     
     
    <?php
    /*
    foreach ($result as $cle => $valeur) {
    	
    	if(preg_match_all("#(youtube.*)#", $valeur, $out) )
    	{
    		$sortie[] = $out;
    	}
    }
    ?>
     
    <hr>
    <pre>
    <?php var_dump($sortie);
    */?></pre>
    Je rencontre deux soucis, le premier c'est que les balises p sont traitées de la même manière indifféremment si elles font partie du même post ou non. Le deuxième c'est que je sélectionne tous les liens dont la classe est bloc-pseudo-msg text-auteur text-user ce qui fonctionne excepté qu'il me sort quand même les autres liens vraisemblablement, sous forme de chaine vide.

    Si quelqu'un a une idée, une piste...

  14. #14
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    3 018
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 3 018
    Par défaut
    Est-ce que tu peux me dire où tu as appris tout ça ?
    Rien de mystérieux, c'est comme pour tout: documentation PHP + tutoriels + pratique.

    les balises p sont traitées de la même manière indifféremment si elles font partie du même post ou non
    La méthode getElementsByTagName peut s'appliquer à n'importe quel élément et dans ce cas elle renverra uniquement les éléments p descendants de cet élément.

    Si tu veux obtenir les paragraphes séparément pour chaque post:
    tu dois obtenir une nodelist de tes posts, boucler dessus et pour chaque post, obtenir une nodelist de tes p.
    Si tu veux cibler un post particulier: arrange toi pour l'atteindre d'une manière ou d'une autre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if($aNode->hasAttribute('class') == TRUE)
    La méthode hasAttribute renvoie un boolean, donc soit tu écris if ( $aNode->hasAttribute('class') ) (ce qui fait exactement la même chose), soit tu écris avec une comparaison stricte: if ( $aNode->hasAttribute('class') === true ).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if($attribute = $aNode->getAttribute('class'))
    Écrire ça est plutôt vilain, d'autant que trois lignes plus tard tu testes la valeur de ton attribut. Autant écrire simplement: $attribute = $aNode->getAttribute('class');, ça fera un if de moins dans le code, sauf si tu tiens pour une raison spéciale à tester si l'attribut est vide. (Dans ce cas fait un teste avec empty($attribute)).

    Après sans ton code html, je ne peux pas t'en dire plus.

Discussions similaires

  1. [XL-2010] Expressions rationnelles pour des dates (point de point)
    Par Alba1981 dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 24/02/2017, 15h53
  2. Expression rationnelle pour éliminer symbole
    Par Invité dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 01/08/2015, 20h13
  3. [RegEx] expression régulière pour détecter apostrophe
    Par flilou dans le forum Langage
    Réponses: 4
    Dernier message: 27/06/2013, 15h01
  4. [RegEx] Expression régulière pour les balises HTML
    Par meloo dans le forum Langage
    Réponses: 3
    Dernier message: 09/07/2009, 15h23
  5. Cherche Expression rationnelle pour isoler une chaîne
    Par ritual dans le forum Shell et commandes GNU
    Réponses: 10
    Dernier message: 18/08/2008, 14h31

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