Scrapy-redis增量爬取以及Simhash相似文档的去重

来源:互联网 发布:公文写作神器软件 编辑:程序博客网 时间:2024/06/09 03:14

最近在实习,第一个任务就是从各大门户网站抓取新闻,爬虫本身不是一个很难的事情,用scrapy框架很容易完成(关于scrapy的具体用法可以参考我之前的一篇博客http://blog.csdn.net/john_xyz/article/details/78157805, 但是由于要求是要增量爬取,而且要去除相似的新闻,这里记录一下解决问题的方法以及踩过的坑

Scrapy-redis增量爬取

Scrapy-redis是在Scrapy的的基础上,提供了一些一redis为基础的组件。具体的,提供了Schedule, Dupefilter, Pipeline, Spider。有兴趣的同学可以看看源码 darkrho/scrapy-redis · GitHub 。
Scrapy在爬取的过程当中,有一个主要的数据结构是“待爬队列”,用python自带的collection.deque来存储,以及能够操作这个队列的调度器。
Scrapy-redis把deque换成redis数据库。因为爬虫程序需要每天定时爬取,因此,在redis中,每个网站(key)都有个判重池(dupefilter),该判重池存储这爬取过的url。这样在爬取的时候,当待抓取的url在判重池里的时候,就不去抓新闻了,否则,就抓取新闻,并将url加入判重池.

Simhash相似文档的去重

Simhash(http://www2007.org/papers/paper215.pdf)是google在2007年提出的海量文本去重算法。其核心思想是将一篇文档转换成64位的比特,判断文档是否相似就是判断它们最后64位比特海明距离是否小于k(k一般取3),就可以判断两篇文档是否相似。需要说明的是Simhash是局部敏感哈希,也就是说,相似的文档或单词,其哈希值也是相似的
这里写图片描述
算法流程:
1. 对Doc进行关键词抽取(分词和计算TF-IDF权重),抽出n个关键词[(wod1, weight1), (word2, weight2), …,(wordn, weightn)]
2. 对每个word,计算哈希值 hash_weight_pairs = [(hash(word1), weight), (hash(word2), weight2),…, (hash(wordn), weightn)],每个单词被hash成默认64位的二进制比特。
3. 对hash_weight_pairs进行纵向加权求和,如果该位是1,则+weight,如果是0,则-weight,最后生成长度位64的数组
4.遍历这64位的数组,如果该位为正,则转换成1,否则位0

海明距离的计算

举个简单例子来说明

A = [1,0,0,1,1,1]b = [1,0,0,1,1,0]

A和B的海明距离是1,就是A xor B后二进制1的个数。
实际在做去重时,一般取k=3, 也就是海明距离小于3,认为两篇文档相似

算法的几何意义和原理

随机超平面hash算法

Simhash是由随机超平面hash算法演变而来的,随机超平面hash算法非常简单,对于一个n维向量v,要得到一个f位的签名(f << n),算法如下:

1,随机产生f个n维的向量r1,…rf;
2,对每一个向量ri,如果v与ri的点积大于0,则最终签名的第i位为1,否则为0.

这个算法相当于随机产生了f个n维超平面,每个超平面将向量v所在的空间一分为二,v在这个超平面上方则得到一个1,否则得到一个0,然后将得到的f个0或1组合起来成为一个f维的签名

Simhash算法和随机超平面hash算法之间的联系

Simhash算法与随机超平面hash是怎么联系起来的呢?在simhash算法中,并没有直接产生用于分割空间的随机向量,而是间接产生的:第 k个特征的hash签名的第i位拿出来,如果为0,则改为-1,如果为1则不变,作为第i个随机向量的第k维。由于hash签名是f位的,因此这样能产生 f个随机向量,对应f个随机超平面。下面举个例子:

假设用5个特征w1,…,w5来表示所有文档,现要得到任意文档的一个3维签名。假设这5个特征对应的3维向量分别为:

h(w1) = (1, -1, 1)T
h(w2) = (-1, 1, 1)T
h(w3) = (1, -1, -1)T
h(w4) = (-1, -1, 1)T
h(w5) = (1, 1, -1)T

按simhash算法,要得到一个文档向量d=(w1=1, w2=2, w3=0, w4=3, w5=0) T的签名,
先要计算向量m = 1*h(w1) + 2*h(w2) + 0*h(w3) + 3*h(w4) + 0*h(w5) = (-4, -2, 6) T,然后根据simhash算法,得到最终的签名s=001。
上面的计算步骤其实相当于,先得到3个5维的向量,第1个向量由h(w1),…,h(w5)的第1维组成:
r1=(1,-1,1,-1,1) T;
第2个5维向量由h(w1),…,h(w5)的第2维组成:
r2=(-1,1,-1,-1,1) T;
同理,第3个5维向量为:
r3=(1,1,-1,1,-1) T.

按随机超平面算法的步骤2,分别求向量d与r1,r2,r3的点积:
d * r1=-4 < 0,所以s1=0;
d * r2=-2 < 0,所以s2=0;
d * r3=6 > 0,所以s3=1.

故最终的签名s=001,与simhash算法产生的结果是一致的。

从上面的计算过程可以看出,simhash算法其实与随机超平面hash算法是相同的,simhash算法得到的两个签名的汉明距离,可以用来衡量原始向量的夹角。这其实是一种降维技术,将高维的向量用较低维度的签名来表征。衡量两个内容相似度,需要计算汉明距离,这对给定签名查找相似内容的应用来说带来了一些计算上的困难;我想,是否存在更为理想的simhash算法,原始内容的差异度,可以直接由签名值的代数差来表示呢?

利用Simhash进行海量文本的去重

使用上述方法产生的simhash可以用来比较两个文本之间的相似度。问题是,如何将其扩展到海量数据的近重复检测中去呢?譬如说对于64位的待查询文本的simhash code来说,如何在海量的样本库(>1M)中查询与其海明距离在3以内的记录呢?下面在引入simhash的索引结构之前,先提供两种常规的思路。第一种是方案是查找待查询文本的64位simhash code的所有3位以内变化的组合,大约需要四万多次的查询,参考下图:
此处输入图片的描述
另一种方案是预生成库中所有样本simhash code的3位变化以内的组合,大约需要占据4万多倍的原始空间,参考下图:
此处输入图片的描述
显然,上述两种方法,或者时间复杂度,或者空间复杂度,其一无法满足实际的需求。我们需要一种方法,其时间复杂度优于前者,空间复杂度优于后者。

假设我们要寻找海明距离3以内的数值,根据抽屉原理,只要我们将整个64位的二进制串划分为4块,无论如何,匹配的两个simhash code之间至少有一块区域是完全相同的,如下图所示:
此处输入图片的描述
由于我们无法事先得知完全相同的是哪一块区域,因此我们必须采用存储多份table的方式。在本例的情况下,我们需要存储4份table,并将64位的simhash code等分成4份;对于每一个输入的code,我们通过精确匹配的方式,查找前16位相同的记录作为候选记录,如下图所示:
此处输入图片的描述让我们来总结一下上述算法的实质:
1、将64位的二进制串等分成四块
2、调整上述64位二进制,将任意一块作为前16位,总共有四种组合,生成四份table
3、采用精确匹配的方式查找前16位
4、如果样本库中存有2^34(差不多10亿)的哈希指纹,则每个table返回2^(34-16)=262144个候选结果,大大减少了海明距离的计算成本

我们可以将这种方法拓展成多种配置,不过,请记住,table的数量与每个table返回的结果呈此消彼长的关系,也就是说,时间效率与空间效率不可兼得,参看下图:
此处输入图片的描述
事实上,这就是Google每天所做的,用来识别获取的网页是否与它庞大的、数以十亿计的网页库是否重复。另外,simhash还可以用于信息聚类、文件压缩等。
由于文本已经压缩成8个字节了,因此其实Simhash近似查重精度并不高:
此处输入图片的描述

项目地址及经验总结

https://github.com/Johnson0722/News_scrapy_redis

原创粉丝点击