HtmlAgilityPack中通过sibling才能得到对应的InnerText和form,option等tag的子节点
【背景】
之前使用HtmlAgilityPack期间,遇到了2个bug:
1. InnerText没有包含对应字符串(但是用NextSibling.InnerText却可以得到)
对于html:
|
1
|
<option value="search-alias=instant-video">Amazon Instant Video</option> |
用如下的代码:
|
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-videostring generalCategory = singleOptionNode.InnerText; //CAN NOT get: Amazon Instant Video |
是不工作的。
后来经过调试,改为:
|
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-videostring 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中,对应的部分是:
|
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这个策略,还是很变态的。至少让更多人的,都容易误解。
【后记】
后来的后来,经过参考别人的解释:
发现,
其实上述两个,所谓的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 = newHtmlAgilityPack.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规范是怎么定义的。
HtmlAgilityPack中通过sibling才能得到对应的InnerText和form,option等tag的子节点的更多相关文章
- 解决HtmlAgilityPack无法获取form标签子节点的问题
问题描述 今天使用HtmlAgilityPack提取Form表单下的input节点,发现提取的form节点没有子节点,InnerHtml也是为空,起初以为是标签不全导致,后来分析html代码发现不可能 ...
- 仅Firefox中A元素包含Select时点击Select不能选择option
这是在使用京东的一个日期组件时碰到的bug,重现bug的代码精简如下 <!DOCTYPE HTML> <html> <head> <title> 仅Fi ...
- C#中treeview的问题,如何区分根节点和子节点以及根节点和根节点的兄弟节点?
根节点的Level属性为0,一级子节点Level属性为1,二级子节点Level属性为2,以此类推:同级节点可以用索引.名称.文本来区分.用索引区分根节点时,TreeView.Nodes[0]就是第一个 ...
- 关于 Unity UGUI 中修改 Mask 组件下 Image 等子节点组件的材质无效的问题
前几天同事做了一个效果,希望在原本使用了遮罩组件 Mask 的技能图标(让技能图标变成圆形)上在添加一个置灰的功能,但问题来了:因为是动态根据游戏中玩家的条件才动态置灰,以修改 Mask 下子节点 I ...
- zTree中父节点禁用,子节点可以用
参考学习网址:http://www.treejs.cn/v3/main.php#_zTreeInfo zTree中父节点禁用,子节点可以用 axios.get('/base/unit/unittree ...
- Web网页树形列表中实现选中父节点则子节点全选和不选中父则子全不选
需要实现的功能:选中父节点对应子节点全选:不选中父节点,对应子节点也不选中 如下图所示,选中车队,对应车队中车辆也全部选中,以实现车队中所有车辆在地图上的显示. 选中cqupt ...
- SQL 用;with 由所有的子节点查询到树结构中所有父节点
1.所有的子节点查询到树结构中所有父节点 RETURNS @Tree Table(PID )) as begin --DECLARE @ID VARCHAR() --SET @ID = ' ;with ...
- 问题:jQuery中遍历XML文件时候,获取子节点children不支持的情况(已解决)
问题描述: 今天在写一个基于 jquery 的读取xml文件的程序时候,需要遍历xml的节点. 代码片段如下: function parse_xml_node(parent,result){ // r ...
- js中创建html标签、加入select下默认的option的value和text、删除select元素节点下全部的OPTION节点
<pre name="code" class="java"> jsp 中的下拉框标签: <s:select name="sjx&qu ...
随机推荐
- IP首部校验和计算
根据RFC1071文档的计算方法,编写代码实现IP首部校验和的计算 计算步骤: 1.首先将IP首部中校验和字段置0 2.将IP首部每16bit进行相加,如果有进位产生,则将进位加到最低位. 3.将计算 ...
- 自定义指令directive
1.自定义指令 在angular中,module下面的directive方法用于创建自定义指令,用法: m1.directive('myTab',function(){ return { restri ...
- android studio怎么分享项目到Git@OSC托管
鄙人初次发表,如有不妥之处,敬请批评指正 1,安装git. git下载地址:http://git-scm.com/downloads/ 2,在AS 的File->Settings->Ver ...
- 第三次作业——《K米评测》
第三次作业--<K米评测> 一.调研.评测 上手体验 APP的图标做的不错,一眼就知道和KTV唱歌相关的 点进去就是连接包箱的界面和直播界面,把软件最重要的两个功能展示出来了,一目了然 热 ...
- [07]APUE:进程环境
[a] exit / _Exit / _exit #include <stdlib.h> void exit(int status) void _Exit(int status) #inc ...
- 解决Tomcat数据连接池无法释放
近段时间,公司的检测中心报表系统(SMC)的开发人员时不时找到我,说用户老是出现无法登录的情况.前些日子因为手头上 有Jboss集群的测试工作,发现用户不能登录时,都是在Tomcat中将这个项目Rel ...
- android nio
Android开发进阶之NIO非阻塞包(二) 有关Android NIO我们主要分为三大类,ByteBuffer.FileChannel和SocketChannel.由于篇幅原因今天Android12 ...
- 【Git】简单地使用github当做远程共享仓库
简单地使用github当做远程共享仓库 1.进入各自的github,选取一个人的github作为总的远程共享仓库,其余成员每次修改完项目后pull request请求合并自己的修改内容. 2.其余开发 ...
- Android Service完全解析,关于服务你所需知道的一切(下)
转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要 ...
- 大数据通过PHP快速插入MYSQL的方法
如果您的mysql是通过brew安装的,那么请 vi /usr/local/Cellar/mysql/5.6.23/my.cnf 将 max_allowed_packet = 64M 写入保存并重启m ...