HtmlAgilityPack и выбор узлов и субнодов
Надеюсь, кто-то может мне помочь.
Предположим, у меня есть html-документ, содержащий несколько div, подобных этому примеру:
<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>
Я использую HtmlAgilityPack для получения html-документа. Мне нужно знать, как я могу получить промежутки для каждого "search_hit" -div?
Моя первая мысль была примерно такой:
foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']"))
{
foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("//span[@prop]"))
{
}
}
Каждый div должен быть объектом с включенными пролетами как свойствами.
I. e.
public class Record
{
public string Name { get; set; }
public string company { get; set; }
public string street { get; set; }
}
И этот список будет заполнен:
public List<Record> Results = new List<Record>();
Но XPATH, который я использую, не выполняет поиск в подзоне, как он должен делать. Он швыряет, что он снова и снова ищет весь документ.
Я имею в виду, что я уже работал таким образом, чтобы получить только пробелы всей страницы. Но тогда у меня нет отношения между пролетами и div. Значит: я больше не знаю, какой промежуток связан с каким div.
Кто-нибудь знает решение? Я уже так много играл, что теперь я полностью смущен:)
Любая помощь приветствуется!
Ответы
Ответ 1
Следующие работы для меня. Важный бит точно так же, как BeniBela отметил добавление точки во втором вызове "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);
}
Ответ 2
Если вы используете //
, он ищет от начала документа.
Используйте .//
для поиска всех из текущего node
foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]"))
Или полностью отбросить префикс для поиска только для прямых детей:
foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("span[@prop]"))
Ответ 3
Прежде всего, взгляните на это: Html Agility Pack - проблема выбора подноса
Вот полное рабочее решение для вашего вопроса:
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);
}
Если вы прочитаете вопрос, на который я указал вам, вы увидите, что выполнение ./span[@prop='name']
точно такое же, поскольку те span
узлы являются (прямыми) дочерними элементами div
node.
Если узлы span
не имеют этих атрибутов prop
, и вы хотите назначить их в зависимости от того, какой порядок они отображаются, вы можете сделать:
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);
}
Ответ 4
Позор мне:)
Все вы были правы.
Я нашел проблему. Это исключение NullReferenceException продолжало насмехаться, поэтому я потратил больше времени, чтобы подробно рассмотреть его.
Между всеми этими divs был один div с тем же атрибутом class= 'search-hit' ', но без внутренних интервалов. Вот почему он пропускает ошибку во втором цикле.
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);
}
Работающий выше код.
Спасибо, ребята, за ваше время и помощь!
Ответ 5
Я использовал это. 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); }