2014-08-27 4 views
1

Я очищаю некоторые данные, используя HtmlAgilityPack.HtmlAgilityPack NextSibling.InnerText значение пусто

HTML, выглядит следующим образом:

<div id="id-here"> 
    <dl> 
    <dt> Field Name </dt> 
    <dd> Value for above field name </dd> 
    <dt> Field Name </dt> 
    <dd> Value for above field name </dd> 
    <dt> Field Name </dt> 
    <dd> Value for above field name </dd> 
    </dl> 
</div> 

Теперь проблема у меня есть, что не всегда есть определенное количество полей, так что я не могу надежно получить доступ к каждому из них нравится:

//*[@id="id-here"]/dl[1]/dd[1] 

поскольку dd [1] может быть именем на одной странице и телефоном на другом, где пользователь не смог заполнить имя, поэтому поле скрыто.

, так что я захватить все DT и DD узлы следующим образом:

//*[@id="id-here"]/dl[1]/dt | //*[@id="id-here"]/dl[1]/dd 

Теперь я проверяю каждый узел, чтобы увидеть, если он соответствует поле я хочу, и принимает значение NextSibling так:

foreach (HtmlNode node in details) 
    { 
     if (node.InnerText.Contains("Tel:")) telephone = node.NextSibling.InnerText; 
     if (node.InnerText.Contains("Email:")) email = node.NextSibling.InnerText; 
    } 

Это нормально работает для телефона, но по какой-то причине, когда появляется узел «Email:», оба NextSibling.InnerHTML & NextSibling.InnerText пустые, хотя у следующего брата определенно есть данные. Если я действительно перейду к этому node в details и посмотрю на него InnerHTML - вся отформатированная ссылка, а InnerText - это адрес электронной почты.

Не работает ли NextSibling.InnerText, потому что тег A делает его ребенком или чем-то еще? Я посмотрел в отладчике и просто не могу найти нужную мне информацию под NextSibling.

Я уверен, что ответ смехотворно прост, я просто не могу понять это. Кто-нибудь вывел меня из моих страданий?

+0

Относительно отдельный вопрос, но почему вы выбираете элементы 'dd', если вы на самом деле не планируете использовать выбранные' dd 'как вы итерации через' details'? – JLRishe

+0

Чтобы я мог выбрать nextSibling. Если я не выберу DD, то они не будут там, чтобы быть следующей. – Guerrilla

+0

Содержимое 'details' не имеет отношения к тому, что есть у братьев и сестер, и именно это вы видите здесь. – JLRishe

ответ

8

Причина, это происходит в том, что, если node является dt элементом, который отделен от его соответствующего элемента dd некоторых пробелами, то node.NextSibling является все-пробельный текстовым узлом (пространство между </dt> и <dd>). Если вы посмотрите на него в отладчике, вы увидите, что node.NextSibling: NodeType: HtmlNodeType.Text, а не HtmlNodeType.Element.

Я предлагаю создать удобный метод, чтобы получить текст dt узла соответствующий dd:

internal static string GetMatchingDdValue(HtmlNode dtNode) 
{ 
    var found = dtNode.SelectSingleNode("following-sibling::*[1][self::dd]"); 
    return found == null ? "" : found.InnerText; 
} 

Затем вы можете использовать его как это:

if (node.InnerText.Contains("Tel:")) { telephone = GetMatchingDdValue(node); } 

Вот разбивка несколько сложный XPath, используемый в моем методе выше:

(a) following-sibling::* 

^Выберите все элементы, которые имеют один и тот же родительский элемент в качестве текущего узла и после него.

(b) following-sibling::*[1] 

^Выберите первый узел в наборе (а) (если таковые имеются)

(c) following-sibling::*[1][self::dd] 

^Выберите все узлы в наборе (б), что элементы с именем «дд»

SelectSingleNode() выбирает первый узел в наборе (c), который должен всегда быть 1 или 0 узлами.

Возможно, вы, скорее всего, пройдете только с following-sibling::dd или following-sibling::*, но вышеуказанный путь содержит меры предосторожности. Например, если по какой-то причине, вы имели следующий XML и ваш текущий узел был Tel: элемент:

<dl> 
    <dt>Tel:</dt> 
    <dt>Address:</dt> 
    <dd>50 Fake St.</dd> 
</dl> 

following-sibling::dd даст вам результат «50 Поддельный St.», в то время как following-sibling::* даст вам результат " Адрес:". Вместо этого following-sibling::*[1][self::dd] в этом случае будет выбирать пустой набор узлов, поэтому метод будет корректно создавать пустую строку в качестве результата.

+0

Gah, избили меня до удара на 5 секунд :) (И с кодом!) – paul

+0

Спасибо, он отлично работает. Что меня смутило, когда я открыл «детали» в отладчике и увидел, что «Email:» был в [0], а затем адрес электронной почты был в [1], поэтому я подумал, что NextSibling получит следующую запись. Я новичок в xpath и не понимаю, как работает ваш xpath, я попытался выработать его из ссылки, но не полностью понял его. Наверное, мне нужно получить книгу. – Guerrilla

+0

next-siblings = взять все html после этого узла * = взять любой html. [1] = ?? [self :: dd] = выбирает текущий узел и выбирает dd? код все еще работает, если я удаляю этот бит – Guerrilla

0
var html = @" 
<div id='id-here'> 
    <dl> 
    <dt> Field Name </dt> 
    <dd> Value for above field name </dd> 
    <dt> Field Name </dt> 
    <dd> Value for above field name </dd> 
    <dt> Field Name </dt> 
    <dd> Value for above field name </dd> 
    </dl> 
</div>"; 
html = new Regex(">\r\n\\s*<").Replace(html,"><"); 
var doc = new HtmlAgilityPack.HtmlDocument(); 
doc.LoadHtml(html); 
Console.Write(doc.DocumentNode.SelectNodes("//dt")[0].NextSibling.OuterHtml); 

<dd> Value for above field name </dd> 

 Смежные вопросы

  • Нет связанных вопросов^_^