C#自写的一个HTML解析类(类似XElement语法)

这篇文章主要介绍了C#自写的一个HTML解析类(类似XElement语法),本文给出了实现代码和使用实例,同时给出了测试HTML实例,需要的朋友可以参考下

功能:

1、轻松获取指元素HTML元素。
2、可以根据属性标签进行筛选
3、返回的都是Llist强类型无需转换

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

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

用法:

[csharp]  view plain  copy
  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");  

代码:

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

前台HTML:

[xhtml]  view plain  copy
  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> 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值