Probléme avec une expression régulière.

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Probléme avec une expression régulière.

par titerm » 03 juin 2007, 20:51

dans ton expression, $query contient déjà en toute logique une expression xpath, donc il te suffit de concaténer. Je ne sais pas ce que contient query, je ne peux pas donc etre plus précis mais si tu fais qq chose du genre :
$query .= '//td[@class]';
$trList = $xpath->query($query,$doc->documentElement);
ca devrait le faire

par apatride » 03 juin 2007, 12:51

Quelle syntaxe utiliser pour effectuer cette requête
//td[@class]
sur le sous-ensemble (objet) $nodes, obtenu par
$trList = $xpath->query($query,$doc->documentElement);
?

par titerm » 03 juin 2007, 12:07

e voudrais chercher parmi les fiston les td qui ont un attribut class.
La fonction getElementsByClassName n'existe pas !
// ==> tous les

td ==> ce que tu cherches

[] => ajoute une condition

@class => l'attribut class

soit

//td[@class]



Toi tu demandes //@class => tous les attributs class

par apatride » 03 juin 2007, 07:57

Bonjour,
De retour après quelques jours de congés ...

Dans la boucle :
foreach($tds as $td) {
on cherche les balises "table" :
      // Check si y a des tables en noeuds enfants
            $fiston = $td->getElementsByTagName('table') ;
Je voudrais chercher parmi les fiston les td qui ont un attribut class.
La fonction getElementsByClassName n'existe pas !
J'ai pensé à
$chercheEntete= '//@class';
mais je n'arrive pas à l'implémenter.
Comment faire ?

par titerm » 23 mai 2007, 02:27

Le motif
//qq chose/../*
est bien synonyme de
//qq chose/parent::*
?
Oui, mais je trouve .. plus lisible que parent::
Cette ligne compte bien le nombre de ligne (parent de td) ?

Code : Tout sélectionner

$qnumber = 'count(//td[contains(text(),"'.$needle.'")]/../*)';
Non, elle compte le nombre de colonne. On recherche tous les td qui contienne un certain texte, le noeuds courant est donc l'intérieur du td, on remonte d'un cran et on selectionne tous les fils soit tous les td de la ligne qui contenait $needle.


Comment traiter le cas ou un tableau est inclus dans une ligne ?
Aprés avoir trouvé la chaine "Chiffre d'affaires", je suis confronté à un tableau inséré dans des colonnes fusionnées !
En faisant un peu différment je pense, j'avais proposé ca pour donnée une idée. Plutot que de compter les noeuds, il faut peut etre etre plus carré, chercher tous les tr du tableau qui t'interresse, puis parcourrir tous les td de chaque tr. Dans chaque td, vérifier si y a un tableau et faire la meme manip (récursion)


Attention, ce bout code est a titre d'exemple, il ne gère pas le cas ou il y a plusieurs tableau dans un td par exemple, je t'indique la route, a toi ensuite de faire le chemin...

<?php

$doc = new DOMDocument();
@$doc->loadHTMLFile("http://www.boursorama.com/profil/resume_societe.phtml?symbole=1rPARR");
$xpath = new DOMXPath($doc);

// On encode la chaine a chercher en utf8
$needle = utf8_encode("Chiffre d'affaires");
// On recherche  dans tous les td une case de tableau qui contient la chaine rechercher, puis on recupere tous les tr du tableau qui match
$query= '//td[contains(text(),"'.$needle.'")]/../../*';
$trList = $xpath->query($query,$doc->documentElement);

function getData($nodes) {
	$line=0;
	foreach ($nodes as $tr) {
		$row=0;
		$tds = $tr->getElementsByTagName('td');
		foreach($tds as $td) {
			// Check si y a des tables en noeuds enfants
			$fiston = $td->getElementsByTagName('table') ;
			// Si oui, on recurse
			if($fiston->length) {
                                // J'ajoute un index bidon 'récursion' pour pouvoir le repérer plus facilement dans le dump
				// @todel une fois compris
				$data[$line][$row++]['recursion'] =  getData($fiston);
			}
			// Si non, on recupe jsute la valeur du noeud
			else {
				$data[$line][$row++] = $td->nodeValue;
			}
		}
		$line++;
		
	}
	return $data;
}

$data = getData($trList);
var_export($data);

par apatride » 23 mai 2007, 00:28

Bonsoir,
J'ai encore quelques questions :wink: :

Le motif
//qq chose/../*
est bien synonyme de
//qq chose/parent::*
?

Cette ligne compte bien le nombre de ligne (parent de td) ?

Code : Tout sélectionner

$qnumber = 'count(//td[contains(text(),"'.$needle.'")]/../*)';
Comment traiter le cas ou un tableau est inclus dans une ligne ?
Aprés avoir trouvé la chaine "Chiffre d'affaires", je suis confronté à un tableau inséré dans des colonnes fusionnées !
<TABLE BORDER="0" CELLPADDING="1" CELLSPACING="1" WIDTH="100%" CLASS="bright">

  <TR BGCOLOR="#FFFFFF">
    <TD COLSPAN="6">
      <TABLE BORDER="0" CELLPADDING="1" CELLSPACING="0" WIDTH="100%">
	<TR><TD CLASS="ligneItem" COLSPAN="2"><IMG SRC="http://img.boursorama.com/i/d.gif" WIDTH="1" HEIGHT="1"></TD></TR>
	<TR><TD VALIGN="TOP" CLASS="TitreE"><B>Compte de résultat</B></TD></TR>
      </TABLE>
    </TD>
  </TR>

	<tr><td class="l20" colspan="6" height="10"></td></tr>
  <TR CLASS="entetetab">
    <TD ALIGN="CENTER"><B>milliers EUR</TD>
				<TD ALIGN="CENTER"><B>12.01</TD>
				<TD ALIGN="CENTER"><B>12.02</TD>
				<TD ALIGN="CENTER"><B>12.03</TD>
				<TD ALIGN="CENTER"><B>12.04</TD>

				<TD ALIGN="CENTER"><B>12.05</TD>
		  </TR>

	
  <TR CLASS="L20">
    <TD NOWRAP ALIGN="LEFT">&nbsp;&nbsp;&nbsp;Chiffre d'affaires</TD>

par titerm » 21 mai 2007, 20:04

L'encodage est utf8.

A partir de maintenant, je pense qu'il faut etre un poil plus subtil et ne plus chercher la chaine exacte mais une sous chaine. De sorte que si il y a des espace (invisible au niveau html), tu ne passes pas a coté de la chaine recherchée.

Concernant les ', il n'y a pas de traitement particulier a effectuer, si cela ne matchait pas lors de tes essai, c'était certainement du a un caractère non visible. En utilisant contains(), tu devrais arriver a tes fins.

Exemple

// On encode la chaine a chercher en utf8
$needle = utf8_encode('Résultat opérationnel');
// On recherche  dans tous les td une case de tableau qui contient la chaine rechercher, puis on recuper r tous les td du tableau qui match
$query= '//td[contains(text(),"'.$needle.'")]/../..//td';

$tdList = $xpath->query($query,$doc->documentElement);

par apatride » 21 mai 2007, 16:48

Un problème a été éludé (même si vos conseils m'ont permis de faire un pas de géant) :
C'est l'encodage des caractères (je pense) !
Si je cherche la chaine "Résultat opérationnel", je n'ai aucun retour.
J'ai l'impression que les é posent problème.
Comment, par ailleurs, échapper les caractères , genre "Chiffre d'affaires" ? "Chiffre d\'affaires" ?

par titerm » 21 mai 2007, 15:53

Voila qui te permet d'avoir le contenu du tableau dans un tableau php $data

<?php

$doc = new DOMDocument();
@$doc->loadHTMLFile("http://www.boursorama.com/profil/resume_societe.phtml?symbole=1rPARR");
$xpath = new DOMXPath($doc);

$query = '//td[text()="BNA"]/../..//td';
$qnumber = 'count(//td[text()="BNA"]/../*)';
$rowNumber = $xpath->evaluate($qnumber,$doc->documentElement);
$tdList = $xpath->query($query,$doc->documentElement);

$row=0;
$line=0;
foreach ($tdList as $td) {
	if($row++ % $rowNumber === 0) 
		$line++;
	$data[$line][$row] = $td->nodeValue ;
}

var_dump($data);

par titerm » 21 mai 2007, 15:40

petite contrib:
utilise ça pour avoir simplement les données de la ligne de BNA
$query = '//td[text()="BNA"]/..'; 
D'après la demande, c'est le tableau complet qu'il l'interessait, la ligne BNA servait juste a retrouver le tableau au sein de la page.

par sadeq » 21 mai 2007, 13:18

petite contrib:
utilise ça pour avoir simplement les données de la ligne de BNA
$query = '//td[text()="BNA"]/..'; 

par titerm » 21 mai 2007, 12:58

<?php

$doc = new DOMDocument();
@$doc->loadHTMLFile("http://www.boursorama.com/profil/resume_societe.phtml?symbole=1rPARR");
$xpath = new DOMXPath($doc);

$query = '//td[text()="BNA"]/../..//td';
$qnumber = 'count(//td[text()="BNA"]/../*)';
$number = $xpath->evaluate($qnumber,$doc->documentElement);
$tables = $xpath->query($query,$doc->documentElement);

var_dump($number);
$id=0;
foreach ($tables as $table) {
	if($id++%$number === 0) echo '<br>TR:: ';
	echo $table->nodeValue .':' ;
}
Pour la perf, le cout sera vraisemblablement dans le load plutot que dans le parse. A mon avis, un petit coup de cache de page sera très efficace.

par apatride » 21 mai 2007, 12:39

J'avais essayé tidy mais le résultat n'est pas concluant.
Peut-être que j'ai mal positionné un paramètre ...
De toute manière, je n'ai pas envie d'y passer des heures.
La solution xpath me plait bien et je la conserve.
En fait, j'utilise xdebuget je vais utiliser les fonctions de profiling pour voir l'efficacité de cette librairie.

par titerm » 21 mai 2007, 10:05

Les warning sont du au fait que le html n'est pas tout a fait conforme.
Tu as 2 solutions pour ca.
- Une rapide, tu met un @ devant $doc->loadHTMLFile, ca resout rien met ca cache les warnings. En revanche il sont toujours loggés dans php_error.log
- Une plus subtile, tu passes un coup de tidy pour corriger les erreurs avant de parser le html. A voir si tidy sera capable de corriger automatiquement toute les erreurs.

Peut etre y a t il une troisieme solution directement lié a l'API DOM qui permet d'ignorer les warnings. A vérifier aussi.

par apatride » 21 mai 2007, 09:56

J'avais essayé d'utiliser cette méthode mais je n'avais que des warning et j'ai finalement abandonné.
Je trouve cette méthode élégante et c'est certainement celle que je pourrais rendre générique.
Je teste ça tout de suite.
Merci titerm ! :)