HtmlAgilityPack中通过sibling才能得到对应的InnerText和form,option等tag的子节点
来源:互联网 发布:淘宝网小狗用品 编辑:程序博客网 时间:2024/06/05 18:00
转自:http://www.crifan.com/htmlagilitypack_html_tag_form_option_no_child_via_sibling_get_innertext/
最近在用HtmlAgilityPack解析HTML文件,用到的很奇怪的问题,这篇文章给了很详细解释和标准答案,收藏一下!
【背景】
之前使用HtmlAgilityPack期间,遇到了2个bug:
1. InnerText没有包含对应字符串(但是用NextSibling.InnerText却可以得到)
对于html:
<option value="search-alias=instant-video">Amazon Instant Video</option>
用如下的代码:
//<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
是不工作的。
后来经过调试,改为:
//<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
却是可以的。
很是尼玛的诡异。
很明显是一个bug。
和:
2.丢失了form节点的input子节点
访问:
http://www.amazon.com/gp/offer-listing/B0083PWAPW/ref=dp_olp_all_mbc?ie=UTF8&condition=all
得到的html中,对应的部分是:
<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>
可以通过:
htmlDoc = crl.htmlToHtmlDoc(respHtml);
HtmlNodeCollection postItemNodeList = htmlDoc.DocumentNode.SelectNodes(
"//form[starts-with(@action, '/gp/item-dispatch/ref=') and @method='POST']"
);
搜索到form节点,但是结果其下,再去搜input节点:
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之前,添加:
HtmlNode.ElementsFlags.Remove(
"form"
);
变为:
//http://www.crifan.com/htmlagilitypack_html_tag_form_option_no_child_via_sibling_get_innertext/
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)
{
//http://www.amazon.com/gp/item-dispatch/ref=olp_atc_used_1
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中的下面这行注释掉:
ElementsFlags.Add(
"form"
, HtmlElementFlag.CanOverlap | HtmlElementFlag.Empty);
2.不改源码
在HtmlDocument类型的变量执行LoadHtml之前,加上:
HtmlNode.ElementsFlags.Remove(
"tagName"
);
即,对于我之前的crifanlib.cs中的:
public
HtmlAgilityPack.HtmlDocument htmlToHtmlDoc(
string
html)
{
HtmlAgilityPack.HtmlDocument htmlDoc =
new
HtmlAgilityPack.HtmlDocument();
htmlDoc.LoadHtml(html);
return
htmlDoc;
}
换成:
public
HtmlAgilityPack.HtmlDocument htmlToHtmlDoc(
string
html)
{
HtmlAgilityPack.HtmlDocument htmlDoc =
new
HtmlAgilityPack.HtmlDocument();
//http://www.crifan.com/htmlagilitypack_html_tag_form_option_no_child_via_sibling_get_innertext/
//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就是我们所希望的内容了。
- HtmlAgilityPack中通过sibling才能得到对应的InnerText和form,option等tag的子节点
- 清除HtmlAgilityPack得到的InnerText中残留的script
- jquery 通过find(“tag”) 得到标签的子标签
- 通过innerText获取li的值,不再先得到li对应的index
- HtmlAgilityPack不能解析option标签的解决方法
- QEMU中通过GPA得到对应HVA的方法
- JXLS的使用中,某单个单元格的数据需要通过foreach和if才能得到,使用jxls如何使用
- squid中cache_peer的sibling用法
- 通过select的text来选中对应的option
- 一条SQL语句得到树的父节点和子节点
- 通过手势,要拿到对应view的tag
- iOS通过tag值找不到对应的控件
- JavaScript中innerText和innerHTML的区别
- XmlNode中Value和InnerText的区别
- Javascript 中innerHTML和innerText的区别
- XmlNode中Value和InnerText的区别
- XmlNode中Value和InnerText的区别
- JavaScript中innerText和innerHTML的区别
- parted创建硬盘分区并创建LVM
- js 时间格式与时间戳的相互转换示例代码
- 动态规划入门之01背包
- linux下db4.x-util的安装问题,终于成功了(Ubuntu)
- Android 针对ListActivity中ListView 点击事件和长按事件
- HtmlAgilityPack中通过sibling才能得到对应的InnerText和form,option等tag的子节点
- iOS 删除文件夹下所有文件的方式
- linux防火墙最好这么写
- 最优化方法--概述
- 20110921 onItemClick监听器四个arg参数
- 使用手势缩放文本
- 在线调用QQ
- 路由经典解释
- 사람의 生生 (韓長庚 易學原理總論)