【GOGO闯】:后台留了一堆问题,本篇是对其中两个问题的答疑
正文抽取
在【SEO如何处理采集内容 ①】中的“泛采集”部分提到过正文抽取,然后有一些人依旧表示不知道怎么搞。
这东西用网上开源的就可以,Google搜索“{编程语言}正文提取算法”便能找到一大堆的解决方案,如:Readability、Boilerpipe、Diffbot……大部分算法已经打包好了,拿过来就可以直接用,用不着自己写。我们是做网站的,不是搞技术的,有现成的轮子用就OK了。
那么一些人又有一个问题:我该用哪个好呢?
No No No,这不是用轮子的思维,首先不可能每个算法都能提取所有的网页,其次,算法不止一个。
那这件事就简单了,一个算法没有将当前网页的正文提取出来,好办,不用做别的,直接切下一个算法接着试,这个不行再换下一个,如果网页正常,总有一个能将正文提取出来。除非这个页面模板乱七八糟什么都有,比如网站首页,没有明显的主体内容区块,这个另算。
所以,如果泛采集过程中需要提取正文的链接中,最好先将首页url过滤掉。
如果非要纠结用哪个好,请参考:https://tomazkovacic.com/blog/2011/06/09/evaluating-text-extraction-algorithms/
内容去重
另一个问题,采集到重复的内容咋办?
本渣渣之前用过两个办法。
第一种:
首先我们已经限定有效内容需要满足哪些指标,比如字数必须大于150字,才算有效内容,小于150字的删除不入库。那么大于150字的内容一般都有4个以上标点符号。
XXXXXXX,XXXXXXXXX。XXX:“XXXXXX,XXXXXXXXXXXX。XXXXXX,XXXXXXXXXX,XXXXXXXX,XXXXXX。XXX?”
XXXX,XXXXXXX。XXXXXXX;XXXX;XXXXXXXX;XXXXXX,XXXXXXXXXX,XXXXXXXX,XXXXXX – XXX!
所以每篇文章,从第2个标点符号开始,连续提取两个标点符号之间的文本,且字数大于7的,直至提取3个文本段。
然后将这3个文本段合并成一个,将文本段重复的文章去重,只保留一个。因为连续3个文本段相同的文章基本都是重复的,而且是完全重复,改都没改的。
第二种
用现成的文本去重算法,依旧Google搜索,一堆现成的解决方案,如simhash、Shingling…..
首先对所有抓回来的文本清洗,去除无关词汇,如停止词、助词(的地得..)什么的,然后再通过上述的解决方案来计算相似文档。
哪个好?本渣渣觉得都一般,没觉得哪个好,但都可以凑活用。。。
但都有个问题,一旦文章量大起来,比如上了几百万,程序跑起来很慢,巨烧CPU,怎么办??
于是就沿用第一种办法的思路,不分析全文了,直接找出每篇文章的最长的n句话,做一遍hash签名,然后还是用上述现成的算法去跑,n一般取3。不但运行速度快了很多,找相似文章的最终效果貌似也比之前好了。
以上是从闯爷微信公众号【流量贩子】趴过来的内容
那么本渣渣从python技术实现手段上,用过的提取正文的python模块Readability、github上有!不多说,也就是三行代码
smihash算法python实现,可以直接拿过来跑跑!再加上中文分词库,可以比较好的对中文文章计算hash,中文分词可以使用结巴;github上有!
#!/usr/bin/python # coding=utf-8 class simhash: #构造函数 def __init__(self, tokens='', hashbits=128): self.hashbits = hashbits self.hash = self.simhash(tokens); #toString函数 def __str__(self): return str(self.hash) #生成simhash值 def simhash(self, tokens): v = [0] * self.hashbits for t in [self._string_hash(x) for x in tokens]: #t为token的普通hash值 for i in range(self.hashbits): bitmask = 1 << i if t & bitmask : v[i] += 1 #查看当前bit位是否为1,是的话将该位+1 else: v[i] -= 1 #否则的话,该位-1 fingerprint = 0 for i in range(self.hashbits): if v[i] >= 0: fingerprint += 1 << i return fingerprint #整个文档的fingerprint为最终各个位>=0的和 #求海明距离 def hamming_distance(self, other): x = (self.hash ^ other.hash) & ((1 << self.hashbits) - 1) tot = 0; while x : tot += 1 x &= x - 1 return tot #求相似度 def similarity (self, other): a = float(self.hash) b = float(other.hash) if a > b : return b / a else: return a / b #针对source生成hash值 (一个可变长度版本的Python的内置散列) def _string_hash(self, source): if source == "": return 0 else: x = ord(source[0]) << 7 m = 1000003 mask = 2 ** self.hashbits - 1 for c in source: x = ((x * m) ^ ord(c)) & mask x ^= len(source) if x == -1: x = -2 return x if __name__ == '__main__': s = 'This is a test string for testing' # s=open('test1.txt').read() hash1 = simhash(s.split()) s = 'This is a test string for testing also' # s=open('test2.txt').read() hash2 = simhash(s.split()) s = 'nai nai ge xiong cao' # s=open('test3.txt').read() hash3 = simhash(s.split()) print(hash1.hamming_distance(hash2) , " " , hash1.similarity(hash2)) print(hash1.hamming_distance(hash3) , " " , hash1.similarity(hash3))