前一段时间,因公司需求需要爬取大量的新闻网站(2000多个新闻网址),做过爬虫的小伙伴们,当听到这个需求的时候,内心估计早已翻滚!!!起飞吧骚年。好了就不闲扯了,毕竟我们搞技术的,我们还是要回归技术,用技术成就我们。


2000多个网址,可想而知,如果单纯的使用 xpath 进行解析,那就只能是望洋兴叹,独自掉下没有忧愁的发丝,工作量就太巨大了。


起初找到了自动化解析页面的神库 readbility 库,起初使用这个库还行,对于一些标准格式的html网页,解析还是很不错的,但是遇到一些不规则的html 网页,解析率就不太友好了,尝试二次开发,但效果不太理想,同时该算法需要解析 DOM 树,因此时间复杂度和空间复杂度较高,提取的网页正文的时间比较长。果断放弃。


经过一段时间的研究,在论文海中排查出了今天的主角--基于文本及符号密度的网页正文提取方法,经过测试,准确率符合项目的实际开发需求。


进入正题:

现在大多数的网站的网页除了主要的内容,还包含导航栏,广告,版权等无关信息。这些额外的,内容亦被称为噪声,通常与主题无关。


由于这些噪声会妨碍搜索引擎对 Web 数据的挖掘性能,所以需要过滤噪声。在这个论文中,算法是基于网页文本密度与符号密度对网页进行正文内容提取,这是一种快速,准确通用的网页提取算法,而且还可以保留原始结构。


该算法的实现思路来源:


现在大多数正文内容一般在网页的:


1. <body>标签内的<p>标签中;

2. <p>标签一般都为<div>、<td>等标签的子标签,并不独立存在。


同时,在分析的过程中也发现了一些问题:


1. 各个网站对标签的 class,id 等属性名称命名不统一。


2. 网页正文内容中可能有超链接,其可能是文字也可能是图片,标签<p>中 ,不一定就是内容,还可能还会其他标签。


3. <p>标签之间可能还会有 JS 脚本或者是其他的 html 标签。


4. <p>标签的内容不全都是或不一定是正文内容等。网站的版权信息一般也 是<p>标签的内容,但这部分内容属于噪声。


通过文献[基于块密度加权标签路径特征的 Web 新闻在线抽取[J]]。


我们知道可以用每行的文本密度来判断这一行属不属于正文。我们把网页解析成 Dom 树,然后判断每个节点属于正文内容的可能性是多少。每个网页都可以被解析成一颗 Dom 树,所有的标签都是节点,而文字和图片等都是叶子节点。


通过这样的思路得出了以下的算法:


1. TDi = (Ti - LTi)/(TGi - LTGi)

2. SbDi = (Ti - LTi)/( Sbi + 1)

3. score = log(SD)*NDi* log10(PNumi + 2)* log(SbDi)


最终获取 score 的最大值就是网页正文内容。算法实现在下面进行解释。


该算法程序实现的架构图:


           爬虫之-如何抽取上千家新闻网站正文_JAVA


一:预处理去除网页中的噪声(如:script , style 等标签)

经验告诉你,这里的预处理非常的重要,预处理对噪声排除的好,格式化的内容就会越好。


代码实现思路:

1. 先将网页内容转化为 HtmlElement 对象(利用 lxml.html 的fromstring()方法)

2. 接下来就可以操作这个对象进行一系列的自定义操作,PS :







import requestsfrom lxml.html import fromstringelement = fromstring(requests.get(url))#删除script style标签etree.strip_elements(element, ["script","style"])


部分代码:


爬虫之-如何抽取上千家新闻网站正文_java_02


二:预处理后,进行函数计算:

计算每一个节点的要求的变量(进行对应的数学计算)

数学计算分为三步:

1.TDi计算

2.sbDi计算

3.score计算


1.计算文本密度TDI的值


TDI的作用:TDI是衡量一个网页的每个结点文本密度,如果一个结点的纯文本字数比带链接的文本字数明显多很多的时候,可通过TDI的值来判定该结点的文本密度就会很大,从而就可以很容易判断该节点是不是正文的一部分。


公式:

TDi = (Ti - LTi)  / (TGi - LTGi)

参数:

Ti-->节点i的字符串数 即就是节点i的文本长度

LTi-->节点i带链接的字符串数 即就是节点i带链接的文本的长度

TGi-->节点i的标签数

LTGi-->节点i带链接的标签数


步骤:

1.得到节点

2.计算节点的文本长度

3.计算节点带链接的文本长度

4.计算节点的标签数

5.计算节点带链接的标签数


部分代码:

       


2. 计算文本中符号密度 sbdi 值

sbdi 是为文字数量与符号数量的比值,是为了下面的 score 数学模型做优化。

需要注意一个点如果sbdi为0时,需要设置为1, 后面取对数会报错

公式:

sbdi = (ti - lti) / (sbi + 1)

参数:

sbi-->文本中符号的数量

部分代码:

     爬虫之-如何抽取上千家新闻网站正文_java_03


3. 计算 score 的值

因为繁多的网页采用的布局各不相同,所以如果想要一个算法可以通用提取不同的网页,我们需要考虑的因素还有很多,于是我们建立了一个数学模型,这个模型就是 score。score 的作用就是在 tdi 的基础上在做一步处理。

公式:

score = log(sd)*ndi*log10(pnumi + 2)*log(sbdi)

参数:

 sd-->ndi的标准差

ndi-->文本密度

pnumi-->节点的p标签数

部分代码:

     爬虫之-如何抽取上千家新闻网站正文_JAVA_04


最后通过算法计算后得出最大的值及就是我们要的文本内容,可以输出文本,也可以输出html。


效果展示:


爬虫之-如何抽取上千家新闻网站正文_java_05


可以看到效果还是很明显的。