热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

抓取之近似网页过滤

抓取的网页内容中,有大部分会是相似的,抓取时就要过滤掉,开始考虑用VSM算法,后来发现不对,要比较太多东西了,然后就发现了simHash算法,这个算法的解释我就懒得copy了,simhash算法对

  抓取的网页内容中,有大部分会是相似的,抓取时就要过滤掉,开始考虑用VSM算法,后来发现不对,要比较太多东西了,然后就发现了simHash算法,这个算法的解释我就懒得copy了,simhash算法对于短数据的支持不好,但是,我本来就是很长的数据,用上!

  源码实现网上也有不少,但是貌似都是同样的,里面写得不清不楚的,虽然效果基本能达到,但是不清楚的东西,我用来做啥?

  仔细研究simhash算法的说明后,把里面字符串的hash算法换成的fvn-1算法,这个在http://www.isthe.com/chongo/tech/comp/fnv/里面有说明了,具体的那些固定数值,网站上都写了。原先代码里面有些处理,和算法不符的,也换掉了。

  首先搞起IKAnalyzer,切词并计算每个词的频率:

package com.cnblogs.zxub.lucene.similarity;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class WordsSpliter {

public static Map getSplitedWords(String str)
throws IOException {
// str = str.replaceAll("[0-9a-zA-Z]", "");
Analyzer analyzer = new IKAnalyzer();
Reader r
= new StringReader(str);
TokenStream ts
= analyzer.tokenStream("searchValue", r);
ts.addAttribute(CharTermAttribute.
class);

Map
result = new HashMap();
while (ts.incrementToken()) {
CharTermAttribute ta
= ts.getAttribute(CharTermAttribute.class);
String word
= ta.toString();
if (!result.containsKey(word)) {
result.put(word,
0);
}
result.put(word, result.get(word)
+ 1);
}

return result;
}
}

   然后把SimHash的算法搞上:

package com.cnblogs.zxub.lucene.similarity;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Map;
import java.util.Set;

public class SimHash {

private static final int HASH_BITS = 64;
private static final BigInteger FNV_64_INIT = new BigInteger(
"14695981039346656037");
private static final BigInteger FNV_64_PRIME = new BigInteger(
"1099511628211");
private static final BigInteger MASK_64 = BigInteger.ONE.shiftLeft(
HASH_BITS).subtract(BigInteger.ONE);

private String hash;
private BigInteger signature;

public SimHash(String content) throws IOException {
super();
this.analysis(content);
}

public String getHash() {
return this.hash;
}

public BigInteger getSignature() {
return this.signature;
}

private void analysis(String content) throws IOException {
Map
wordInfos = WordsSpliter.getSplitedWords(content);
int[] featureVector = new int[SimHash.HASH_BITS];
Set
words = wordInfos.keySet();
for (String word : words) {
BigInteger wordhash
= this.fnv1_64_hash(word);
for (int i = 0; i ) {
BigInteger bitmask = BigInteger.ONE.shiftLeft(SimHash.HASH_BITS
- i - 1);
if (wordhash.and(bitmask).signum() != 0) {
featureVector[i]
+= wordInfos.get(word);
}
else {
featureVector[i]
-= wordInfos.get(word);
}
}
}

BigInteger signature
= BigInteger.ZERO;
StringBuffer hashBuffer
= new StringBuffer();
for (int i = 0; i ) {
if (featureVector[i] >= 0) {
signature
= signature.add(BigInteger.ONE
.shiftLeft(SimHash.HASH_BITS
- i - 1));
hashBuffer.append(
"1");
}
else {
hashBuffer.append(
"0");
}
}
this.hash = hashBuffer.toString();
this.signature = signature;
}

// fnv-1 hash算法,将字符串转换为64位hash值
private BigInteger fnv1_64_hash(String str) {
BigInteger hash
= FNV_64_INIT;
int len = str.length();
for (int i = 0; i ) {
hash = hash.multiply(FNV_64_PRIME);
hash
= hash.xor(BigInteger.valueOf(str.charAt(i)));
}
hash
= hash.and(MASK_64);
return hash;
}

public int getHammingDistance(BigInteger targetSignature) {
BigInteger x
= this.getSignature().xor(targetSignature);
String s
= x.toString(2);
return s.replaceAll("0", "").length();
}

public int getHashDistance(String targetHash) {
int distance;
if (this.getHash().length() != targetHash.length()) {
distance
= -1;
}
else {
distance
= 0;
for (int i = 0; i <this.getHash().length(); i++) {
if (this.getHash().charAt(i) != targetHash.charAt(i)) {
distance
++;
}
}
}
return distance;
}
}

  数据库里面存个签名就好了,至于距离运算,本打算全部拉出来计算,后来发现oracle的bitand函数,就用它了!异或之后,转二进制字符串,把0去掉,取长度,再count一下长度小于4的,得到的结果就是很相似的内容数目了。以后再把计算改成用缓存的去,先偷个懒。

  oracle函数部分贴上(注意Oracle的length函数永远不会返回0,最后要用个nvl函数,还有就是bitand在数值太大的时候,会溢出导致结果失误,所以要用utl_raw.bit_and,后面两个函数中字符串还不能用64位,改成128位搞定,估计还能小点,不弄了): 

create or replace function bitxor(a in number,b in number) return number
is
begin
return return a+b-2*to_number(utl_raw.bit_and(to_char(a),to_char(b)));
end;
create or replace function dec2bit(v_num number) return varchar
is
v_rtn
varchar(128);
v_n1
number;
v_n2
number;
begin
v_n1 :
= v_num;
loop
v_n2 :
= mod(v_n1, 2);
v_n1 :
= trunc(v_n1 / 2);
v_rtn :
= to_char(v_n2) || v_rtn;
exit when v_n1 = 0;
end loop;
return v_rtn;
end;


create or replace function hm_distance(a in number,b in number) return number
is
v_dis
number;
v_xor
number;
v_bit
varchar(128);
begin
v_xor:
=bitxor(a,b);
v_bit:
=dec2bit(v_xor);
v_dis:
=length(replace(v_bit,'0',''));
return nvl(v_dis,0);
end;

  跑一下 select hm_distance(1108937774045716955,1108937774045721051) from dual ,结果为1,o了。

  后面去用了下,发现fnv1居然正好撞到一个神奇的万金油,改成fnv1a就好了,代码就不改了。。。


推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • C语言常量与变量的深入理解及其影响
    本文深入讲解了C语言中常量与变量的概念及其深入实质,强调了对常量和变量的理解对于学习指针等后续内容的重要性。详细介绍了常量的分类和特点,以及变量的定义和分类。同时指出了常量和变量在程序中的作用及其对内存空间的影响,类似于const关键字的只读属性。此外,还提及了常量和变量在实际应用中可能出现的问题,如段错误和野指针。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
author-avatar
8prye孙瑞D
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有