驴子的新闻提取系统(一)

    转载请标明出处。

新闻采阅系统效果图

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

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

    虽然我信念 不要选择平庸,平庸即有被潜规则的信条,但是在现实生活中,我仍然是一个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。

代码如下:

 

ExpandedBlockStart.gif 判断当前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 ;
            }
        }

 

 

 

 

ExpandedBlockStart.gif 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);
            }        

        }


 

 

ExpandedBlockStart.gif 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);
            }



        }

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值