HtmlAgilityPack的一点总结

最近工作中用到了HtmlAgilityPack的类库,总的来说使用起来确实感觉挺方便,别的不多说,就这类似于能把HTML标签自动补全的Load()方法就感觉挺赞(其实上不是不全,而是将不完整的标签给格式化一下)。但这不就足够了吗?舍得自己去用正则表达式去匹配,万一匹配的内容就是HTML作者写的文本内容,岂不功亏一篑。

不废话,使用的时候加载HtmlAgilityPack.dll(下载去官网),using HtmlAgilityPack; 就可以使用HtmlAgilityPack了。

本文以格式化一篇Html为例,讲述一点此类库的一点用法,至于更多的方法和属性,那就看作者的发挥了。

何为格式化HTML?当你看到别人写的HTML是这样的:

<html><head><title>就是一个字符串</title></head><body><p>就是不换行</p></body></html>

是不是觉得很头疼呢?写成规范一点的树结构是不是更好一点?(虽然改变了原文,加入了很多/r,/n,/t,但毕竟这是用户想看到的)

<html>

______<head>

____________<title>

__________________就是一个字符串

____________</title>

______</head>

______<body>

____________<p>

__________________就是不换行

____________</p>

______</body>

</html>

当然了,强大的IDE和各种工具会帮你做到,但是!实际开发中怎会容你轻松的使用其他非开源工具,而开元的HTMLAgility区区几行代码便能做到。

不多说,上代码:

        private HtmlDocument loadWebSite(string path)
        {
            HtmlDocument hDoc = new HtmlDocument();//整片html文档对象
            if (File.Exists(path))
            {
                hDoc.Load(path, Encoding.Default);
                hDoc.LoadHtml(hDoc.DocumentNode.OuterHtml);//load两边是为了保证标签的完整性,DocumentNode代表文档节点							     						     (文档的开头了结尾,不一定是<html>,OuterHtml代表此nod	e的html文本)
            }
            return hDoc;
        }
计算父节点的方法,以<html>节点为根父节点,有所少个父节点,就加入多少个/t(缩进)
	private int parentNumbers(HtmlNode node, int temp)
        {
            int result = temp;
            if (node.ParentNode != null)
            {
                if (node.ParentNode.Name.Equal("html"))
                {
                    return result;
                }
                else
                {
                    result = result + parentNumbers(node.ParentNode, 1);//递归调用
                }
            }
            return result;
        }
好了,以上两个方法是准备工作,真正格式化的方法在这里:

	private void convertHTML(string filePath)
        {
            HtmlDocument hDoc = loadWebSite(filePath);
            string reg = "\\S+";
            Regex regex = new Regex(reg, RegexOptions.IgnoreCase);//此处的正则表达式是为了将html文件写成一行,将原作者的转义字符取消,加入自己的转义字符(/r/n/t)
            HtmlNodeCollection htmlNodeCollection = hDoc.DocumentNode.SelectNodes("/html");
            StringBuilder htmlContent = new StringBuilder();

            if (htmlNodeCollection.Count() > 0)
            {
                foreach (HtmlNode htmlNode in htmlNodeCollection)
                {
                    foreach (HtmlNode hNode in htmlNode.Descendants().Where(n => n.NodeType == HtmlNodeType.Text))
                    {
                        Match match = regex.Match(hNode.OuterHtml);
                        if (match.Success == false)
                        {
                            hNode.InnerHtml = string.Empty;
                        }
                        else
                        {
                            hNode.InnerHtml = hNode.InnerHtml.Trim();
                        }
                    }
                    htmlContent.Append(htmlNode.OuterHtml);
                }
            }
            else { throw new Exception("Document Have No Html Element"); }
            //load两遍,保证标签的完整性
            hDoc.LoadHtml(htmlContent.ToString());
            StringBuilder content = new StringBuilder(hDoc.DocumentNode.OuterHtml);
            hDoc.LoadHtml(content.ToString());
            HtmlNodeCollection lineCollection = hDoc.DocumentNode.SelectNodes("/html");
            Dictionary<int, string> insContent = new Dictionary<int, string>();//key为要插入的index,value为插入的内容,也就是/r/n, /r/n/t, /r/n/t/t.../t
            StringBuilder tempBuilder = new StringBuilder();

            foreach (HtmlNode htmlNode in lineCollection)
            {
                insContent.Add(htmlNode.StreamPosition, "\r\n");
                if (htmlNode.HasChildNodes)
                {
                    insContent.Add(htmlNode.FirstChild.StreamPosition, "\r\n\t");
                    insContent.Add(htmlNode.LastChild.StreamPosition + htmlNode.LastChild.OuterHtml.Length, "\r\n");
                }
                foreach (HtmlNode hNode in htmlNode.Descendants())//遍历所有子代节点
                {
                    if (hNode.ParentNode.Name.Eq("style"))//此处的判断是针对HTMLAgility的一个Bug,当节点为<style>时,此节点的StreamPosition为0,这是不对的,正确值应为对应							    的html字符串形式时的Index值
                    {
                        continue;
                    }
                    if (hNode.PreviousSibling != null)//上一个兄弟节点
                    {
                        if (!insContent.ContainsKey(hNode.StreamPosition))
                        {
                            tempBuilder.Remove(0, tempBuilder.Length);
                            tempBuilder.Append("\r\n");
                            for (int i = 0; i < parentNumbers(hNode, 1); i++)
                            {
                                tempBuilder.Append("\t");
                            }
                            insContent.Add(hNode.StreamPosition, tempBuilder.ToString());
                        }
                    }
                    if (hNode.NextSibling != null)//下一个兄弟节点
                    {
                        if (!insContent.ContainsKey(hNode.StreamPosition))
                        {
                            tempBuilder.Remove(0, tempBuilder.Length);
                            tempBuilder.Append("\r\n");
                            for (int i = 0; i < parentNumbers(hNode, 1); i++)
                            {
                                tempBuilder.Append("\t");
                            }
                            insContent.Add(hNode.NextSibling.StreamPosition, tempBuilder.ToString());
                        }
                    }
                    if (hNode.HasChildNodes)
                    {
                        if (hNode.FirstChild != null)//第一个子节点
                        {
                            if (!insContent.ContainsKey(hNode.FirstChild.StreamPosition))
                            {
                                tempBuilder.Remove(0, tempBuilder.Length);
                                tempBuilder.Append("\r\n");
                                for (int i = 0; i < parentNumbers(hNode.FirstChild, 1); i++)
                                {
                                    tempBuilder.Append("\t");
                                }
                                insContent.Add(hNode.FirstChild.StreamPosition, tempBuilder.ToString());
                            }
                        }
                        if (hNode.LastChild != null)//最后一个子节点
                        {
                            if (!insContent.ContainsKey(hNode.LastChild.StreamPosition + hNode.LastChild.OuterHtml.Length))
                            {
                                tempBuilder.Remove(0, tempBuilder.Length);
                                tempBuilder.Append("\r\n");
                                for (int i = 0; i < parentNumbers(hNode, 1); i++)
                                {
                                    tempBuilder.Append("\t");
                                }
                                insContent.Add(hNode.LastChild.StreamPosition + hNode.LastChild.OuterHtml.Length, tempBuilder.ToString());
                            }
                        }
                    }
                }
            }

            foreach (KeyValuePair<int, string> item in insContent.OrderByDescending(n => n.Key))//倒序插入,保证原html不变
            {
                content.Insert(item.Key, item.Value);
            }
            File.WriteAllText(filePath, content.ToString(), Encoding.UTF8);
        }
好了,以上方法能够实现格式化一篇html的功能了,经测试,新浪,搜狐,网易的门户网站的html已经格式化成功,其实作者也可以看看这些门户网站的html代码,那是多么的乱。针对于HTMLAgility的类库和方法介绍的并不算全面,但是以上这些获取节点集合,属性集合,innerHtml等等吧,还有HtmlWeb类都是最常用的,解析HTML我想足够了,就这些吧。




























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值