摘要:
上次和大家讨论了一些正文提取的细节问题,这次我把一些关键问题解决了,给大家上一下代码,并做了演示,能提取不少网站的正文了,当然在功能和代码组织上还有不少改进的地方。
思路:
1、抓取远程网页源码,这里要实现自动判断网页编码,否则有可能抓到乱码。我是先看应答的http头的chareset,一般这个很准,但像csdn的新闻比较变态http应答的头里的chareset和网页的meta里声明的chareset不一致,所以我手工加了一下判断,如果不一致再在内存流里用网页声明的编码读取一遍源码
2、把网页分割成几大块。试用了一下tidy的.net包装及HtmlParse的.net版本,都不太好用。于是我自己写了个算法,可以把网页里的div块,td块等都提取出来,支持嵌套的情况。一般只提取div的文字块儿就行了。
3、把汉字少于200的文本块去了,一般少于200字的文本块不会是正文,即便是正文,一般来说也不会有太多的价值,我直接去掉。
4、因为div支持嵌套,所以剩下的文本块,有可能是重复的,一个是另一个的父节点,所以要把最里层的文本块找出来,最里层的文本块肯定是汉字最多的,而其它文本最少的,所以要计算出剩余文本块中汉字占所有字符比例最高的文本块,基本上它就是正文的文本块了。当然有的网页正文里也可能还有div的文本块,这时候可能会判断错误,但只要正文嵌套的Div文本块的汉字少于200字,我的算法还是能准确提取正文文本块的。这一步我用写了一个自定义的方法传递给List的Sort方法。
5、把<p><br>等标签替换成特殊占位符[p][br]等,因为最终的正文需要保留段落和回车换行等格式。这一步用正则实现。
6、把最后剩下的文本块的html标签去掉,我用正则过滤的。
7、吧[p]替换成回车换行加俩空格,吧[br]替换成回车换行,这步也用正则。到此,正文提取完毕
主要代码:
测试:
我测试了几个大网站的页面,效果还可以
新浪博客
http://blog.sina.com.cn/s/blog_4f168ead01008tk3.html
博客园
http://www.cnblogs.com/shinn/archive/2008/04/12/1147473.html
CSDN博客
http://blog.csdn.net/testing_is_believing/archive/2008/04/09/2271195.aspx
新浪新闻
http://news.sina.com.cn/c/2008-04-12/175315343555.shtml
百度空间(提取的比较干净)
http://hi.baidu.com/freepole/blog/item/de8edd09e23f9b87d0581bd8.html
qq新闻(把后面的推荐视频也提取了)
http://finance.qq.com/a/20080412/001783.htm
搜狐新闻
http://news.sohu.com/20080411/n256238690.shtml
新华网新闻
http://news.xinhuanet.com/newscenter/2008-04/12/content_7966283.htm
网易新闻
http://news.163.com/08/0412/00/499PUOR00001124J.html
网易博客
http://cantonsir.blog.163.com/blog/static/84402712008310631830/?fromAmusement
CSDN新闻
http://news.csdn.net/n/20080411/115109.html
源码下载:
https://files.cnblogs.com/onlytiancai/GetMainContent.zip
改进:
1、排序之前把超链接文字数量和汉字数量比例超过百分之50的div去掉,因为这些都是相关链接或者文字广告
2、把title分词(去除停止词,只保留名词和动词)后和剩下的几个div做比较,把内容里没有title分词的文字块去掉,因为正文内容一般都多多少少包含标题里的词。当然如果正文里用的都是比喻,拟人等手法,那也没办法了
3、代码强壮性增强,有些地方可能会抛异常。
4、好多论坛的地址都是一个Frame,所以获取源码的时候得不到目标页面的源码,这个其实可以加一个智能判断,580k就支持。
5、最后提取出的结果有时候不太赶紧,其实可以把最后剩下的文本块再做详细分析,去除掉无关文本。
6、目前只支持div布局的网站,还要加上支持表格布局的网站比如百度贴吧,达到自动检测。
编辑:
1、以上代码有个bug,就是不支持大写的DIV标签,在gettag函数里比较标签的时候用忽略大小写的startwith就尅了,如下
if (biaoqian.StartsWith(string.Format("</{0}", tag),StringComparison.OrdinalIgnoreCase))
2、发现一个bug,对于博客园的帖子,如果正文贴的代码太多,就无法提取了,因为代码都不是汉字,导致正文区域的评分太低,所以没被识别成正文,所以我的算法比较适合于提取中文正文。