web-dev-qa-db-fra.com

HtmlAgilityPack et sélection des nœuds et sous-nœuds

J'espère que quelqu'un peut m'aider.

Disons que j'ai un document html qui contient plusieurs divs comme cet exemple:

<div class="search_hit">

    <span prop="name">Richard Winchester</span>
    <span prop="company">Kodak</span>
    <span prop="street">Arlington Road 1</span>

</div>
<div class="search_hit">

    <span prop="name">Ted Mosby</span>
    <span prop="company">HP</span>
    <span prop="street">Arlington Road 2</span>

</div>

J'utilise HtmlAgilityPack pour obtenir le document html. Ce que je dois savoir, c'est comment puis-je obtenir les portées pour chaque division "search_hit"?

Ma première pensée a été quelque chose comme ça:

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']"))
{
     foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("//span[@prop]"))
     {

     }
}

Chaque div doit être un objet avec les étendues incluses comme propriétés. C'est à dire.

public class Record
    {
        public string Name { get; set; }
        public string company { get; set; }
        public string street { get; set; }
    }

Et cette liste sera alors remplie:

public List<Record> Results = new List<Record>();

Mais le XPATH que j'utilise ne fait pas de recherche dans le sous-noeud comme il se doit. Il semble qu'il recherche encore et encore l'ensemble du document.

Je veux dire que je l'ai déjà fait fonctionner de cette façon que je reçois juste les étendues de la page entière. Mais alors je n'ai aucune relation entre les travées et les divs. Signifie: Je ne sais plus quelle durée est liée à quelle div.

Quelqu'un connaît-il une solution? J'ai déjà joué autant que je suis totalement confus maintenant :)

Toute aide est appréciée!

40
The Jack

Ce qui suit fonctionne pour moi. Le bit important est tout comme BeniBela l'a noté pour ajouter un point lors du deuxième appel à "SelectNodes".

List<Record> lstRecords=new List<Record>();
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']"))
{
  Record record=new Record();
  foreach (HtmlNode node2 in node.SelectNodes(".//span[@prop]"))
  {
    string attributeValue = node2.GetAttributeValue("prop", "");
    if (attributeValue == "name")
    {
      record.Name = node2.InnerText;
    }
    else if (attributeValue == "company")
    {
      record.company = node2.InnerText;
    }
    else if (attributeValue == "street")
    {
      record.street = node2.InnerText;
    }
  }
  lstRecords.Add(record);
}
34
shriek

Si tu utilises //, il recherche à partir du document commencer.

Utilisation .// pour tout rechercher à partir du nœud actuel

 foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]"))

Ou supprimez entièrement le préfixe pour rechercher uniquement les enfants directs:

 foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("span[@prop]"))
53
BeniBela

Tout d'abord, jetez un œil à ceci: Pack d'agilité Html - Problème de sélection du sous-noeud

Voici une solution de travail complète pour votre question:

IList<Record> results = new List<Record>();
foreach (var node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) {
    var record = new Record();
    record.Name = node.SelectSingleNode(".//span[@prop='name']").InnerText;
    record.company = node.SelectSingleNode(".//span[@prop='company']").InnerText;
    record.street = node.SelectSingleNode(".//span[@prop='street']").InnerText;
    results.Add(record);
}

Si vous lisez la question que je vous ai indiquée, vous verrez que faire ./span[@prop='name'] est exactement le même, car ces nœuds span sont les enfants (directs) du nœud div.


Si les nœuds span n'ont pas ces attributs prop et que vous souhaitez les affecter en fonction de l'ordre dans lequel ils apparaissent, vous pouvez faire:

foreach (var node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) {
    var spanNodes = node.SelectNodes("./span");
    var record = new Record();
    record.Name = spanNodes[0].InnerText;
    record.company = spanNodes[1].InnerText;
    record.street = spanNodes[2].InnerText;
    results.Add(record);
}
3
Oscar Mederos

Honte sur moi :)

Vous aviez tous raison.

J'ai trouvé le problème. Cette NullReferenceException ne cessait de me harceler, j'ai donc passé plus de temps à l'examiner en détail. Entre tous ces divs il y avait un div avec le même attribut "class = 'search-hit'" mais sans les portées à l'intérieur. C'est pourquoi il traverse une erreur à la deuxième boucle.

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//span[@prop]/ancestor::div[@class='search_hit']"))
   {
        Record rec = new Record();
        foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]"))
           {
           }
           rList.Results.Add(rec);
   }

Le code ci-dessus fonctionne.

Merci les gars pour votre temps et votre aide!

2
The Jack

J'ai utilisé ça. id de conversion de classe

  HtmlNodeCollection nodes = dokuman.DocumentNode.SelectNodes("//div[@id='search_hit']//span[@prop]");


            for (int i = 0; i < nodes .Count; i++)
        {
            var record = new Record();


                record.Name = links[i].InnerText;   results.Add(record);  }
0
ibrahim ozboluk