2014-08-27 8 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 노드과 같이 잡아 :

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

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

지금 나는 그것이 내가 원하는 필드와 일치하는지 등처럼로 nextSibling 값을 각 노드를 확인

전화에 문제가 없지만 "Email :"노드가 나타나면 어떤 이유에서든지 NextSibling.InnerHTML & NextSibling.InnerText은 모두 다음 형제에게는 확실히 데이터가 있지만 비어 있습니다. 실제로 내가 에 가서 그걸 보면 InnerHTML은 전체 형식화 된 링크이고 InnerText은 이메일 주소입니다.

NextSibling.InnerText이 A 태그로 인해 작동하지 않습니까? 디버거에서 한 번 보았고 NextSibling에서 필요한 정보를 찾을 수 없습니다.

나는 대답이 어리석게 간단하다라고 생각한다. 누구든지 나를 비참하게 만들었지? 이런 일

+0

'details'를 반복하면서 선택한'dd'를 실제로 사용하지 않으려면'dd' 요소를 선택하는 이유는 무엇입니까? – JLRishe

+0

nextSibling을 선택할 수 있도록. DD를 선택하지 않으면 nextSibling이 존재하지 않을 것입니다. – Guerrilla

+0

'details '의 내용은 노드의 형제가 무엇인지에 관계가 없으며 정확히 여기에서 목격하고있는 내용입니다. – JLRishe

답변

8

이유 node 일부 공백하여 대응 dd 소자로부터 분리 된 dt 소자이면 다음 node.NextSibling이 모두 공백 텍스트 노드합니다 (</dt><dd> 사이의 공간)이 있다는 것이다. 디버거에서 살펴보면 node.NextSiblingNodeTypeHtmlNodeType.Text이 아니라 HtmlNodeType.Element이 아님을 알 수 있습니다.

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

그런 다음이처럼 사용할 수 있습니다 :

가 나는 dt 노드의이 dd 대응의 텍스트 얻을 수있는 편리한 방법을 만드는 것이 좋습니다 여기

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

은의 고장입니다 위 메서드에서 사용 된 다소 까다로운 XPath :

(a) following-sibling::* 

^현재 부모를 공유하고 이후에 발생하는 모든 요소를 ​​선택하십시오.

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

^세트의 첫 번째 노드를 선택 (a)

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

^세트 내의 모든 노드를 선택하여 (하나가 존재하는 경우) (b) 는 이름 "DD"인 요소임을

SelectSingleNode()은 세트 (c)의 첫 번째 노드를 선택합니다.이 노드는 항상 1 또는 0 노드 여야합니다.

그냥 following-sibling::dd 또는 following-sibling::* 일 가능성이 높지만 위 경로에는 안전 장치가 포함되어 있습니다. "following-sibling::* 당신에게 결과를 줄 것이라고하면서,

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

following-sibling::dd는 당신에게 결과"50 가짜 성 "을 줄 것이다 : 예를 들어, 어떤 이유로, 당신은 다음과 같은 XML을했고 현재 노드는 Tel: 요소였다 주소:". 대신 following-sibling::*[1][self::dd]은이 경우 빈 노드 집합을 선택하므로 메서드는 빈 문자열을 결과로 올바르게 생성합니다.

+0

가, 펀치에 5 초 정도 맞 춥니 다 :) (코드로!) – paul

+0

고마워, 완벽하게 작동합니다. 나에게 혼란 스러웠던 것은 디버거에서 'details'를 열었을 때 "Email :"이 [0]이고 이메일 주소가 [1]이라고되어서 NextSibling이 다음 항목을 가져올 것이라고 생각하는 것을 보았습니다. 나는 xpath에 익숙하지 않고 xpath가 어떻게 작동하는지 완전히 이해하지 못한다. 나는 그것을 참고에서부터 풀려고했지만 완전히 이해하지는 못했다. 나는 그것에 관한 책을 얻을 필요가 있다고 생각한다. – Guerrilla

+0

다음 형제 =이 노드 뒤의 모든 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>