写这篇文章的目的主要是总结一下目前知道的去重方法。文章有点杂乱看着参考。
常见URL过滤方法
第一,基于磁盘的顺序存储。
这里,就是指把每个已经下载过的URL进行顺序存储。你可以把全部已经下载完成的URL存放到磁盘记事本文件中。每次有一个爬虫线程得到一个任务URL开始下载之前,通过到磁盘上的该文件中检索,如果没有出现过,则将这个新的URL写入记事本的最后一行,否则就放弃该URL的下载。
这种方式几乎没有人考虑使用了,但是这种检查的思想是非常直观的。试想,如果已经下载了100亿网页,那么对应着100亿个链接,也就是这个检查URL是否重复的记事本文件就要存储这100亿URL,况且,很多URL字符串的长度也不小,占用存储空间不说,查找效率超级低下,这种方案肯定放弃。
第二 入数据库查询比较
即假设要存储url A,在入库前首先查询url库中是否存在 A,如果存在,则url A 不入库,否则存入url库。这种方法准确性高,但是一旦数据量变大,占用的存储空间也变大,同时,由于要查库,数据一多,查询时间变长,存储效率下降。
第三 MD5入数据库
在第二种的情况之下可以减少内存的消耗,一定程度上提高了效率。毕竟:直接用MD5对URL做编码。MD5的结果是128 bit也就是16 byte的长度。相比于之间估计的URL平均长度100byte已经缩小了好几倍,可以多撑好多天了。
当然,哪怕找个一个可以压缩到极致的算法,随着URL越来越多,终有一天会Out Of Memory。所以,这个方案不解决本质问题。
第四 Set 集合存储
同样建议MD5后储存到Set集合中,毕竟可以很大程度上减少内存。
第五,基于布隆过滤器(Bloom Filter)的存储。
使用布隆过滤器,设计多个Hash函数,也就是对每个字符串进行映射是经过多个Hash函数进行映射,映射到一个二进制向量上,这种方式充分利用了比特位。
基于内存的HashSet的方法存在一个本质的问题,就是它消耗的内存是随着URL的增长而不断增长的。除非能够保证内存的大小能够容纳下所有需要抓取的URL,否则这个方案终有一天会到达瓶颈。
以上自己大多数的方法已经实现过了,在数据量较少的情况下都可以达到很好的效果。但是在数据量较大的情况下,采集速度还是很不乐观的,今天就分享一下自己在网上看大佬写的一个基于布隆过滤器和redis结合自己的写的爬虫看一下效果。
参考网站:https://blog.csdn.net/hellozhxy/article/details/80942581
https://blog.csdn.net/a1368783069/article/details/52137417
先讲一下布隆过滤器的实现吧,原理我自己从网上看的很多分享页大概了解了,你们也可以自行去网上看一些原理这里就不分享了,可以给你们分享我所参考的大佬博客:
https://blog.csdn.net/a1368783069/article/details/52137417
直接上代码结构吧
这是我的代码结构
bloom_filter 代码如下:
# encoding=utf-8
import redis
from hashlib import md5
class SimpleHash(object):
def __init__(self, cap, seed):
self.cap = cap
self.seed = seed
def hash(self, value):
ret = 0
for i in range(len(value)):
ret += self.seed * ret + ord(value[i])
return (self.cap - 1) & ret
class BloomFilter(object):
def __init__(self, host='localhost', port=6379, db=0, blockNum=2, key='bloomfilter'):
"""
:param host: the host of Redis
:param port: the port of Redis
:param db: witch db in Redis
:param blockNum: one blockNum for about 90,000,000; if you have more strings for filtering, increase it.
:param key: the key's name in Redis
"""
self.server = redis.Redis(host=host, port=port, db=db)
self.bit_size = 1 << 31 # Redis的String类型最大容量为512M,现使用256M
self.seeds = [5, 7, 11, 13, 31, 37, 61]
self.key = key
self.blockNum = blockNum
self.hashfunc = []
for seed in self.seeds:
self.hashfunc.append(SimpleHash(self.bit_size, seed))
def isContains(self, str_input):
if not str_input:
return False
m5 = md5()
str_input = str_input.encode('utf-8')
m5.update(str_input)
str_input = m5.hexdigest()
ret = True
name = self.key + str(int(str_input[0:2], 16) % self.blockNum)
for f in self.hashfunc:
loc = f.hash(str_input)
ret = ret & self.server.getbit(name, loc)
return ret
def insert(self, str_input):
m5 = md5()
str_input = str_input.encode('utf-8')
m5.update(str_input)
str_input = m5.hexdigest()
name = self.key + str(int(str_input[0:2], 16) % self.blockNum)
for f in self.hashfunc:
loc = f.hash(str_input)
self.server.setbit(name, loc, 1)
if __name__ == '__main__':
""" 第一次运行时会显示 not exists!,之后再运行会显示 exists! """
bf = BloomFilter()
if bf.isContains('http://www.66.com'): # 判断字符串是否存在
print('exists!')
else:
print('not exists!')
bf.insert('http://www.66.com')
写的那么好肯定是转载别人的
转载博客:https://blog.csdn.net/bone_ace/article/details/53107018 大佬勿怪 替你博客做个广告 可以关注这位大佬。
tian_tang_img 文件代码
import os
import time
import requests
from scrapy import Selector
from fake_useragent import UserAgent
from fanyu_spider.bloom_filter import *
class TianTangSpider:
def __init__(self):
self.bf = BloomFilter() # 初始布隆过滤器
self.md = md5() # 初始化一个md5对象
self.ua = UserAgent() # 随机请求头的设置
self.key_world = input('Please input the content of the picture you want to grab >:')
self.img_path = r'D:\Image\TianTangImgDownLoad\mask' # 下载到的本地目录
if not os.path.exists(self.img_path): # 路径不存在时创建一个
os.makedirs(self.img_path)
self.url = 'http://www.ivsky.com/search.php?q=%s' % self.key_world
def start_crawl(self):
print('being grabbed %s contents of page' % self.url)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36'
}
try:
start_response = requests.get(url=self.url, headers=headers)
start_response = Selector(text=start_response.text)
all_img_url = start_response.xpath('//img/@src').extract()
except BaseException as e:
print(e)
else:
self.img_down(all_img_url)
next_page_url = start_response.xpath('//a[@class="page-next"]/@href').extract_first()
if next_page_url: # 判断是否存在下一页了。
self.url = 'http://www.ivsky.com/' + next_page_url
self.start_crawl()
else:
print('采集结束')
def img_down(self, all_img_url):
for each_img_url in all_img_url:
if self.bf.isContains(each_img_url):
print('this %s url is exists!' % each_img_url)
continue
else:
try:
self.bf.insert(each_img_url) # 插入redis数据库方便分布式管理爬取,当然你也可以选择不插入
except BaseException as e:
print('插入第 %s 个urls 报错 请检查' % each_img_url)
else:
headers = {'User-Agent': self.ua.random}
try:
img_response = requests.get(url=each_img_url, headers=headers)
time.sleep(2)
except BaseException as e:
time.sleep(5)
print(e)
self.proxies = self.random_agent()
print('\n' + '*' * 500 + '\n' + ('this %s url is flase' % each_img_url))
continue
else:
with open('%s/%s.jpg' % (self.img_path, time.time()), 'wb')as f:
f.write(img_response.content)
if __name__ == '__main__':
tian_tang = TianTangSpider()
tian_tang.start_crawl()
以上做个简单分享吧,后续可能自己会看。如有错误指正。