启发式提取一个网页的主体内容

对于一个网页来说,一般都会有非常丰富的边框或导航条信息,但是用户往往比较专注于主题内容,边框内容可以说是没有太大价值的。尤其是对于手持设备来说,显示大量的边框信息将变得非常讨厌。在搜索引擎中,其实只要搜索引网页的主题内容,对于边框内容的索引意义不大。

上面说了一下提取一个网页主体内容的作用,下面来讨论一下方法。如果真要准确地提取一个网页的主题将非常困难,因为网页的结构多种多样,如果要做到程序自动化提取还是有一定困难的。

在网上看资料的时候,看到一篇文章http://w-shadow.com/blog/2008/01/25/extracting-the-main-content-from-a-webpage/,讲述了如何基于启发式提取网页内容,还有php实现的代码。

我们知道对于导航信息来说,一般是都是一些链接,那么网页中的链接文本的占比程度就会比较高,我们只要去除这些链接占比程度比较高的文本,就基本可以去除这些丰富的边框信息了。

下面是我用C#实现的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Sgml;
using System.IO;

namespace Soso.Youwang
{

    class HtmlMainContentExtractor
    {
        private HashSet containerTags = new HashSet() { "div", "table", "td", "th", "tbody", "thead", "tfoot", "col", "colgroup", "ul", "ol", "html", "center", "span", "form" };
        private HashSet removeTags = new HashSet() { "script", "noscript", "style", "meta", "input", "iframe", "embed", "hr", "img", "#comment", "link", "label" };
        private HashSet igonoreLenTags = new HashSet() { "span" };

        /// 
        /// 链接文本的长度
        /// 
        private int totalLinkLen = 0;
        /// 
        /// 总的文本的长度
        /// 
        private int totalLen = 0;

        /// 
        /// 链接度,是平均的多少倍以上就删除
        /// 
        private double rate = 1.1;

        /// 
        /// 平均链接度
        /// 
        private double avgLinkRate;

        /// 
        /// 小于一定字符数的container将被删除
        /// 
        private int minLen = 20;

        public String Extract(string html)
        {
            html = html.ToLower();
            XmlDocument doc = ConvertHtml2Xhtml(html);
            HeuristicRemove(doc.DocumentElement);
            avgLinkRate = 1.0 * totalLinkLen / totalLen;
            int total, link;
            ContainerRemove(doc.DocumentElement, out total, out link);
            return doc.OuterXml;
            
        }

        private bool ContainerRemove(XmlNode node, out int total, out int link)
        {
            total = 0;
            link = 0;
            List toRemove = new List();
            foreach (XmlNode el in node.ChildNodes)
            {
                int t;
                int l;
                if (ContainerRemove(el, out t, out l))
                {
                    toRemove.Add(el);
                }
                else
                {
                    total += t;
                    link += l;
                }
            }

            foreach (XmlNode el in toRemove)
            {
                node.RemoveChild(el);
            }

            if (containerTags.Contains(node.Name))
            {
                if ((!igonoreLenTags.Contains(node.Name) && total <= minLen) || 1.0 * link / total >= rate * avgLinkRate)
                {
                    return true;
                }
            }
            else if (node.NodeType == XmlNodeType.Text)
            {
                total += node.Value.Length;
            }
            else if (node.Name == "a")
            {
                link += node.InnerText.Length;
            }
            return false;
        }

        private bool HeuristicRemove(XmlNode node)
        {
            if (removeTags.Contains(node.Name))
            {
                return true;
            }
            List toRemove = new List();
            foreach (XmlNode el in node.ChildNodes)
            {
                if (HeuristicRemove(el))
                {
                    toRemove.Add(el);
                }
            }
            foreach (XmlNode el in toRemove)
            {
                node.RemoveChild(el);
            }

            if (node.Name == "a")
            {
                totalLinkLen += node.InnerText.Length;
            }
            else if (node.NodeType == XmlNodeType.Text)
            {
                totalLen += node.Value.Length;
            }
            return false;
        }

        private XmlDocument ConvertHtml2Xhtml(string html)
        {
            using (SgmlReader reader = new SgmlReader())
            {
                reader.DocType = "HTML";
                reader.InputStream = new StringReader(html);
                using (StringWriter stringWriter = new StringWriter())
                {
                    using (XmlTextWriter writer = new XmlTextWriter(stringWriter))
                    {
                        reader.WhitespaceHandling = WhitespaceHandling.None;
                        writer.Formatting = Formatting.Indented;
                        XmlDocument doc = new XmlDocument();
                        doc.Load(reader);
                        return doc;
                    }
                }
            }
        }
    }
}

代码中用到了一个将html正规化,然后转成xml的一个工具:SgmlReaderDll。

整个工程代码,请点这里

posted on 2011-12-22 22:41  小橋流水  阅读(318)  评论(0编辑  收藏  举报

导航