功能:

1、轻松获取指元素HTML元素。

2、可以根据属性标签进行筛选

3、返回的都是Llist强类型无需转换

用过XElement的都知道 用来解析XML非常的方便,但是对于HTML的格式多样化实在是没办法兼容。

所以我就写了这么一个类似XElement的 XHTMLElement

用法:

  1. string filePath = Server.MapPath("~/file/test.htm");
  2. //获取HTML代码
  3. string mailBody = FileHelper.FileToString(filePath);
  4.  
  5. XHtmlElement xh = new XHtmlElement(mailBody);
  6.  
  7. //获取body的子集a标签并且class="icon"
  8. var link = xh.Descendants("body").ChildDescendants("a").Where(c => c.Attributes.Any(a => a.Key == "class" && a.Value == "icon")).ToList();
  9.  
  10. //获取带href的a元素
  11. var links = xh.Descendants("a").Where(c => c.Attributes.Any(a => a.Key == "href")).ToList();
  12. foreach (var r in links)
  13. {
  14. Response.Write(r.Attributes.Single(c => c.Key == "href").Value); //出输href
  15. }
  16.  
  17. //获取第一个img
  18. var img = xh.Descendants("img");
  19.  
  20. //获取最近的第一个p元素以及与他同一级的其它p元素
  21. var ps = xh.Descendants("p");

代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7.  
  8. namespace SyntacticSugar
  9. {
  10. /// <summary>
  11. /// ** 描述:html解析类
  12. /// ** 创始时间:2015-4-23
  13. /// ** 修改时间:-
  14. /// ** 作者:sunkaixuan
  15. /// ** qq:610262374 欢迎交流,共同提高 ,命名语法等写的不好的地方欢迎大家的给出宝贵建议
  16. /// </summary>
  17. public class XHtmlElement
  18. {
  19. private string _html;
  20. public XHtmlElement(string html)
  21. {
  22. _html = html;
  23. }
  24.  
  25. /// <summary>
  26. /// 获取最近的相同层级的HTML元素
  27. /// </summary>
  28. /// <param name="elementName">等于null为所有元素</param>
  29. /// <returns></returns>
  30. public List<HtmlInfo> Descendants(string elementName = null)
  31. {
  32. if (_html == null)
  33. {
  34. throw new ArgumentNullException("html不能这空!");
  35. }
  36. var allList = RootDescendants(_html);
  37. var reval = allList.Where(c => elementName == null || c.TagName.ToLower() == elementName.ToLower()).ToList();
  38. if (reval == null || reval.Count == )
  39. {
  40. reval = GetDescendantsSource(allList, elementName);
  41. }
  42. return reval;
  43. }
  44.  
  45. /// <summary>
  46. /// 获取第一级元素
  47. /// </summary>
  48. /// <param name="elementName"></param>
  49. /// <returns></returns>
  50. public List<HtmlInfo> RootDescendants(string html = null)
  51. {
  52. /*
  53. * 业务逻辑:
  54. * 1、获取第一个html标签一直找结尾标签,如果在这个过程中遇到相同的标签收尾标签就要加1
  55. * 2、第一个标签取到后继续第一步操作,找第2个元素 。。第N个元素
  56. */
  57. if (html == null) html = _html;
  58. var firstTag = Regex.Match(html, "<.+?>");
  59.  
  60. List<string> eleList = new List<string>();
  61. List<HtmlInfo> reval = new List<HtmlInfo>();
  62. GetElementsStringList(html, ref eleList);
  63. foreach (var r in eleList)
  64. {
  65. HtmlInfo data = new HtmlInfo();
  66. data.OldFullHtml = r;
  67. data.SameLeveHtml = html;
  68. data.TagName = Regex.Match(r, @"(?<=\s{1}|\<)[a-z,A-Z]+(?=\>|\s)", RegexOptions.IgnoreCase).Value;
  69. data.InnerHtml = Regex.Match(r, @"(?<=\>).+(?=<)", RegexOptions.Singleline).Value;
  70. var eleBegin = Regex.Match(r, "<.+?>").Value;
  71. var attrList = Regex.Matches(eleBegin, @"[a-z,A-Z]+\="".+?""").Cast<Match>().Select(c => new { key = c.Value.Split('=').First(), value = c.Value.Split('=').Last().TrimEnd('"').TrimStart('"') }).ToList();
  72. data.Attributes = new Dictionary<string, string>();
  73. if (attrList != null && attrList.Count > )
  74. {
  75. foreach (var a in attrList)
  76. {
  77. data.Attributes.Add(a.key, a.value);
  78. }
  79. }
  80. reval.Add(data);
  81. }
  82. return reval;
  83.  
  84. }
  85.  
  86. #region private
  87. private List<HtmlInfo> GetDescendantsSource(List<HtmlInfo> allList, string elementName)
  88. {
  89. foreach (var r in allList)
  90. {
  91. if (r.InnerHtml == null || !r.InnerHtml.Contains("<")) continue;
  92. var childList = RootDescendants(r.InnerHtml).Where(c => elementName == null || c.TagName.ToLower() == elementName.ToLower()).ToList();
  93. if (childList == null || childList.Count == )
  94. {
  95. childList = GetDescendantsSource(RootDescendants(r.InnerHtml), elementName);
  96. if (childList != null && childList.Count > )
  97. return childList;
  98. }
  99. else
  100. {
  101. return childList;
  102. }
  103. }
  104. return null;
  105. }
  106.  
  107. private void GetElementsStringList(string html, ref List<string> eleList)
  108. {
  109. HtmlInfo info = new HtmlInfo();
  110. info.TagName = Regex.Match(html, @"(?<=\<\s{0,5}|\<)([a-z,A-Z]+|h\d{1})(?=\>|\s)", RegexOptions.IgnoreCase).Value;
  111. string currentTagBeginReg = @"<\s{0,10}" + info.TagName + @".*?>";//获取当前标签元素开始标签正则
  112. string currentTagEndReg = @"\<\/" + info.TagName + @"\>";//获取当前标签元素收尾标签正则
  113. if (string.IsNullOrEmpty(info.TagName)) return;
  114.  
  115. string eleHtml = "";
  116. //情况1 <a/>
  117. //情况2 <a></a>
  118. //情况3 <a> 错误格式
  119. //情况4endif
  120. if (Regex.IsMatch(html, @"<\s{0,10}" + info.TagName + "[^<].*?/>"))//单标签
  121. {
  122. eleHtml = Regex.Match(html, @"<\s{0,10}" + info.TagName + "[^<].*?/>").Value;
  123. }
  124. else if (!Regex.IsMatch(html, currentTagEndReg))//没有收尾
  125. {
  126. if (Regex.IsMatch(html, @"\s{0,10}\<\!\-\-\[if"))
  127. {
  128. eleHtml = GetElementString(html, @"\s{0,10}\<\!\-\-\[if", @"\[endif\]\-\-\>", );
  129. }
  130. else
  131. {
  132. eleHtml = Regex.Match(html, currentTagBeginReg,RegexOptions.Singleline).Value;
  133. }
  134. }
  135. else
  136. {
  137. eleHtml = GetElementString(html, currentTagBeginReg, currentTagEndReg, );
  138. }
  139.  
  140. try
  141. {
  142. eleList.Add(eleHtml);
  143. html = html.Replace(eleHtml, "");
  144. html = Regex.Replace(html, @"<\!DOCTYPE.*?>", "");
  145. if (!Regex.IsMatch(html, @"^\s*$"))
  146. {
  147. GetElementsStringList(html, ref eleList);
  148. }
  149.  
  150. }
  151. catch (Exception ex)
  152. {
  153. throw new Exception("SORRY,您的HTML格式不能解析!!!");
  154.  
  155. }
  156.  
  157. }
  158.  
  159. private string GetElementString(string html, string currentTagBeginReg, string currentTagEndReg, int i)
  160. {
  161.  
  162. string newHtml = GetRegNextByNum(html, currentTagBeginReg, currentTagEndReg, i);
  163. var currentTagBeginMatches = Regex.Matches(newHtml, currentTagBeginReg, RegexOptions.Singleline).Cast<Match>().Select(c => c.Value).ToList();
  164. var currentTagEndMatches = Regex.Matches(newHtml, currentTagEndReg).Cast<Match>().Select(c => c.Value).ToList();
  165. if (currentTagBeginMatches.Count == currentTagEndMatches.Count)
  166. { //两个签标元素相等
  167. return newHtml;
  168. }
  169. return GetElementString(html, currentTagBeginReg, currentTagEndReg, ++i);
  170. }
  171.  
  172. private string GetRegNextByNum(string val, string currentTagBeginReg, string currentTagEndReg, int i)
  173. {
  174. return Regex.Match(val, currentTagBeginReg + @"((.*?)" + currentTagEndReg + "){" + i + "}?", RegexOptions.IgnoreCase | RegexOptions.Singleline).Value;
  175. }
  176. #endregion
  177.  
  178. }
  179. public static class XHtmlElementExtendsion
  180. {
  181. /// <summary>
  182. /// 获取最近的相同层级的HTML元素
  183. /// </summary>
  184. /// <param name="elementName">等于null为所有元素</param>
  185. /// <returns></returns>
  186. public static List<HtmlInfo> Descendants(this IEnumerable<HtmlInfo> htmlInfoList, string elementName = null)
  187. {
  188. var html = htmlInfoList.First().InnerHtml;
  189. XHtmlElement xhe = new XHtmlElement(html);
  190. return xhe.Descendants(elementName);
  191. }
  192. /// <summary>
  193. /// 获取下级元素
  194. /// </summary>
  195. /// <param name="elementName"></param>
  196. /// <returns></returns>
  197. public static List<HtmlInfo> ChildDescendants(this IEnumerable<HtmlInfo> htmlInfoList, string elementName = null)
  198. {
  199. var html = htmlInfoList.First().InnerHtml;
  200. XHtmlElement xhe = new XHtmlElement(html);
  201. return xhe.RootDescendants(html).Where(c => elementName == null || c.TagName == elementName).ToList();
  202. }
  203.  
  204. /// <summary>
  205. /// 获取父级
  206. /// </summary>
  207. /// <param name="htmlInfoList"></param>
  208. /// <returns></returns>
  209. public static List<HtmlInfo> ParentDescendant(this IEnumerable<HtmlInfo> htmlInfoList,string fullHtml)
  210. {
  211. var saveLeveHtml = htmlInfoList.First().SameLeveHtml;
  212. string replaceGuid=Guid.NewGuid().ToString();
  213. fullHtml = fullHtml.Replace(saveLeveHtml,replaceGuid);
  214. var parentHtml = Regex.Match(fullHtml, @"<[^<]+?>[^<]*?" + replaceGuid + @".*?<\/.+?>").Value;
  215. parentHtml = parentHtml.Replace(replaceGuid, saveLeveHtml);
  216. XHtmlElement xhe = new XHtmlElement(parentHtml);
  217. return xhe.RootDescendants();
  218. }
  219. }
  220. /// <summary>
  221. /// html信息类
  222. /// </summary>
  223. public class HtmlInfo
  224. {
  225. /// <summary>
  226. /// 元素名
  227. /// </summary>
  228. public string TagName { get; set; }
  229. /// <summary>
  230. /// 元素属性
  231. /// </summary>
  232. public Dictionary<string, string> Attributes { get; set; }
  233. /// <summary>
  234. /// 元素内部html
  235. /// </summary>
  236. public string InnerHtml { get; set; }
  237.  
  238. public string OldFullHtml { get; set; }
  239.  
  240. public string SameLeveHtml { get; set; }
  241.  
  242. /// <summary>
  243. /// 得到元素的html
  244. /// </summary>
  245. /// <returns></returns>
  246. public string FullHtml
  247. {
  248. get
  249. {
  250. StringBuilder reval = new StringBuilder();
  251. string attributesString = string.Empty;
  252. if (Attributes != null && Attributes.Count > )
  253. {
  254. attributesString = string.Join(" ", Attributes.Select(c => string.Format("{0}=\"{1}\"", c.Key, c.Value)));
  255. }
  256. reval.AppendFormat("<{0} {2}>{1}</{0}>", TagName, InnerHtml, attributesString);
  257. return reval.ToString();
  258. }
  259. }
  260. }
  261. }

前台HTML:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title></title>
  5. </head>
  6. <body>
  7. <a id="1">我是1</a>
  8. <a id="2" class="icon">icon</a>
  9. <img />
  10. </body>
  11. </html>

HTML解析类 ,让你不使用正则也能轻松获取HTML相关元素 -C# .NET的更多相关文章

  1. 自己用的框架写了一个PHP模版解析类

    <?php if(!defined('IS_HEARTPHP')) exit('Access Denied'); /** * template.class.php 模板解析类 * * @copy ...

  2. PHP模板解析类实例

    作者:mckee 这篇文章主要介绍了PHP模板解析类,涉及php针对模板文件的解析与字符串处理的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下 <?php class template { ...

  3. 黄聪:C#类似Jquery的html解析类HtmlAgilityPack基础类介绍及运用

    Html Agility Pack下载地址:http://htmlagilitypack.codeplex.com/ Html Agility Pack 源码中的类大概有28个左右,其实不算一个很复杂 ...

  4. 【转】C#类似Jquery的html解析类HtmlAgilityPack基础类介绍及运用

    Html Agility Pack下载地址:http://htmlagilitypack.codeplex.com/ Html Agility Pack 源码中的类大概有28个左右,其实不算一个很复杂 ...

  5. IniParse解析类

    说明 iniParse这个类是一个解析ini文件的类,他的功能和Windows下GetPrivateProfileString的功能一样,可以很方便的保存读取配置. 当然他不是只有GetPrivate ...

  6. 深入源码解析类Route

    微软官网对这个类的说明是:提供用于定义路由及获取路由相关信息的属性和方法.这个说明已经很简要的说明了这个类的作用,下面我们就从源码的角度来看看这个类的内部是如何工作的. public class Ro ...

  7. Json解析类

      Json解析类 定义两个辅助类 public class JSONObject : Dictionary<string, object> { } public class JSONAr ...

  8. C++PE文件格式解析类(轻松制作自己的PE文件解析器)

    PE是Portable Executable File Format(可移植的运行体)简写,它是眼下Windows平台上的主流可运行文件格式. PE文件里包括的内容非常多,详细我就不在这解释了,有兴趣 ...

  9. 04StringBuffer相关知识、Arrays类、类型互换、正则、Date相关

    04StringBuffer相关知识.Arrays类.类型互换.正则.Date相关-2018.7.12 1.StringBuffer A:StringBuffer的构造方法: public Strin ...

随机推荐

  1. Entity Framework Code First迁移基本面拾遗

    项目中用到了EF Code First和迁移,但发现有些方面似懂非懂.比如:如何在迁移文件中控制迁移过程?如果在迁移文件中执行SQL语句?如何使用Update-Database的其它参数?数据库在生产 ...

  2. 一个purge参数引发的惨案——从线上hbase数据被删事故说起

    在写这篇blog前,我的心情久久不能平静,虽然明白运维工作如履薄冰,但没有料到这么一个细小的疏漏会带来如此严重的灾难.这是一起其他公司误用puppet参数引发的事故,而且这个参数我也曾被“坑过”.   ...

  3. QT Creater + vs2010 发布程序

    这几天帮同学写了个简单的gui应用,用的qt5.0.2_msvc2010.写的程序需要在一台没有装过vs和qt的机子上运行. 在release下编译运行通过后,把相应的依赖dll加入到exe相同的文件 ...

  4. embarcadero radstudio xe5 正式版 下载地址

    http://altd.embarcadero.com/download/radstudio/xe5/delphicbuilder_xe5_win.iso

  5. Apache shiro之权限校验流程

    从张开涛blog学习后整理:http://jinnianshilongnian.iteye.com/blog/2018398 图片原图比较大,建议将图片在新的选项卡打开后100%大小浏览 在权限校验中 ...

  6. LeetCode: Unique Paths 解题报告

    A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...

  7. ubuntu-16.04+-xxx-i386.iso :安装 Oracle 11gR2 数据库

    前言:说实在的,ubuntu 16.04以上很难安装oracle!其间走过了艰难的一段路! 重要附件:ubuntu16.04+-xxx-i386.iso_安装oracle所需的软件包.zip 特点: ...

  8. Spring3系列11- Spring AOP——自动创建Proxy

    Spring3系列11- Spring AOP——自动创建Proxy 在<Spring3系列9- Spring AOP——Advice>和<Spring3系列10- Spring A ...

  9. Filter之——GZIP全站压缩

    GZIP压缩:将压缩后的文本文件,发送给浏览器,减少流量. 一.进行gzip压缩条件: 1.请求头:Accept-Encoding : gzip  告诉服务器,该浏览器支持gzip压缩. 2.响应头: ...

  10. MailMessage to EML

    EML格式是微软公司在Outlook中所使用的一种遵循RFC822及其后续扩展的文件格式,并成为各类电子邮件软件的通用格式. 做个笔记,C# 邮件处理保存为eml格式: 一.网上好多这样的写法,可以在 ...