转载请标明出处。

新闻采阅系统效果图

    《网页解析器设计》这个题目是我本科毕业设计的题目。时间真快哈,转眼又是一年过去了。去年的这个时候,我正忙着毕设以及考研的复试。那个时候的,未经历过社会的洗礼,对科研,都研究生生活充满了天真而或是白痴般的憧憬。

    真是不吃一堑不长一智哈。经过了考研,复试的洗礼。我那幼稚单纯的心,终于开化了,作为一个具有社会属性的人,我也越发市侩和成熟啦。相信我会越走越好的,因为我不在单纯地生活在了我自己杜撰的社会的中了。。。

    虽然我信念 不要选择平庸,平庸即有被潜规则的信条,但是在现实生活中,我仍然是一个As plain as Jane一样的女孩。学术上没有 novel idea;工程上也没有或将成为大牛的魄力。混乱的编码习惯,尽管大师兄一再强调要改掉,可是至今没有改好,尽管我每次都会思考这个变量该起什么名字好呢?可是写完程序,还是觉得可读性很差,大多数人看了头痛,看不懂。就犹如我写的字一样。。。。

   废话少说,切入正题。本来打算以这个题目为切机发一篇论文了,可是我的方法太老了,而且有诸多参照,如果发PAPER也是在哪种不入流的刊物上发表,而且日后可能会因为这篇文章坏了名声。所以就写成工程型的文字和网友分享了。感谢 same name 的师兄对我论文草稿的修改,再发论文,我会发一个其他版面的。

  我的这篇文字,理论基础在于  孙承杰,关毅 基于统计的网页正文信息抽取方法的研究 《中文信息学报》2004年 第18卷 5期

 当然一年前,我还是个小P孩,还没有本事将人家论文上的算法直接实现  。当时老师让我用C++/MFC进行实现,可是我安装boost库的时候就没安好,又见 VS2005写C++语言程序的时候没有提示,又见了网络上对VS2005的一些负面提示,就打消这个念头了,换成了C#。

  这篇文章我的代码实现是参考的咱们园子里的蛙蛙王子的蛙蛙推荐:基于标记窗的网页正文提取算法的一些细节问题      蛙蛙牌正文提取算

 在这里要对蛙蛙的刻苦学习和钻研精神进行一下赞扬。已经是工作的人了还要这么大的劲头进行学习钻研,确实是值得我们小辈学习的。

注:蛙蛙的算法,不应该是基于标记窗的正文提取算法,应该属于孙承杰的统计方法。

 

 

前面一部分是老太太裹脚布般的引言,下面 let's begin.

系统实现
语言 C#
调用外来库Winistahtmlparser.

程序的总体印象图请见新闻采阅系统效果图。上半学期《现代信息检索》的大作业就是在本科毕设的基础上进行的修改。

提取过程中提供了几个新闻语料库可供大家下载。

搜狐语料

新浪语料

网易语料

腾讯语料库

 

我的系统分为两个解析模块: 目录页(参照中科院软件所于满全的叫法叫做索引页)解析模块和新闻正文页(参照于满全的叫法叫做正文页)解析模块。与以往网友版解析系统不同之处在于,如果是多页新闻,那么我的系统1.可以将新闻的每一页都提出来;2.能够自动识别文本编码。当然这两个特点是和那个金油条新闻提取器来比较的。

目录页提取算法。

目前大多数,二级目录页面都是div/table/li型的,即提取的URL放在这三种元素中的一种。另外,几乎所有的新闻入口目录页上面都标记有新闻题目和发布时间(精确到小时和分钟),那么为了简便,我们就以这个\d\d:\d\d为特征模式提取新闻的URL。

代码如下:

 

判断当前URL在什么元素中
  /// <summary>
        
/// 函数名称:GetPattern
        
/// 功能说明:用于判定索引页正文是储存在Li中还是table中
        
/// 参数:string rawtext 去掉style 等无关标签之后的网页源码
        
/// 返回值 bool  true表明是table型;false表明是li型
        
/// </summary>
        
/// <param name="rawtext"></param>
        
/// <returns></returns>
       public static bool GetPattern(string rawtext)
        {
            Lexer lexer 
= new Lexer(rawtext);
            Parser parser 
= new Parser(lexer);
            NodeFilter filter 
= new TagNameFilter("li");//解析出其中的li元素
            NodeList htmlNodes = parser.Parse(filter);
            
if (htmlNodes.Count == 0)
            {
                
return true;//如果源码中不含有li元素则该索引页属于table型。
            }
            
else
            {
                
//去掉其中不含有时间的条目
                Regex f2 = new Regex(@"\d\d:\d\d");
                
for (int i = htmlNodes.Count - 1; i >= 0; i--)
                {
                    
if (!f2.IsMatch(htmlNodes[i].ToHtml()))
                        htmlNodes.Remove(i);

                }

                
if (htmlNodes.Count == 0)//如果网页源码中含有li元素,但是li元素中不含有带发布时间的连接,则该索引页属于table型
                    return true;
                
else//否则为li型
                    return false;
            }
        }

 

 

 

 

table型索引页提取算法
   /// <summary>
       
/// 函数名称:ItemRetrival_1
       
/// 功能说明:用于提取帖子列表页面的url,帖子标题,帖子时间
       
/// 参数:string url表示帖子列表url
       
/// 参数 ref Encoding encode 用于获取网页字符集编码
       
/// 参数: ref List<string> listUrl,listTitle,listTime用于存放提取出的各项信息
       
/// 
       
/// </summary>
       
/// <param name="url"></param>
       
/// <param name="encode"></param>
       
/// <param name="listurl"></param>
       
/// <param name="listtitle"></param>
       
/// <param name="listtime"></param>

        
public static void ItemRetrival_1(string url, ref Encoding encode, ref List<string> listUrl, ref List<string> listTitle,
                                          
ref List<string> listTime)
        {
            
//获取网页源码;
            string rawtext = GetDataFromUrl(url, ref encode);
            
//将无关的style,script等标签去掉;
            string reg1 = @"<style[\s\S]+?/style>|<select[\s\S]+?/select>|<script[\s\S]+?/script>|<\!\-\-[\s\S]*?\-\->";
            rawtext 
= new Regex(reg1, RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(rawtext, "");


            
//以下用htmlparser提取源码中的目标table;

            Lexer lexer 
= new Lexer(rawtext);
            
//解析出其中的table元素
            Parser parser = new Parser(lexer);
            NodeFilter filter 
= new TagNameFilter("table");
            NodeList htmlNodes 
= parser.Parse(filter);
            
//去除嵌套式table
            Regex f1 = new Regex(@"<table.*?>");
            
for (int i = htmlNodes.Count - 1; i >= 0; i--)
            {
                MatchCollection myCollection 
= f1.Matches(htmlNodes[i].ToHtml());
                
if (myCollection.Count > 1)
                    htmlNodes.Remove(i);

            }

            
//去除没有时间的table,认为这种table是无效table
            Regex f2 = new Regex(@"\d\d:\d\d");
            
for (int i = htmlNodes.Count - 1; i >= 0; i--)
            {
                
if (!f2.IsMatch(htmlNodes[i].ToHtml()))
                    htmlNodes.Remove(i);

            }



            
//以下程序解析出以上三种目标信息

            
string final = htmlNodes.ToHtml();
            Lexer lex2 
= new Lexer(final);
            Parser par2 
= new Parser(lex2);
            NodeFilter filter2 
= new TagNameFilter("tr");
            NodeList finalNodes 
= par2.Parse(filter2);
            
//提取发帖时间信息
            RegexFilter rf = new RegexFilter(@"\d\d:\d\d");
            
for (int i = 0; i < finalNodes.Count; i++)
            {
                Lexer lexerTmp 
= new Lexer(finalNodes[i].ToHtml());
                Parser parserTmp 
= new Parser(lexerTmp);
                NodeList tmp 
= parserTmp.Parse(rf);
                
if (tmp.Count > 0)
                    
for (int j = 0; j < tmp.Count; j++)
                    {
                        
string temp = tmp[j].ToHtml();
                        ModifyRawText(
ref temp);
                        listTime.Add(temp);

                    }

            }
            
//提取帖子URL以及帖子标题
            string atagAssist = finalNodes.ToHtml();
            Lexer lex3 
= new Lexer(atagAssist);
            Parser par3 
= new Parser(lex3);
            NodeFilter filter3 
= new TagNameFilter("a");
            NodeList atagNodes 
= par3.Parse(filter3);
            
string urlpart = new Regex(@"http://.*?(?=/)").Match(url).Value;
            
for (int i = 0; i < atagNodes.Count; i++)
            {
                ATag link 
= (ATag)atagNodes.ElementAt(i);
                
string temp1 = link.GetAttribute("href");
                
string temp2 = link.StringText;
                
                
if (!new Regex("http").IsMatch(temp1))//如果提取出的url为相对url,则加上域名补全为绝对url
                {
                    temp1 
= urlpart + temp1;//将提取出的url构造完整,形成完整的url
                }
                ModifyRawText(
ref temp2);
                listUrl.Add(temp1);
                listTitle.Add(temp2);
            }        

        }


 

 

Li型索引页提取算法
  /// <summary>
        
/// 函数名称:ItemRetrival_2
        
/// 功能说明:用于提取帖子列表页面的url,帖子标题,帖子时间
        
/// 参数:string url表示帖子列表url
        
/// 参数 ref Encoding encode 用于获取网页字符集编码
        
/// 参数: ref List<string> listUrl,listTitle,listTime用于存放提取出的各项信息
        
/// 
        
/// </summary>
        
/// <param name="url"></param>
        
/// <param name="encode"></param>
        
/// <param name="listurl"></param>
        
/// <param name="listtitle"></param>
        
/// <param name="listtime"></param>
        public static void ItemRetrival_2(string url, ref Encoding encode, ref List<string> listUrl, ref List<string> listTitle,
                                            
ref List<string> listTime)
        {

            
//获取网页源码;
            string rawtext = GetDataFromUrl(url, ref encode);
            
string reg1 = @"<style[\s\S]+?/style>|<select[\s\S]+?/select>|<script[\s\S]+?/script>|<\!\-\-[\s\S]*?\-\->";
            rawtext 
= new Regex(reg1, RegexOptions.Multiline | RegexOptions.IgnoreCase).Replace(rawtext, "");
            
//将无关的style,script等标签去掉;
            
//以下操作用于提取帖子页面的发帖时间、帖子URL,帖子标题等信息
            
//用htmlparser获取目标li元素
            Lexer lexer = new Lexer(rawtext);
            Parser parser 
= new Parser(lexer);
            NodeFilter filter 
= new TagNameFilter("li");//解析出其中的li元素
            NodeList htmlNodes = parser.Parse(filter);
            
//去掉其中不含有时间的条目
            Regex f2 = new Regex(@"\d\d:\d\d");
            
for (int i = htmlNodes.Count - 1; i >= 0; i--)
            {
                
if (!f2.IsMatch(htmlNodes[i].ToHtml()))
                    htmlNodes.Remove(i);

            }
            RegexFilter rf 
= new RegexFilter(@"\d\d:\d\d");
            
string final = htmlNodes.ToHtml();
            
for (int i = 0; i < htmlNodes.Count; i++)
            {
                Lexer lexerTmp 
= new Lexer(htmlNodes[i].ToHtml());
                Parser parserTmp 
= new Parser(lexerTmp);
                NodeList tmp 
= parserTmp.Parse(rf);
                
if (tmp.Count > 0)
                    
for (int j = 0; j < tmp.Count; j++)
                    {
                        
string temp = tmp[j].ToHtml();
                        ModifyRawText(
ref temp);
                        listTime.Add(temp);

                    }
            }


            
//提取帖子url和标题
            string atagAssist = htmlNodes.ToHtml();
            Lexer lex3 
= new Lexer(atagAssist);
            Parser par3 
= new Parser(lex3);
            NodeFilter filter3 
= new TagNameFilter("a");
            NodeList atagNodes 
= par3.Parse(filter3);
            
for (int i = 0; i < atagNodes.Count; i++)
            {
                
string urlpart = new Regex(@"http://.*?(?=/)").Match(url).Value;
                ATag link 
= (ATag)atagNodes.ElementAt(i);
                
string temp1 = link.GetAttribute("href");
                
string temp2 = link.StringText;

                
if (!new Regex("http").IsMatch(temp1))//如果提取出的url为相对url,则加上域名补全为绝对url
                {
                    temp1 
= urlpart + temp1;//将提取出的url构造完整,形成完整的url
                }
                ModifyRawText(
ref temp2);
                listUrl.Add(temp1);
                listTitle.Add(temp2);
            }



        }

 

 

 

 

 

 

 

 

posted on 2010-03-20 10:58  finallyly  阅读(6812)  评论(25编辑  收藏  举报