网络爬虫URL消重
2015-08-06 20:22
483 查看
在搜索引擎中建立url检测机制,如果一个url被爬取过就记录下来,在爬取新的url之前先和url库中的资源进行对比,如果没有该记录,则正常解析爬取资源,如果有则忽略该url。接下来考虑的就是如何让这个去重的过程更高效的问题。
考虑了如下几个方案:
1 记录爬取过得每一个url,并按顺序存储在数据库中,当爬虫得到新的url之后,先和数据库中已有的url进行对比,如果没有该记录,则进行爬取,爬取成功后把该url追加进数据库中。如果数据库中已有该记录,则忽略该url。这个方案简单直观,问题是如果url数据库很大的话,整个url匹配过程很费时间,如果量不多,可以考虑使用内存数据库,redis之类的带有持久存储功能的数据库。
2 对url进行hash运算映射到某个地址,将该url和hash值当做键值对存放到hash表中,当需要检测新的url的时候,只需要对该url进行hash映射,如果得到的地址在hash表中已经存在,则说明已经被爬取过,则放弃爬取,否则,进行爬取并记录键值对。这样只需要维护一个hash表即可,需要考虑的问题是hash碰撞的问题,互联网上数据如瀚海般,如果hash函数设计不当,碰撞还是很容易发生的。
3 URL采用MD5加密,md5也是采用了基于hash算法,MD5算法能够将任何字符串压缩为128位整数,并映射为物理地址,MD5也是经过时间验证的,MD5进行Hash映射碰撞的几率非常小。我们目前的图片的存储中就用了该方案。
4 采用布隆过滤器,它是一种space efficient的概率型数据结构,用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等经常被用到。哈希表也能用于判断元素是否在集合中,但是布隆过滤器只需要哈希表的1/8或1/4的空间复杂度就能完成同样的问题。布隆过滤器可以插入元素,但不可以删除已有元素。其中的元素越多,false
positive rate(误报率)越大,但是false negative (漏报)是不可能的。
附布隆过滤器的简单实现:
import java.util.BitSet;
/**
*
* @author 梦里花开
*/
public class BloomFilter {
private static final int DEFAULT_SIZE = 2 << 24;//布隆过滤器的比特长度
private static final int[] seeds = {3,5,7, 11, 13, 31, 37, 61};//这里要选取质数,能很好的降低错误率
private static BitSet bits = new BitSet(DEFAULT_SIZE);
private static SimpleHash[] func = new SimpleHash[seeds.length];
public static void addValue(String value)
{
for(SimpleHash f : func)//将字符串value哈希为8个或多个整数,然后在这些整数的bit上变为1
bits.set(f.hash(value),true);
}
public static void add(String value)
{
if(value != null) addValue(value);
}
public static boolean contains(String value)
{
if(value == null) return false;
boolean ret = true;
for(SimpleHash f : func)//这里其实没必要全部跑完,只要一次ret==false那么就不包含这个字符串
ret = ret && bits.get(f.hash(value));
return ret;
}
public static void main(String[] args) {
String value = "xkeyideal@gmail.com";
for (int i = 0; i < seeds.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
add(value);
System.out.println(contains(value));
}
}
class SimpleHash {//这玩意相当于C++中的结构体
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
public int hash(String value) {//字符串哈希,选取好的哈希函数很重要
int result = 0;
int len = value.length();
for (int i = 0; i < len; i++) {
result = seed * result + value.charAt(i);
}
return (cap - 1) & result;
}
}
考虑了如下几个方案:
1 记录爬取过得每一个url,并按顺序存储在数据库中,当爬虫得到新的url之后,先和数据库中已有的url进行对比,如果没有该记录,则进行爬取,爬取成功后把该url追加进数据库中。如果数据库中已有该记录,则忽略该url。这个方案简单直观,问题是如果url数据库很大的话,整个url匹配过程很费时间,如果量不多,可以考虑使用内存数据库,redis之类的带有持久存储功能的数据库。
2 对url进行hash运算映射到某个地址,将该url和hash值当做键值对存放到hash表中,当需要检测新的url的时候,只需要对该url进行hash映射,如果得到的地址在hash表中已经存在,则说明已经被爬取过,则放弃爬取,否则,进行爬取并记录键值对。这样只需要维护一个hash表即可,需要考虑的问题是hash碰撞的问题,互联网上数据如瀚海般,如果hash函数设计不当,碰撞还是很容易发生的。
3 URL采用MD5加密,md5也是采用了基于hash算法,MD5算法能够将任何字符串压缩为128位整数,并映射为物理地址,MD5也是经过时间验证的,MD5进行Hash映射碰撞的几率非常小。我们目前的图片的存储中就用了该方案。
4 采用布隆过滤器,它是一种space efficient的概率型数据结构,用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等经常被用到。哈希表也能用于判断元素是否在集合中,但是布隆过滤器只需要哈希表的1/8或1/4的空间复杂度就能完成同样的问题。布隆过滤器可以插入元素,但不可以删除已有元素。其中的元素越多,false
positive rate(误报率)越大,但是false negative (漏报)是不可能的。
附布隆过滤器的简单实现:
import java.util.BitSet;
/**
*
* @author 梦里花开
*/
public class BloomFilter {
private static final int DEFAULT_SIZE = 2 << 24;//布隆过滤器的比特长度
private static final int[] seeds = {3,5,7, 11, 13, 31, 37, 61};//这里要选取质数,能很好的降低错误率
private static BitSet bits = new BitSet(DEFAULT_SIZE);
private static SimpleHash[] func = new SimpleHash[seeds.length];
public static void addValue(String value)
{
for(SimpleHash f : func)//将字符串value哈希为8个或多个整数,然后在这些整数的bit上变为1
bits.set(f.hash(value),true);
}
public static void add(String value)
{
if(value != null) addValue(value);
}
public static boolean contains(String value)
{
if(value == null) return false;
boolean ret = true;
for(SimpleHash f : func)//这里其实没必要全部跑完,只要一次ret==false那么就不包含这个字符串
ret = ret && bits.get(f.hash(value));
return ret;
}
public static void main(String[] args) {
String value = "xkeyideal@gmail.com";
for (int i = 0; i < seeds.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
add(value);
System.out.println(contains(value));
}
}
class SimpleHash {//这玩意相当于C++中的结构体
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
public int hash(String value) {//字符串哈希,选取好的哈希函数很重要
int result = 0;
int len = value.length();
for (int i = 0; i < len; i++) {
result = seed * result + value.charAt(i);
}
return (cap - 1) & result;
}
}
相关文章推荐
- Http网络传递参数中文乱码问题
- 网络安全专有(一个)
- 网络爬虫 HTML的高级解析 <web scraping with python>第二章
- 游戏引擎网络开发者的64做与不做(二A):协议与API
- TCP即时小通信
- 虚拟机的网络配置
- TCP连接端口的状态
- 神经网络:caffe特征可视化的代码样例
- centos网络设置
- XMLHttpRequest Level 2 使用指南
- HttpClient 连接网络的两种方法doGet和doPost
- 【linux高级程序设计】(第十五章)UDP网络编程应用 4
- 网络处理2-异步POST请求和同步请求
- 网络处理1-异步GET请求
- TCP/IP 笔记 7 Ping
- 预防黑客入侵 防黑必学的cmd命令vs网络安全
- tcp服务器组包
- 网络通信之Socket与LocalSocket的比较
- jgraphx中语言文件配置
- http://www.blogjava.net/zJun/archive/2006/06/28/55511.html