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

功能:

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

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

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

用法:

string filePath = Server.MapPath("~/file/test.htm");
      //获取HTML代码
      string mailBody = FileHelper.FileToString(filePath);

      XHtmlElement xh = new XHtmlElement(mailBody);

      //获取body的子集a标签并且class="icon"
      var link = xh.Descendants("body").ChildDescendants("a").Where(c => c.Attributes.Any(a => a.Key == "class" && a.Value == "icon")).ToList();

      //获取带href的a元素
      var links = xh.Descendants("a").Where(c => c.Attributes.Any(a => a.Key == "href")).ToList();
      foreach (var r in links)
      {
        Response.Write(r.Attributes.Single(c => c.Key == "href").Value); //出输href
      }

      //获取第一个img
      var img = xh.Descendants("img");

      //获取最近的第一个p元素以及与他同一级的其它p元素
      var ps = xh.Descendants("p");

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;

namespace SyntacticSugar
{
  /// <summary>
  /// ** 描述:html解析类
  /// ** 创始时间:2015-4-23
  /// ** 修改时间:-
  /// ** 作者:sunkaixuan
  /// ** qq:610262374 欢迎交流,共同提高 ,命名语法等写的不好的地方欢迎大家的给出宝贵建议
  /// </summary>
  public class XHtmlElement
  {
    private string _html;
    public XHtmlElement(string html)
    {
      _html = html;
    }

    /// <summary>
    /// 获取最近的相同层级的HTML元素
    /// </summary>
    /// <param name="elementName">等于null为所有元素</param>
    /// <returns></returns>
    public List<HtmlInfo> Descendants(string elementName = null)
    {
      if (_html == null)
      {
        throw new ArgumentNullException("html不能这空!");
      }
      var allList = RootDescendants(_html);
      var reval = allList.Where(c => elementName == null || c.TagName.ToLower() == elementName.ToLower()).ToList();
      if (reval == null || reval.Count == 0)
      {
        reval = GetDescendantsSource(allList, elementName);
      }
      return reval;
    }

    /// <summary>
    /// 获取第一级元素
    /// </summary>
    /// <param name="elementName"></param>
    /// <returns></returns>
    public List<HtmlInfo> RootDescendants(string html = null)
    {
      /*
       * 业务逻辑:
             * 1、获取第一个html标签一直找结尾标签,如果在这个过程中遇到相同的标签收尾标签就要加1
             * 2、第一个标签取到后继续第一步操作,找第2个元素 。。第N个元素
       */
      if (html == null) html = _html;
      var firstTag = Regex.Match(html, "<.+?>");

      List<string> eleList = new List<string>();
      List<HtmlInfo> reval = new List<HtmlInfo>();
      GetElementsStringList(html, ref eleList);
      foreach (var r in eleList)
      {
        HtmlInfo data = new HtmlInfo();
        data.OldFullHtml = r;
        data.SameLeveHtml = html;
        data.TagName = Regex.Match(r, @"(?<=\s{1}|\<)[a-z,A-Z]+(?=\>|\s)", RegexOptions.IgnoreCase).Value;
        data.InnerHtml = Regex.Match(r, @"(?<=\>).+(?=<)", RegexOptions.Singleline).Value;
        var eleBegin = Regex.Match(r, "<.+?>").Value;
        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();
        data.Attributes = new Dictionary<string, string>();
        if (attrList != null && attrList.Count > 0)
        {
          foreach (var a in attrList)
          {
            data.Attributes.Add(a.key, a.value);
          }
        }
        reval.Add(data);
      }
      return reval;

    }

    #region private
    private List<HtmlInfo> GetDescendantsSource(List<HtmlInfo> allList, string elementName)
    {
      foreach (var r in allList)
      {
        if (r.InnerHtml == null || !r.InnerHtml.Contains("<")) continue;
        var childList = RootDescendants(r.InnerHtml).Where(c => elementName == null || c.TagName.ToLower() == elementName.ToLower()).ToList();
        if (childList == null || childList.Count == 0)
        {
          childList = GetDescendantsSource(RootDescendants(r.InnerHtml), elementName);
          if (childList != null && childList.Count > 0)
            return childList;
        }
        else
        {
          return childList;
        }
      }
      return null;
    }

    private void GetElementsStringList(string html, ref List<string> eleList)
    {
      HtmlInfo info = new HtmlInfo();
      info.TagName = Regex.Match(html, @"(?<=\<\s{0,5}|\<)([a-z,A-Z]+|h\d{1})(?=\>|\s)", RegexOptions.IgnoreCase).Value;
      string currentTagBeginReg = @"<\s{0,10}" + info.TagName + @".*?>";//获取当前标签元素开始标签正则
      string currentTagEndReg = @"\<\/" + info.TagName + @"\>";//获取当前标签元素收尾标签正则
      if (string.IsNullOrEmpty(info.TagName)) return;

      string eleHtml = "";
      //情况1 <a/>
      //情况2 <a></a>
      //情况3 <a> 错误格式
      //情况4endif
      if (Regex.IsMatch(html, @"<\s{0,10}" + info.TagName + "[^<].*?/>"))//单标签
      {
        eleHtml = Regex.Match(html, @"<\s{0,10}" + info.TagName + "[^<].*?/>").Value;
      }
      else if (!Regex.IsMatch(html, currentTagEndReg))//没有收尾
      {
        if (Regex.IsMatch(html, @"\s{0,10}\<\!\-\-\[if"))
        {
          eleHtml = GetElementString(html, @"\s{0,10}\<\!\-\-\[if", @"\[endif\]\-\-\>", 1);
        }
        else
        {
          eleHtml = Regex.Match(html, currentTagBeginReg,RegexOptions.Singleline).Value;
        }
      }
      else
      {
        eleHtml = GetElementString(html, currentTagBeginReg, currentTagEndReg, 1);
      }

      try
      {
        eleList.Add(eleHtml);
        html = html.Replace(eleHtml, "");
        html = Regex.Replace(html, @"<\!DOCTYPE.*?>", "");
        if (!Regex.IsMatch(html, @"^\s*$"))
        {
          GetElementsStringList(html, ref eleList);
        }

      }
      catch (Exception ex)
      {
        throw new Exception("SORRY,您的HTML格式不能解析!!!");

      }

    }

    private string GetElementString(string html, string currentTagBeginReg, string currentTagEndReg, int i)
    {

      string newHtml = GetRegNextByNum(html, currentTagBeginReg, currentTagEndReg, i);
      var currentTagBeginMatches = Regex.Matches(newHtml, currentTagBeginReg, RegexOptions.Singleline).Cast<Match>().Select(c => c.Value).ToList();
      var currentTagEndMatches = Regex.Matches(newHtml, currentTagEndReg).Cast<Match>().Select(c => c.Value).ToList();
      if (currentTagBeginMatches.Count == currentTagEndMatches.Count)
      { //两个签标元素相等
        return newHtml;
      }
      return GetElementString(html, currentTagBeginReg, currentTagEndReg, ++i);
    }

    private string GetRegNextByNum(string val, string currentTagBeginReg, string currentTagEndReg, int i)
    {
      return Regex.Match(val, currentTagBeginReg + @"((.*?)" + currentTagEndReg + "){" + i + "}?", RegexOptions.IgnoreCase | RegexOptions.Singleline).Value;
    }
    #endregion

  }
  public static class XHtmlElementExtendsion
  {
    /// <summary>
    /// 获取最近的相同层级的HTML元素
    /// </summary>
    /// <param name="elementName">等于null为所有元素</param>
    /// <returns></returns>
    public static List<HtmlInfo> Descendants(this IEnumerable<HtmlInfo> htmlInfoList, string elementName = null)
    {
      var html = htmlInfoList.First().InnerHtml;
      XHtmlElement xhe = new XHtmlElement(html);
      return xhe.Descendants(elementName);
    }
    /// <summary>
    /// 获取下级元素
    /// </summary>
    /// <param name="elementName"></param>
    /// <returns></returns>
    public static List<HtmlInfo> ChildDescendants(this IEnumerable<HtmlInfo> htmlInfoList, string elementName = null)
    {
      var html = htmlInfoList.First().InnerHtml;
      XHtmlElement xhe = new XHtmlElement(html);
      return xhe.RootDescendants(html).Where(c => elementName == null || c.TagName == elementName).ToList();
    }

    /// <summary>
    /// 获取父级
    /// </summary>
    /// <param name="htmlInfoList"></param>
    /// <returns></returns>
    public static List<HtmlInfo> ParentDescendant(this IEnumerable<HtmlInfo> htmlInfoList,string fullHtml)
    {
      var saveLeveHtml = htmlInfoList.First().SameLeveHtml;
      string replaceGuid=Guid.NewGuid().ToString();
      fullHtml = fullHtml.Replace(saveLeveHtml,replaceGuid);
      var parentHtml = Regex.Match(fullHtml, @"<[^<]+?>[^<]*?" + replaceGuid + @".*?<\/.+?>").Value;
      parentHtml = parentHtml.Replace(replaceGuid, saveLeveHtml);
      XHtmlElement xhe = new XHtmlElement(parentHtml);
      return xhe.RootDescendants();
    }
  }
  /// <summary>
  /// html信息类
  /// </summary>
  public class HtmlInfo
  {
    /// <summary>
    /// 元素名
    /// </summary>
    public string TagName { get; set; }
    /// <summary>
    /// 元素属性
    /// </summary>
    public Dictionary<string, string> Attributes { get; set; }
    /// <summary>
    /// 元素内部html
    /// </summary>
    public string InnerHtml { get; set; }

    public string OldFullHtml { get; set; }

    public string SameLeveHtml { get; set; }

    /// <summary>
    /// 得到元素的html
    /// </summary>
    /// <returns></returns>
    public string FullHtml
    {
      get
      {
        StringBuilder reval = new StringBuilder();
        string attributesString = string.Empty;
        if (Attributes != null && Attributes.Count > 0)
        {
          attributesString = string.Join(" ", Attributes.Select(c => string.Format("{0}=\"{1}\"", c.Key, c.Value)));
        }
        reval.AppendFormat("<{0} {2}>{1}</{0}>", TagName, InnerHtml, attributesString);
        return reval.ToString();
      }
    }
  }
}

前台HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
</head>
<body>
  <a id="1">我是1</a>
  <a id="2" class="icon">icon</a>
  <img />
</body>
</html>
(0)

相关推荐

  • 使用C#获取网页HTML源码的例子

    最近在做一个项目,其中一个功能是根据一个URL地址,获取到网页的源代码.在ASP.NET(C#)中,获取网页源代码貌似有很多种方法,我随便搞了一个简单的WebClient,非常简单容易.但后面一个非常恼火的问题出来了,那就是中文的乱码. 通过仔细研究,中文的网页不外乎GB2312和UTF-8这两种编码.于是有了下面这段代码: 复制代码 代码如下: /// <summary>        /// 根据网址的URL,获取源代码HTML        /// </summary>   

  • C#实现将HTML转换成纯文本的方法

    本文实例讲述了C#实现将HTML转换成纯文本的方法.分享给大家供大家参考.具体如下: 使用方法: 复制代码 代码如下: HtmlToText convert = new HtmlToText(); textBox2.Text = convert.Convert(textBox1.Text); C#代码如下: /// <summary> /// Converts HTML to plain text. /// </summary> class HtmlToText { // Stat

  • C#获取网页HTML源码实例

    本文实例讲述了C#获取网页HTML源码的方法,分享给大家供大家参考.具体方法如下: 关键代码如下: 复制代码 代码如下: /// <summary> /// 获取网页HTML源码 /// </summary> /// <param name="url">链接 eg:http://www.baidu.com/ </param> /// <param name="charset">编码 eg:Encoding.

  • C#实现下载网页HTML源码的方法

    本文实例讲述了C#实现下载网页HTML源码的方法.分享给大家供大家参考之用.具体方法如下: public static class DownLoad_HTML { private static int FailCount = 0; //记录下载失败的次数 public static string GetHtml(string url) //传入要下载的网址 { string str = string.Empty; try { System.Net.WebRequest request = Sys

  • C#实现写入文本文件内容的方法

    本文实例讲述了C#实现写入文本文件内容的方法.分享给大家供大家参考.具体如下: private void write_txt(string str1, string str2, string str3) { System.DateTime currentTime = System.DateTime.Now; string strYMD = currentTime.ToString("d"); string FILE_NAME = "MyFileSend" + str

  • C#实现压缩HTML代码的方法

    本文实例讲述了C#实现压缩html代码的方法,分享给大家供大家参考之用.具体方法如下: 主要代码如下: /// <summary> /// 压缩html代码 /// </summary> /// <param name="writer"></param> protected override void Render(HtmlTextWriter writer) { System.IO.StringWriter html = new Sys

  • C#实现HTML和UBB互相转换的方法

    本文实例讲述了C#实现HTML和UBB互相转换的方法.分享给大家供大家参考.具体实现方法如下: html转UBB的还不是很完美,有空修改,一些代码来自百度谷歌,如下所示: 复制代码 代码如下: private string DoHtmlToUBB(string _Html) { _Html = Regex.Replace(_Html, "<br[^>]*>", "\n");     _Html = Regex.Replace(_Html, @&qu

  • C#实现文本文件读写方法汇总

    方法一: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; namespace txt

  • C#判断多个文本框是否为空的方法

    本文实例讲述了C#判断多个文本框是否为空的方法.分享给大家供大家参考.具体实现方法如下: /// <summary> /// 自定义方法,判断项目中txt标签是否为空 /// </summary> /// <param name="txt">要进行判断为空的标签</param> /// <returns>是否全部都不为空,如果全部都不为空返回 true </returns> bool CheckEmpty(para

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

    功能: 1.轻松获取指元素HTML元素. 2.可以根据属性标签进行筛选 3.返回的都是Llist强类型无需转换 用过XElement的都知道 用来解析XML非常的方便,但是对于HTML的格式多样化实在是没办法兼容. 所以我就写了这么一个类似XElement的 XHTMLElement 用法: string filePath = Server.MapPath("~/file/test.htm"); //获取HTML代码 string mailBody = FileHelper.FileT

  • 用Javascript正则实现url链接的解析类

    用 Javascript 解析链接(URL)是一个常见的需求,本文介绍了一个非常健全的用 Javascript 写的链接(URL)解析类,他可以准确获取一个完整的 URL 中每个部分的内容,包括协议.URL中包含的用户名和密码.主机名.端口.路径名.参数.锚点(Fragment Anchor)等信息. if (typeof Poly9 == 'undefined') { var Poly9 = {}; } Poly9.URLParser = function(url) { this._field

  • c# 编写一个轻量级的异步写日志的实用工具类(LogAsyncWriter)

    一说到写日志,大家可能推荐一堆的开源日志框架,如:Log4Net.NLog,这些日志框架确实也不错,比较强大也比较灵活,但也正因为又强大又灵活,导致我们使用他们时需要引用一些DLL,同时还要学习各种用法及配置文件,这对于有些小工具.小程序.小网站来说,有点"杀鸡焉俺用牛刀"的感觉,而且如果对这些日志框架不了解,可能输出来的日志性能或效果未毕是与自己所想的,鉴于这几个原因,我自己重复造轮子,编写了一个轻量级的异步写日志的实用工具类(LogAsyncWriter),这个类还是比较简单的,实

  • PHP模板解析类实例

    本文实例讲述了PHP模板解析类.分享给大家供大家参考.具体如下: <?php class template { private $vars = array(); private $conf = ''; private $tpl_name = 'index'; //如果模板不存在 会查找当前 controller默认index模板 private $tpl_suffix = '.html';//如果CONFIG没配置默认后缀 则显示 private $tpl_compile_suffix= '.t

  • 专为新手写的结合smarty的类第1/3页

    专为新手写的结合smarty的类,诚挚邀请大家多提宝贵意见 这个是为新手(也为自己)写的结合smarty一起使用的类,是未完成的,现在放出来的目的不是马上让新手使用,所以也没有把注解写的非常详细 希望各位高手多多提意见,我尽量完善它.     首先声明,我写这个的目的也是为了锻炼自己,尽管我知道现在已经有很多类似的类了,但是我还是决定来写一个, 所以请大家在浏览的时候口下留情. 还有文件我也已经打包上传了,放在下面,请大家多多下载,多多提意见.有什么问题直接Q我 目前该类包括以下功能(使用范例,

  • 自己写了一个php检测文件编码的函数

    关于文件编码的检测,百度一下一大把都是,但是确实没有能用的. 很多人建议 mb_detect_encoding 检测,可是不知为何我这不成功,什么都没输出. 看到有人写了个增强版,用 BOM 判断的,我果断就无视了,这东西完全不靠谱. 最终根据PHP手册里 mb_detect_encoding 函数下方的例子,自己写了一个检测函数, 还包括自动检测编码并按指点编码读取文件的函数. 源码献上,不喜勿喷. 网上的方法我试过没用才写的,说不定环境不一样导致的. 所以万一没用,也别喷我,我只是共享想思路

  • php写的AES加密解密类分享

    今天写了一个php的AES加密类.适用于Yii的扩展. 如果不用在Yii框架中,把代码中Yii::app()->params['encryptKey'] 换成你对应的默认key就可以了. 类代码: <?php /** * php AES加解密类 * 如果要与java共用,则密钥长度应该为16位长度 * 因为java只支持128位加密,所以php也用128位加密,可以与java互转. * 同时AES的标准也是128位.只是RIJNDAEL算法可以支持128,192和256位加密. * java

  • xml 解析类

    xml 解析类 <?php ob_start(); /*                                                              * Copyright (c) 2003 NightKids  <weidewang@magus-soft.com>                      * All rights reserved.                                                      

  • Spring常用配置及解析类说明

    springMVC配置用法的文章很多,但具体描述清楚的不多,这里主要介绍下常用的配置项的用法,以及它的解析类,springMVC处理内容有两种方式,一种是converter,另一种是ViewResolver,两种都能处理json,xml以及form内容格式. <?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.s

  • Python建立Map写Excel表实例解析

    本文主要研究的是用Python语言建立Map写Excel表的相关代码,具体如下. 前言:我们已经能够很熟练的写Excel表相关的脚本了.大致的操作就是,从数据库中取数据,建立Excel模板,然后根据模板建立一个新的Excel表,把数据库中的数据写入.最后发送邮件.之前的一篇记录博客,写的很标准了.这里我们说点遇到的新问题. 我们之前写类似脚本的时候,有个问题没有考虑过,为什么要建立模板然后再写入数据呢?诶-其实也不算是没考虑过,只是懒没有深究罢了.只求快点完成任务... 这里对这个问题进行思考阐

随机推荐