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

Eléphanteau du PHP | 13 Messages

20 mai 2007, 11:13

Bonjour,

Je cherche à extraire des chiffres d'un tableau html.

Ce tableau est caractérisé par une chaîne qu'il contient.

Seulement, la mise en page du site est faite en utilisant des tableaux et celui qui m'intéresse se retrouve donc imbriqué dans toute une hiérarchie de tableaux.

Voici donc mon raisonnement :

je cherche une chaîne qui respecte le masque suivant :

<table quelque chose > (quelque chose chaîne caractéristique quelque chose)</table>

La ligne que j'ai écrite est la suivante :

Code : Tout sélectionner

preg_match_all("`<table.*?>(.*?$cible.*?)</table>`is",$contenu,$regs);

Problème : elle me retourne toute la hiérarchie de tableaux en plus de leurs contenus !
Ce que je cherche est noyé.

Je ne sais pas traduire la condition (quelque chose chaîne caractéristique quelque chose) ne contient pas table.
Si vis pacem para bellum

Mammouth du PHP | 505 Messages

20 mai 2007, 12:59

Déjà, tu ne dis pas précisément ce que tu cherches ? Est-ce ce que tu nommes chaine caractéristique ?

Ensuite, tu fais un preg_match_all. Tu cherches plusieurs choses ?

Si tu veux que l'on puisse t'aider plus efficacement, il faudrait donner aussi un exemple du texte que tu parses et précisément la string que tu veux extraire.

Eléphanteau du PHP | 13 Messages

20 mai 2007, 13:58

Voila qui devrait effectivement préciser ma demande.

Le texte que je parse : http://www.boursorama.com/profil/resume ... ole=1rPARR
La chaine caractéristique : BNA,
Je recherche le tableau qui contient BNA.
Si vis pacem para bellum

ViPHP
ViPHP | 1380 Messages

20 mai 2007, 17:01

Si tu veux capturer le contenu du tableau il faut d'abord isoler le tableau html qui contient la valeur cible. Ensuite, dans une deuxième regex, en extraire le contenu des cellules.
preg_match('#<TABLE.*?>*?>BNA<.*?</TABLE>#s', $txt, $capture1);
preg_match_all('#<TD[^>]*>([^<]+)</TD>#', $capture1[0], $capture2);
echo '<pre>'; print_r($capture2);
A essayer.
ripat

Eléphanteau du PHP | 13 Messages

20 mai 2007, 19:41

Merci. ça marche 8-)
Mais j'avoue ne pas tout comprendre :oops:
Modèle :

<TABLE
qqchose
>*? ---> [0 ou plusieurs >]
>BNA<
qqchose
</TABLE>
Exemple :

<TABLE
WIDTH="100%" CELLPADDING="2" CELLSPACING="1" CLASS="bright"
>
<TR CLASS="L20"><TD></TD><TD>2006</TD><TD>2007</TD><TD>2008</TD></TR><TR CLASS="L20"><TD align="left" -> rien n'est prévu ?
>BNA<
/TD>...
Si vis pacem para bellum

Mammouth du PHP | 505 Messages

20 mai 2007, 21:35

Dans un contexte comme celui la, je trouve que les regexp ne sont pas forcement la meilleure solution.
Un parsing en utilisant le dom me semble plus robuste.
$doc = new DOMDocument();
$doc->loadHTMLFile("http://www.boursorama.com/profil/resume_societe.phtml?symbole=1rPARR");
$xpath = new DOMXPath($doc);

// Un peu de xpath 
// tous les td dont le contenu est BNA, puis parent 2 fois pour remonter les td, puis tr (on se retrouve donc sur l'element table, puis tous les tr de cette table
// C'est evidemnt un exemple que tu peux peaufiner comme bon te semble
$query = '//td[text()="BNA"]/../..//tr';
$tables = $xpath->query($query,$doc->documentElement);
foreach ($tables as $table) {
	echo $table->nodeName . '::' . $table->nodeValue . '<br>';
}

Resultat

Code : Tout sélectionner

tr:: 2006 2007 2008 tr::BNA 2.18 2.51 2.77 tr::Dividende 2.16 1.81 2.53 tr::Rendement 2.89 % 2.42 % 3.38 % tr::PER 34.32 29.77 27.00

Eléphanteau du PHP | 13 Messages

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 ! :)
Si vis pacem para bellum

Mammouth du PHP | 505 Messages

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.

Eléphanteau du PHP | 13 Messages

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.
Si vis pacem para bellum

Mammouth du PHP | 505 Messages

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.

Modérateur PHPfrance
Modérateur PHPfrance | 2575 Messages

21 mai 2007, 13:18

petite contrib:
utilise ça pour avoir simplement les données de la ligne de BNA
$query = '//td[text()="BNA"]/..'; 
--------//////----//---//----//////
-------//---//----//---//----//---//
------//////----//////-----//////
-----||--------||--||---||
Prendre le recul n'est pas une perte de temps.


ps: Affrontez moi dans l'arène

Mammouth du PHP | 505 Messages

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.

Mammouth du PHP | 505 Messages

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);
Modifié en dernier par titerm le 21 mai 2007, 19:43, modifié 1 fois.

Eléphanteau du PHP | 13 Messages

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" ?
Si vis pacem para bellum

Mammouth du PHP | 505 Messages

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);