Recuperer des données spécifique dans un document string HTML

yodarkyodark Membre
11:08 modifié dans API AppKit #1
Bonjour à  tous,

Petite question, comment joueriez vous avec les string pour chercher une valeur précise dans un string ?

Exemple je voudrais récupérer cost value de "0.00" de GPRS et cost value de "0.00" pour SMS

&lt;tr class=&quot;cell-orange-1-L&quot;&gt;<br /> &nbsp;<br /><br /> &nbsp;	&lt;td class=&quot;left-top&quot;&gt;&lt;span id=&quot;communicationValue&quot;&gt;GPRS&lt;/span&gt;&lt;/td&gt;<br /> &nbsp; &nbsp;&lt;td class=&quot;middle-top&quot;&gt;&lt;span id=&quot;quantityValue&quot;&gt;15547&lt;/span&gt;&lt;/td&gt;<br /> &nbsp; &nbsp;&lt;td class=&quot;middle-top&quot;&gt;&lt;span id=&quot;unitValue&quot;&gt;KB&lt;/span&gt;&lt;/td&gt;<br /> &nbsp; &nbsp;&lt;td class=&quot;right-top&quot;&gt;&lt;span id=&quot;costValue&quot;&gt;0.00&lt;/span&gt;&lt;/td&gt;<br /> &nbsp;&lt;/tr&gt;<br /><br /> &nbsp;&lt;tr class=&quot;cell-orange-2-L&quot;&gt;<br /> &nbsp;<br /><br /> &nbsp;	&lt;td class=&quot;left-top&quot;&gt;&lt;span id=&quot;communicationValue&quot;&gt;SMS&lt;/span&gt;&lt;/td&gt;<br /> &nbsp; &nbsp;&lt;td class=&quot;middle-top&quot;&gt;&lt;span id=&quot;quantityValue&quot;&gt;20&lt;/span&gt;&lt;/td&gt;<br /> &nbsp; &nbsp;&lt;td class=&quot;middle-top&quot;&gt;&lt;span id=&quot;unitValue&quot;&gt;message&lt;/span&gt;&lt;/td&gt;<br /> &nbsp; &nbsp;&lt;td class=&quot;right-top&quot;&gt;&lt;span id=&quot;costValue&quot;&gt;0.00&lt;/span&gt;&lt;/td&gt;<br /> &nbsp;&lt;/tr&gt;

Réponses

  • Philippe49Philippe49 Membre
    11:08 modifié #2
    NSScanner avec
    scanUpToString: intoString:
    et
    scanDouble:


    ou alors carrément en C avec strstr(), et sscanf()
  • AliGatorAliGator Membre, Modérateur
    décembre 2008 modifié #3
    Pourquoi ne pas parser ton XML avec les classes Cocoa adéquates ?
    Bon là  tu n'as qu'une partie d'un XML, qu'il faudrait encapsuler dans un élément racine (car là  tu as 2 éléments au niveau zéro de ton XML, qui sont deux <tr>, alors qu'un XML valide a une racine unique), mais sinon une fois le NSXMLDocument (sous-classe de NSXMLNode) construit, tu peux faire des requêtes sur les éléments du XML en utilisant XQuery ou plus simplement XPath

    - Avec un XPath simple genre //tr[1]/td[4]/span tu peux récupérer le span du 4e td du 1er tr de ton XML. Et donc avoir le costValue du GPRS. Mais ça suppose que ton XML se présentera toujours sous cet ordre, et que tu n'auras pas un <tr> qui vienne s'intercaler avant ou après, changeant l'ordre de tes noeuds dans ton XML...
    - Avec le XPath plus complet du genre //tr/td/span[@id=";costValue"] si je ne me trompe pas, tu devrais obtenir tes deux "costValues" directement (un tableau avec tes costValues dans l'ordre dans lequel elles apparaà®ssent dans ton XML).
    - Et enfin si on veut être super précis, avec //tr[td/span[@id=';communicationValue']='GPRS']/td/span[@id=';costValue'] tu devrais avoir le costValue de GPRS quel que soit le cas : en gros je demande tous les "tr" de ton XML, je filtre (crochets) ensuite sur les TR en regardant le td -> span qui a pour attribut "id" la valeur "communicationValue", pour ne prendre que celui dont la valeur dans ce span est "GPRS", ce qui ne me garde que le premier des deux blocs "tr". Et enfin je prend le td de ce tr qui a comme id pour son span "costValue" et je regarde dedans pour avoir le coût. Bon c'est le genre de XPath qu'on construit par étapes hein mais au moins t'es sûr d'avoir ce que tu veux, le costValue qui est dans le même <tr> que celui où le communicationValue est GPRS, quel que soit l'ordre dans lequel ces entrées apparaissent dans le XML
    (Bon si tu as besoin que je te décompose pour ce dernier XPath tu le dis parce que quand on voit le XPath pour la première fois ça doit sans doute pas sauter aux yeux :P)

    Au final au feeling ça devrait donner un code du genre (en utilisant le dernier XPath que je mentionne, qui est le plus complet et donc ciblant à  coup sûr ta valeur, même si du coup plus complexe) :
    NSString* yourString = ...; // ta chaà®ne avec tes &lt;tr&gt;...&lt;/tr&gt;<br /><br />NSString* xmlString = [NSString stringWithFormat:@&quot;&lt;root&gt;%@&lt;/root&gt;&quot; , yourString]; // pour être sur d&#39;avoir un unique élément racine<br />NSXMLDocument* xmlDoc = [[NSXMLDocument alloc] initWithXMLString:xmlString options:0 error:nil];<br /><br />NSArray* gprs = [xmlDoc nodesForXPath: @&quot;//tr[td/span/[@id=&#39;communicationValue&#39;]=&#39;GPRS&#39;]/td/span[@id=&#39;costValue&#39;]&quot; error:nil];<br />double gprsCost = [[[gprs objectAtIndex:0] stringValue] doubleValue];<br /><br />NSArray* sms = [xmlDoc nodesForXPath: @&quot;//tr[td/span/[@id=&#39;communicationValue&#39;]=&#39;SMS&#39;]/td/span[@id=&#39;costValue&#39;]&quot; error:nil];<br />double smsCost = [[[sms objectAtIndex:0] stringValue] doubleValue];<br /><br />[xmlDoc release];
    
    (pas testé)
    Tu peux bien sûr remplacer le XPath utilisé par le XPath plus simple //tr[1]/td[4]/span mentionné plus haut par exemple... mais si ton bout de XML commence à  changer d'ordre (par exemple si tu as d'autres <tr> qui viennent s'intercaler entre celui du GPRS et celui du SMS, ou qu'une fois c'est le SMS qui est mis en premier et le GPRS en second et pas dans l'autre sens...) ou se complexifier ça ne marchera plus...
  • schlumschlum Membre
    11:08 modifié #4
    NSXMLParser
  • AliGatorAliGator Membre, Modérateur
    11:08 modifié #5
    Salut !

    J'ai failli proposer ça mais NSXMLParser est un parseur SAX (événementiel : ça parse au fur et à  mesure et appelle une méthode de delegate à  chaque fois que ça rencontre un tag ouvrant ou fermant etc) alors que l'utilisation de NSXMLDocument est du DOM (on parse tout l'arbre complètement et ensuite on accède à  l'élément en demandant par exemple "l'enfant du 3e enfant du 2e élément du 4e enfant du noeud racine")

    Autant je suis d'accord que pour des gros documents XML le SAX est mieux car on ne garde pas tout l'arbre en mémoire et on parse au fur et à  mesure. Mais c'est souvent pas aussi évident pour récupérer les données qu'on veut (là  par exemple il faut prévoir un flag pour dire dans quel communicationValue on est actuellement, mettre cette valeur lorsqu'on est informé qu'un tag span est ouvert mais seulement si son attribut id est "communicationValue", et si l'attribut est "costValue" regarder le dernier communicationValue dans lequel on est passé pour savoir auquel ce costValue correspond... et penser au cas où l'on pourrait avoir un communicationValue de défini sans costValue correspondant ou vice-versa.

    Mais là  le contenu XML/HTML étant en plus pas bien gros, utiliser du parsing DOM plutôt que SAX me parait plus simple à  utiliser (et pas tout plein de méthodes de delegate à  implémenter en plus). Même si les XPath nous font peur, on peut toujours demander avec NSXMLDocument "l'enfant du 4e élément du premier élément" pour tomber sur notre costValue de GPRS (équivalent du XPath "tr[1]/td[4]/span"). Alors que pour NSXMLParser on est plutôt parti pour créer une sous-classe qui va servir de delegate et contenir les variables d'instance servant pendant le parsing pour savoir où l'on en est à  peu près... Pas méchant mais un peu plus lourd pour si peu :)

    Voilà , après il est bon de savoir que les deux (NSXMLParser --> SAX et NSXMLDocument --> DOM) existent, d'autant que sur iPhone par exemple si tu veux y passer un jour, seul le parseur SAX (NSXMLParser) est disponible pour des questions de mémoire ;)
  • Philippe49Philippe49 Membre
    décembre 2008 modifié #6
    Bon y a quand même simple avec les NSScanner, pour une fois qu'ils ne sont pas dénigrés ...
    (d'ailleurs c'est aussi simple et plus rapide en C)


    #import <Foundation/Foundation.h>

    int main(int argc, char *argv[]) {
    NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
    NSString * string=@ ............ xxx \cost\">0.25<ffffffffffffffffffff  \"cost\">0.357<fffffffffffffffffff  \"cost\">.57< ";
    NSScanner * scanner=[NSScanner scannerWithString:string];

    while(
    [scanner scanUpToString:@cost intoString:nil]
    &&
    [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@0.123456789] intoString:nil]
    )
    {
    float x;
    [scanner scanFloat:&x];
    printf("%f\n",x);
    }

    [pool drain];
    return 0;
    }


    % gcc pgm.m -o pgm -framework Foundation
    % pgm
    0.250000
    0.357000
    0.570000
    %
  • AliGatorAliGator Membre, Modérateur
    11:08 modifié #7
    Je pense que les 3 méthodes ont leurs avantages et inconvénients.
    Le NSScanner est une solution pour laquelle on considère la chaà®ne comme du texte brut, ce qui est valable aussi.

    Après il n'a plus que l'embarras du choix sur les solutions :D Faut voir aussi l'usage qu'il veut en faire, si le texte est toujours exactement de ce genre ou si c'est une réponse à  une requête applicative sur un serveur (auquel cas du moment que la réponse respecte le XSD qu'il définit, rien n'impose l'ordre des éléments dans le XML de la réponse).
    C'est vrai que si c'est du parsing d'une page HTML statique un NSScanner peut aller. Après ça dépend d'où il récupère son HTML. Si c'est une réponse à  une requête PHP, genre il interroge une page PHP avec des critères et récupère le HTML obtenu, par contre, aucune garantie que le tableau HTML qu'il veut parser sera toujours dans le même ordre ;)
Connectez-vous ou Inscrivez-vous pour répondre.