Htmlagilipack i wybieranie węzłów i Podmodułów

Mam nadzieję, że ktoś mi pomoże.

Powiedzmy, że mam dokument html, który zawiera wiele div, jak w tym przykładzie:

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

Im za pomocą HtmlAgilityPack, aby uzyskać dokument html. Co muszę wiedzieć, jak Mogę uzyskać zakresy dla każdego "search_hit" - div?

Moja pierwsza myśl była taka:

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

     }
}

Każdy div powinien być obiektem z dołączonymi zakresami jako właściwościami. I. E.

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

I listę tę wypełnia się wtedy:

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

Ale XPATH im za pomocą nie robi wyszukiwania w podnodzie, jak powinno to zrobić. Szwy, że przeszukuje cały dokument ponownie i ponownie.

Mam na myśli, że już to działa w ten sposób, że po prostu uzyskać przęsła całej strony. Ale wtedy nie mam związku między przęsłami i divami. Znaczy: Nie wiem już, który zakres jest związany z którym div.

Czy ktoś zna jakieś rozwiązanie? Grałem już tyle, że jestem teraz całkowicie zdezorientowany:)

Każda pomoc jest doceniam to!

Author: The Jack, 2013-02-21

5 answers

Poniżej działa dla mnie. Ważnym bitem jest tak jak zauważył BeniBela dodanie kropki w drugim wywołaniu do '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);
}
 29
Author: shriek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-21 23:06:38

Jeśli używasz //, przeszukuje od początku dokumentu.

Użyj .//, aby przeszukać wszystkie z bieżącego węzła

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

Lub porzuć przedrostek całkowicie, aby wyszukać tylko bezpośrednie dzieci:

 foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("span[@prop]"))
 47
Author: BeniBela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-21 13:55:06

Po pierwsze, spójrz na to: Html Agility Pack-Problem z wyborem podnodu

Oto pełne rozwiązanie dla Twojego pytania:

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

Jeśli przeczytasz pytanie, na które Ci zwróciłem uwagę, zobaczysz, że robienie ./span[@prop='name'] jest dokładnie takie samo, ponieważ te span węzły są (bezpośrednimi) dziećmi div węzła.


Jeśli span węzły nie mają atrybutów prop i chcesz je przypisać w zależności od kolejności, w jakiej się pojawiają, możesz do:

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);
}
 2
Author: Oscar Mederos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:09

Wstyd mi:)

Wszyscy mieliście rację. Znalazłem problem. Ta NullReferenceException dokuczała mi, więc spędziłem więcej czasu, aby przyjrzeć się jej szczegółowo. Pomiędzy tymi divami był jeden div z tym samym atrybutem "class=' search-hit'", ale bez przęseł wewnątrz. Dlatego przechodzi błąd w drugiej pętli.
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);
   }

Powyższy kod działa.

Dziękuję wam za Wasz czas i pomoc!

 2
Author: The Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-22 12:47:08

Użyłem tego. class convert id

  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
Author: ibrahim ozboluk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 07:54:24