HtmlAgilityPack中經過sibling才能獲得對應的InnerText和form,option等tag的子節點

【背景】css

以前使用HtmlAgilityPack期間,遇到了2個bug:html

1. InnerText沒有包含對應字符串(可是用NextSibling.InnerText卻能夠獲得)node

對於html:session

?
1
<option value="search-alias=instant-video">Amazon Instant Video</option>

用以下的代碼:ide

?
1
2
3
4
//<option value="search-alias=instant-video">Amazon Instant Video</option>
string searchValue = singleOptionNode.Attributes["value"].Value; //search-alias=instant-video
//instant-video
string generalCategory = singleOptionNode.InnerText; //CAN NOT get: Amazon Instant Video

是不工做的。post

後來通過調試,改成:this

?
1
2
3
4
//<option value="search-alias=instant-video">Amazon Instant Video</option>
string searchValue = singleOptionNode.Attributes["value"].Value; //search-alias=instant-video
//instant-video
string generalCategory = singleOptionNode.NextSibling.InnerText; //can get: Amazon Instant Video

倒是能夠的。google

非常尼瑪的詭異。spa

很明顯是一個bug。調試

 

和:

2.丟失了form節點的input子節點

訪問:

http://www.amazon.com/gp/offer-listing/B0083PWAPW/ref=dp_olp_all_mbc?ie=UTF8&condition=all

獲得的html中,對應的部分是:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<form method= "POST"  action= "/gp/item-dispatch/ref=olp_atc_fm_1"  >
     <input type= "hidden"  name= "session-id"  value= "182-0726239-4848949" >
     <input type= "hidden"  name= "qid"  value= "" >
     <input type= "hidden"  name= "sr"  value= "" >
     <input id= "signInToHUC"  type= "hidden"  value= "0"  name= "signInToHUC" >
     <input type= "hidden"  name= "metric-asin.B0083PWAPW"  value= "1" >
     <input type= "hidden"  name= "registryItemID.1"  value= "" >
     <input type= "hidden"  name= "registryID.1"  value= "" >
     <input type= "hidden"  name= "itemCount"  value= "1" >
     <input type= "hidden"  name= "offeringID.1" value= "%2F%2FeHHmpktM3oPoQj%2FOWhDI%2FpHyvwwFCwEfNIBEgFcfAHzKHAzVK%2BZfhkmBFO%2BPbow9JfdOmrE6eKME4ydhLTTK1Dgaf8O3N7SyOR%2F136TvVh0lfJypEt4Q%3D%3D" >
     <input type= "hidden"  name= "isAddon"  value= "0" >
     <input type= "image"  src= "http://g-ecx.images-amazon.com/images/G/01/x-locale/nav2/images/add-to-cart-md-p._V192250398_.gif"  align= "absmiddle" alt= "Add to cart"  border= "0"  height= "21"  name= "submit.addToCart"  width= "112" />
</form>

能夠經過:

?
1
2
htmlDoc = crl.htmlToHtmlDoc(respHtml);
HtmlNodeCollection postItemNodeList = htmlDoc.DocumentNode.SelectNodes( "//form[starts-with(@action, '/gp/item-dispatch/ref=') and @method='POST']" );

搜索到form節點,可是結果其下,再去搜input節點:

?
1
HtmlNodeCollection inputTypeNodeList = postItemNode.SelectNodes( ".//input[@type='hidden' and @name and @value]" );

居然獲得的inputTypeNodeList是null:

即form下面,沒有找到任何的child,即,全部的input節點,都丟失了!

再回去查看postItemNode,結果其下就是沒有child的:

 

因此,應該是對應的HtmlAgilityPack的bug。

 

【折騰過程】

1. 後來看到:

No child nodes for FORM object

中提到了,說是:

In Html specification form tag can overlap, so Htmlagilitypack handle this node a little different.  
。。。

 

After adding this call all form elements are added as children.

而後就去看看,結果果真是從child變成了sibling了,並且此處仍是很變態的,NextSibling的NextSibling纔是咱們要的input節點:

因此,此處,看來只能是說動的,相似於上面那個問題同樣的,寫成NextSibling的NextSibling

不過,真是這樣寫的話,那也夠變態的。。。。

2.而後也看到別人也遇到一樣問題:

Problem parsing children of a node with HtmlAgilityPack

並且某人也是放棄了HtmlAgilityPack而轉到了SGMLReader了。

不過,另外有人說,不是bug,而是能夠配置的。

其相關的討論見:

http://htmlagilitypack.codeplex.com/workitem/21782

再參考:

HtmlAgilityPack — Does <form> close itself for some reason?

去,在將html轉爲htmlDoc以前,添加:

?
1
HtmlNode.ElementsFlags.Remove( "form" );

變爲:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
HtmlNode.ElementsFlags.Remove( "form" );
 
htmlDoc = crl.htmlToHtmlDoc(respHtml);
HtmlNodeCollection postItemNodeList = htmlDoc.DocumentNode.SelectNodes( "//form[starts-with(@action, '/gp/item-dispatch/ref=') and @method='POST']" );
if  (postItemNodeList ==  null )
{
     //something error
}
else
{
     foreach  (HtmlNode postItemNode  in  postItemNodeList)
     {
         string  itemDispatchUrl = postItemNode.Attributes[ "action" ].Value;  ///gp/item-dispatch/ref=olp_atc_used_1
         itemDispatchUrl = constAmazonDomainUrl + itemDispatchUrl; //http://www.amazon.com/gp/item-dispatch/ref=olp_atc_used_1
 
         Dictionary< string string > postDict =  new  Dictionary< string string >();
 
         HtmlNodeCollection inputTypeNodeList = postItemNode.SelectNodes( ".//input[@type='hidden' and @name and @value]" );

而後獲得的inputTypeNodeList,的確不是null了,也有了child了:

 

【總結】

以前還誇獎HtmlAgilityPack好用呢,結果還沒用多久,就出現這麼多的bug。看來真的無法繼續使用了。

每次都要很當心,不知道啥時候就會出錯,真鬱悶。。。

即便不是bug,其自己把form下面的節點,都弄成其sibling這個策略,仍是很變態的。至少讓更多人的,都容易誤解。


【後記】

後來的後來,通過參考別人的解釋:

<option> have no child, why?

發現,

其實上述兩個,所謂的bug,就是同一個問題:

對於HtmlAgilityPack,實際上,對於option,form等tag,其默認的處理的結果是:其下的子節點,會變成sibling

因此,上面的:

對於option須要經過NextSibling才能得到對應的InnerText;

對於form子節點爲空,也是須要經過NextSibling(的NextSibling)才能得到對應的input子節點;

其本質都是:

HtmlAgilityPack是針對HTML 3.2的規範去實現的,而HTML 3.2就是這樣規定的。

其不是bug,而是feature

可是很明顯,是屬於讓人蛋疼的feature。

解決辦法有兩種:

1.改源碼

把HtmlNode.cs中的下面這行註釋掉:

?
1
ElementsFlags.Add( "form" , HtmlElementFlag.CanOverlap | HtmlElementFlag.Empty);

2.不改源碼

在HtmlDocument類型的變量執行LoadHtml以前,加上:

?
1
HtmlNode.ElementsFlags.Remove( "tagName" );

即,對於我以前的crifanlib.cs中的:

?
1
2
3
4
5
6
7
8
public  HtmlAgilityPack.HtmlDocument htmlToHtmlDoc( string  html)
{
     HtmlAgilityPack.HtmlDocument htmlDoc =  new HtmlAgilityPack.HtmlDocument();
 
     htmlDoc.LoadHtml(html);
 
     return  htmlDoc;
}

換成:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public  HtmlAgilityPack.HtmlDocument htmlToHtmlDoc( string  html)
{
     HtmlAgilityPack.HtmlDocument htmlDoc =  new  HtmlAgilityPack.HtmlDocument();
 
     //make some html tag: form/option, has child
     HtmlNode.ElementsFlags.Remove( "form" );
     HtmlNode.ElementsFlags.Remove( "option" );
 
     htmlDoc.LoadHtml(html);
 
     return  htmlDoc;
}

便可。

如此,後續解析html獲得的form,option等tag,其child就是咱們所但願的內容了。

 

另外:

1.對因而否還有其餘特殊的html的tag,也是默認被處理爲子節點變爲sibling的,就不知道了。

2.等有空再去深究背後的那個HTML 3.2規範是怎麼定義的。

相關文章
相關標籤/搜索