当前位置:   article > 正文

使用xpath实现document.querySelector样式选择器进行html解析(四):将选择结果封装进行输出_document xpath

document xpath

使用xpath实现document.querySelector样式选择器进行html解析(一):将html转成xml

使用xpath实现document.querySelector样式选择器进行html解析(二):扩展一下xpath以便支持正则

使用xpath实现document.querySelector样式选择器进行html解析(三):实现样式选择器

使用xpath实现document.querySelector样式选择器进行html解析(四):将选择结果封装进行输出

-----------------------------------------------------------------

恩,其实到目前为止,关于xpath解析html的样式选择器其实已经完工了,而且,应该说比预期的目的还多出了一丢丢的效果

例如:QuerySelector("*[style*='(?<!\w)font-weight\s*:\s*bold(?!\w)']"),可以选中所有style属性中包含粗体定义的节点

例如:QuerySelector("*:contains(abc.*?xyz)"),可以选中所有正文中包含abc和xyz,且abc在xyz之前的节点

注意到没有,我们可以直接使用正则,当然了,第三章我贴的伪类contains处理之前的那个正则不支持圆括号输入,关于指定属性的正则不支持方括号输入,自己魔改一下就可以完整的支持正则了,或者比较麻烦,需要层深计算,但还是可以实现的,可以参考文盲老顾之前关于正则的博文,但是个人感觉没什么必要了

本章为选择器的最后一章,关于结果输出方式的,有自己代码风格的可以不看,毕竟文盲老顾也是半路出家的c#工作者,没有考虑到的,没有学习到的东西都还很多,有更好的结果输出方案的也请告知文盲,让文盲继续进步哦

-----------------------------------------------------------------

为了方便得到结果之后直接可以用.连接属性,用来直接输出结果,所以QuerySelector方法我们修改一下
  1. public HtmlObjectResult QuerySelector(string selection)
  2. {
  3. string xpath = CssParser.ParseCSS(selection);
  4. try
  5. {
  6. return new HtmlObjectResult(_xml.SelectNodes(xpath, XMLExpand.XPathExpand));
  7. }
  8. catch (Exception ex)
  9. {
  10. throw ex;
  11. }
  12. }

直接将返回的XmlNodeList封装到一个类里,作为结果实现

  1. public class HtmlObjectResult
  2. {
  3. private List<HtmlObjectNode> _result = new List<HtmlObjectNode>();
  4. private int _curr = 0;
  5. public int Count
  6. {
  7. get
  8. {
  9. return _result == null ? 0 : _result.Count;
  10. }
  11. }
  12. public HtmlObjectNode[] NodeCollection
  13. {
  14. get
  15. {
  16. return _result.ToArray();
  17. }
  18. }
  19. public HtmlObjectNode Node
  20. {
  21. get
  22. {
  23. return _result == null ? null : _result[_curr];
  24. }
  25. }
  26. public HtmlObjectResult(XmlNodeList xnl)
  27. {
  28. for (int i = 0; i < xnl.Count; i++)
  29. {
  30. _result.Add(new HtmlObjectNode(xnl[i]));
  31. }
  32. }
  33. public HtmlObjectResult Next
  34. {
  35. get
  36. {
  37. _curr += _curr < _result.Count - 1 ? 1 : 0;
  38. return this;
  39. }
  40. }
  41. public HtmlObjectResult Previous
  42. {
  43. get
  44. {
  45. _curr -= _curr > 0 ? 1 : 0;
  46. return this;
  47. }
  48. }
  49. public HtmlObjectResult First
  50. {
  51. get
  52. {
  53. _curr = 0;
  54. return this;
  55. }
  56. }
  57. }

对于结果来说,它其实是一个节点集合,所以,提供一个Count,来表示到底有多少节点被选中

一般情况下,我们都是直接使用的第一个节点作为我们的结果,所以定义一个First,如果需要其他结果,可以用Next、Previous来选择不同的结果,恩,反正都是返回这个结果集本身,只是下标定位改变了而已

然后可以返回当前选中的结果作为输出内容,也就是Node属性,Node也是一个封装后的xml节点,稍后再讲

当然,如果不喜欢这些,可以直接输出所有的结果,NodeCollection可以满足你的需要,当然其中的元素也是被封装好的结果节点

再然后是正式输出我们期望的结果值了

  1. public class HtmlObjectNode
  2. {
  3. private XmlNode _node = null;
  4. public HtmlObjectNode(XmlNode node)
  5. {
  6. _node = node;
  7. }
  8. public HtmlObjectNode Next
  9. {
  10. get
  11. {
  12. return _node == null ? null : _node.NextSibling != null ? new HtmlObjectNode(_node.NextSibling) : this;
  13. }
  14. }
  15. public HtmlObjectNode Previous
  16. {
  17. get
  18. {
  19. return _node == null ? null : _node.PreviousSibling != null ? new HtmlObjectNode(_node.PreviousSibling) : this;
  20. }
  21. }
  22. public HtmlObjectNode Parent
  23. {
  24. get
  25. {
  26. return _node == null ? null : _node.ParentNode != null ? new HtmlObjectNode(_node.ParentNode) : this;
  27. }
  28. }
  29. public string InnerHtml
  30. {
  31. get
  32. {
  33. return _node == null ? null : Regex.Replace(_node.InnerXml, @"<!\[CDATA\[|\]\]>", "", RegexOptions.IgnoreCase).Trim();
  34. }
  35. }
  36. public string OuterHtml
  37. {
  38. get
  39. {
  40. return _node == null ? null : Regex.Replace(_node.OuterXml, @"<!\[CDATA\[|\]\]>", "", RegexOptions.IgnoreCase).Trim();
  41. }
  42. }
  43. public string InnerText
  44. {
  45. get
  46. {
  47. return _node == null ? null : _node.InnerText;
  48. }
  49. }
  50. public XmlNode Node
  51. {
  52. get
  53. {
  54. return _node;
  55. }
  56. }
  57. }

作为被选中的节点,有时候我们需要纯文本内容,有时候需要html内容,html内容有时候需要包含节点本身,有时候不包含

所以,我们的结果输出就直接定义成三个,分别是InnerText、InnerHtml、OuterHtml,这个也符合html本身的习惯

由于我在第一章的时候,将html转成xml的时候还追加了不少的CDataSetion节点,这些节点在作为结果输出的时候应该被删除节点声明,所以我在这里用正则删除了一些信息

当然,有时候某些节点定位非常麻烦,可他相邻的部分节点非常好定位,那么我们通常会定位到我们希望选中的节点之前,例如有一个表格,很多行很多列,没有样式啦、ID啦,甚至数据的位置也可能改变,但表格的格式不变,比如每一个td声明数据名称后,下一个紧跟着的td必定是它对应的值的时候,我们就可以直接定位到这个数据名称的位置,例如 QuerySelector("td:contains(姓名)"),然后使用Next向后移动一个节点,再输出就是对应的值了:QuerySelector("td:contains(姓名)").First.Node.Next.InnerText

需要注意的是,Node之前的Next是在结果集中选择下一个对应的结果,Node之后的Next是在Html中对应的元素的下一个元素

好了,HtmlParser部分基本上讲完了,之后文盲老顾会尝试做一些数据提取方面新的尝试,在尽量减少指正的情况下,如何从页面内获取到我们想要的数据,例如自动解析表格之类的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/839423
推荐阅读
相关标签
  

闽ICP备14008679号