爬虫系列目录
配套视频地址:https://www.wotiecity.com/system/course/MTc=?cid=93332355
那么,我相信初学的小伙伴现在一定是似懂非懂的。那么下面我们通过一个案例来慢慢进行分析,具体如下:
今天,我们的目标是一个图片网站,https://www.quanjing.com/tupian/meinv-1.html
首先,我们第一步需要做的就是项目分析,我们来看看爬取这个网站我们需要哪些步骤。
需求就是将该网站中所有的美女图片分类下载到本地。
如果我们需要下载上面所表示的所有的图片的话,我们需要如下几个步骤:
# -*- coding: utf-8 -*-
import requests
from lxml import etree
import urllib3
urllib3.disable_warnings()
def getClassification(num):
"""
获取分类链接
:return:
"""
url = f"https://www.quanjing.com/tupian/meinv-{num}.html"
html = sendRequest(url, "get")
htmlValus = htmlAnalysis(html.text, '//*[@id="gallery-list"]/li')
for item in htmlValus:
imgUrl = item.xpath('./a/img/@src')[0]
downLoad(imgUrl)
def downLoad(url):
"""
下载图片
:param url:
:return:
"""
img = sendRequest(url)
imgName = url.split("@")[0].split("/")[-1]
with open("./quanjing/" + imgName, 'wb') as imgValue:
imgValue.write(img.content)
def htmlAnalysis(html, rule):
"""
根据 xpath 获取数据
:param html:
:param rule:
:return:
"""
htmlValues = etree.HTML(html)
value = htmlValues.xpath(rule)
return value
def sendRequest(url, method="get"):
"""
发送请求
:param url:
:param method:
:return:
"""
if method.lower() == "get":
html = requests.get(url=url, headers=getHeader(), verify=False)
elif method.lower() == "post":
html = requests.post(url=url, headers=getHeader())
else:
html = None
return html
def getHeader():
"""
获取Header
:return:
"""
ua_headers = {
"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
"referer": "https://www.quanjing.com/"
}
return ua_headers
def main():
getClassification(1)
if __name__ == '__main__':
main()
def main():
for i in range(10):
getClassification(i)
def downLoad(url):
"""
下载图片
:param url:
:return:
"""
img = sendRequest(url)
imgName = url.split("@")[0].split("/")[-1]
with open("./quanjing/" + imgName, 'wb') as imgValue:
imgValue.write(img.content)
配套视频地址:https://www.wotiecity.com/catalogue/MTc=/index.html?cid=93332355
Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。
下面我们给出一个 Scrapy 的架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qt7eRegs-1661835244698)(…/…/.vuepress/public/img/无标题-2022-07-21-1250.svg)]
上面的架构图明确的说明了 Scrapy 主要有 5 个部分。
其实除了上述的内容外,Scrapy 还提供一些中间件,例如:下载器中间件(Downloader Middlewares)和爬虫中间件(Spider Middlewares)等。
所以,把上面完整的图可以画成如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MM5ShgB9-1661835244701)(https://img2022.cnblogs.com/blog/1143853/202208/1143853-20220817143142581-267267330.svg)]
在命令行模式下使用pip命令即可安装。
$ pip install scrapy
第一步:创建一个scrapy项目
$ scrapy startproject mySpider
第二步:生成一个爬虫
$ cd mySpider
$ scrapy genspider tupian https://www.quanjing.com/
#1 查看帮助
scrapy -h
scrapy <command> -h
#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要
Global commands:
startproject #创建项目
genspider #创建爬虫程序
settings #如果是在项目目录下,则得到的是该项目的配置
runspider #运行一个独立的python文件,不必创建项目
shell #scrapy shell url地址 在交互式调试,如选择器规则正确与否
fetch #独立于程单纯地爬取一个页面,可以拿到请求头
view #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
version #查看scrapy的版本
Project-only commands:
crawl #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
check #检测项目中有无语法错误
list #列出项目中所包含的爬虫名
edit #编辑器,一般不用
parse #scrapy parse url地址 --callback 回调函数
bench #scrapy bentch压力测试
#3 官网链接
https://docs.scrapy.org/en/latest/topics/commands.html
import scrapy
from ..items import MyspiderItem
class TupianSpider(scrapy.Spider):
# 定义爬虫名,scrapy会根据该值定位爬虫程序,所以它必须要有且必须唯一
name = 'tupian'
# 定义允许爬取的域名,如果OffsiteMiddleware启动(默认就启动),
# 那么不属于该列表的域名及其子域名都不允许爬取
allowed_domains = ['www.quanjing.com']
# 如果没有指定url,就从该列表中读取url来生成第一个请求
start_urls = ['https://www.quanjing.com/tupian/meinv.html']
# 爬虫启动函数,必须定义成这个函数名称
def parse(self, response):
imgList = response.xpath('//*[@id="gallery-list"]/li')
for item in imgList:
imgSrcValue = item.xpath('./a/img/@src').get()
yield MyspiderItem(img_url=imgSrcValue)
from itemadapter import ItemAdapter
class MyspiderPipeline:
def process_item(self, item, spider):
print(item)
return item
# 设置日志的级别
LOG_LEVEL = "WARNING"
# 关闭 Robots.txt 协议
ROBOTSTXT_OBEY = False
# 开启 pipelines
ITEM_PIPELINES = {
'mySpider.pipelines.MyspiderPipeline': 300,
}
from scrapy import cmdline
# 方法 1
cmdline.execute('scrapy crawl yourspidername'.split())
# 方法 2
sys.argv = ['scrapy', 'crawl', 'down_info_spider']
cmdline.execute()
配套视频地址:https://www.wotiecity.com/system/course/MTc=?cid=93332355
总结 Spider 主要有三个作用,分别是:链接配置、抓取逻辑和解析逻辑。
Spider 的整个爬取循环过程如下:
Spider 继承自 scrapy.spiders.Spider
。scrapy.spiders.Spider
这个类是最简单最基本的 Spider 类,其他 Spider 必须继承这个类。
scrapy.spiders.Spider
类提供了start_requests()
方法的默认实现,读取并请求 start_urls
属性 ,并根据返回的结果调用 parse()
方法解析结果 。
它有如下一些基础属性:
name:爬虫名称,是定义 Spider 名字的字符串。Spider 的名字定义了 Scrapy 如何定位并初始化 Spider ,它必须是唯一的。不过我们可以生成多个相同的 Spider 实例,数量没有限制。name 是 Spider 最重要的属性 。 如果 Spider 爬取单个网站, 一个常见的做法是以该网站的域名名称来命名 Spider。 例如, Spider 爬取 mywebsite.com , 该 Spider通常会被命名为 mywebsite。
allowed_domains
:允许爬取的域名,是可选配置,不在此范围的链接不会被跟进爬取 。
start_urls
:它是起始 URL 列表,当我们没有实现start_requests()
方法时,默认会从这个列表开始抓取。
custom_settings
:它是一个字典,是专属于本 Spider 的配置,此设置会覆盖项目全局的设置。此设置必须在初始化前被更新,必须定义成类变量。
不同爬虫pipeline设置
custom_settings = {
'ITEM_PIPELINES': {
'video.pipelines.VideoPipeline': 301,
}
}
crawler
:它是由 from_crawler()
方法设置的,代表的是本 Spider 类对应的 Crawler 对象 。Crawler 对象包含了很多项目组件,利用它我们可以获取项目的一些配置信息,如最常见的获取项目的设置信息,即 Settings。
settings
:它是一个 Settings 对象,利用它我们可以直接获取项目的全局设置变量 。
除了基础属性,Spider 还有一些常用的方法。
start_requests()
:此方法用于生成初始请求,它必须返回一个可迭代对象,该方法可以被重写。此方法会默认使用 start_urls 里面的 URL 来构造 Request,而且 Request 默认是 GET 请求方式。如果我们想在启动时以 POST方式访问某个站点,可以直接重写这个方法,发送 POST请求时使用 FormRequest
即可 。parse(response)
:当 Response 没有指定回调函数时,该方法会默认被调用 。 它负责处理 Response 处理返回结果,并从中提取出想要的数据和下一步的请求,然后返回。该方法需要返回一个包含 Request
或 ltem
的可迭代对象。closed(reason)
:当 Spider 关闭时,该方法会被调用,在这里一般会定义释放资源的一些操作或其他收尾操作。import scrapy
from ..items import MyspiderItem
class TupianSpider(scrapy.Spider):
# 定义爬虫名,scrapy会根据该值定位爬虫程序,所以它必须要有且必须唯一
name = 'tupian'
# 定义允许爬取的域名,如果OffsiteMiddleware启动(默认就启动),
# 那么不属于该列表的域名及其子域名都不允许爬取
allowed_domains = ['www.quanjing.com']
# 如果没有指定url,就从该列表中读取url来生成第一个请求
start_urls = ['https://www.quanjing.com/tupian/meinv.html']
def start_requests(self):
"""
开始请求之前的执行
:return:
"""
print("我是开始")
yield scrapy.Request(
url=self.start_urls[0],
callback=self.parse
)
# 爬虫启动函数,必须定义成这个函数名称
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
print("我是 parse")
imgList = response.xpath('//*[@id="gallery-list"]/li')
for item in imgList:
imgSrcValue = item.xpath('./a/img/@src').get()
yield MyspiderItem(img_url=imgSrcValue)
def close(spider, reason):
"""
结束时调用
:param reason:
:return:
"""
print("关闭")
return None
我们可以通过 scrapy.selector.unified.SelectorList
对象来查找get()、getall()、extract()、extract_first()、**re.first()**的如何使用。
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
imgList = response.xpath('//*[@id="gallery-list"]/li').extract()
print(imgList)
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
imgList = response.xpath('//*[@id="gallery-list"]/li').extract_first()
print(imgList)
imgList = response.xpath('//*[@id="gallery-list"]/li').extract_first()
print(imgList)
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
imgList = response.xpath('//*[@id="gallery-list"]/li').get()
print(imgList)
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
imgList = response.xpath('//*[@id="gallery-list"]/li').re("\d+")
print(imgList)
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
imgList = response.xpath('//*[@id="gallery-list"]/li').re_first("\d+")
print(imgList)
常见的几个 response 方法。
print(response.url)
print(response.urljoin("dsadasd"))
当 Item 在 Spider 中被收集之后,就会被传递到 Item Pipeline 中进行处理。
每个 item pipeline 组件是实现了简单的方法的 python 类,负责接收到 item 并通过它执行一些行为,同时也决定此 Item 是否继续通过 pipeline ,或者被丢弃而不再进行处理。
item pipeline 的主要作用:清理html数据、验证爬取的数据、去重并丢弃和保存数据。
每个pipeline
组件是一个独立的 pyhton
类,必须实现以process_item(self, item,spider)
方法。
每个item pipeline
组件都需要调用该方法,这个方法必须返回一个具有数据的 dict,或者 item对象,或者抛出 DropItem 异常,被丢弃的 item 将不会被之后的 pipeline 组件所处理。
注意:如果要使用哪一个 pipeline ,必须在配置文件中配置 ITEM_PIPELINES
。
scrapy pipeline 中主要的函数有 open_spider(self,spider)、close_spider(self,spider)和from_crawler(cls,crawler)。
表示当 spider 被开启的时候调用的主方法。
class MyspiderPipeline:
def process_item(self, item, spider):
print(item)
return item
其中,item 参数是 spider 返回的数据。
spider 是调用该管道的 spider。
当 spider 结束的时候这个方法被调用。
class MyspiderPipeline:
def process_item(self, item, spider):
print(type(spider))
return item
def close_spider(self, spider):
print("结束时调用!")
表示当 spider 被开启的时候调用这个方法。
def open_spider(self, spider):
print("开始时调用")
获取参数。
@classmethod
def from_crawler(cls, crawler):
log_level = crawler.settings.get('LOG_LEVEL')
# FIXME: for now, stats are only supported from this constructor
return cls(log_level)
def __init__(self, log_level):
self.log_level = log_level
但是,scrapy 还提供一个新的获取配置的方法。
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
print(settings.get('LOG_LEVEL'))
scrapy 默认携带了很多常用的中间件。下面我们来跟大家介绍几个常用的自带中间件。
文件下载中间件。
注意:使用 ImagesPipeline 首先定义存储文件的路径,所以需要定义一个FILES_STORE
变量,在settings.py中添加如下代码:
FILES_STORE = "./quanjing/"
设置下载文件时的请求头, 并返回一个 request 对象。
def get_media_requests(self, item, info):
"""
设置下载文件时的请求头, 返回一个 request 对象
:param item:
:param info:
:return:
"""
yield scrapy.Request(item['img_url'], meta={'Referer': item['img_url']})
设置下载文件的名称。
def file_path(self, request, response=None, info=None, *, item=None):
"""
设置下载路径
:param request:
:param response:
:param info:
:param item:
:return:
"""
return request.url.split("@")[0].split("/")[-1]
图片下载管道。
注意:使用 ImagesPipeline 首先定义存储文件的路径,所以需要定义一个IMAGES_STORE
变量,在settings.py中添加如下代码:
IMAGES_STORE = './images'
设置下载文件时的请求头, 并返回一个 request 对象。
def get_media_requests(self, item, info):
"""
设置下载文件时的请求头, 返回一个 request 对象
:param item:
:param info:
:return:
"""
yield scrapy.Request(item['img_url'], meta={'Referer': item['img_url']})
设置下载文件的名称。
def file_path(self, request, response=None, info=None, *, item=None):
"""
设置下载路径
:param request:
:param response:
:param info:
:param item:
:return:
"""
return request.url.split("@")[0].split("/")[-1]
当下载结束之后调用。
def item_completed(self, results, item, info):
"""
当下载文件结束之后调用
:param results:
:param item:
:param info:
:return:
"""
image_path = []
error_path = []
for ok, x in results:
if ok:
image_path.append(x)
else:
error_path.append(x)
print(error_path)
print(image_path)
return item
# []
# [{'url': 'https://pic.quanjing.com/9e/57/QJ6173385556.jpg@!350h', 'path': 'QJ6173385556.jpg', 'checksum': 'e5af2c0cf607b968c1e7eec24e4934ac', 'status': 'uptodate'}]
scrapy 中间件是 scrapy框架的重要组成部分,主要分为两大种类,分别是:下载器中间件(DownloaderMiddleware
)和 爬虫中间件(SpiderMiddleware
)。
引擎(engine)将 request 对象交给下载器之前,会经过下载器中间件;并且 scrapy 是支持同时使用多个中间件的。多个中间件之间遵循先进后出
的原理。
DownloaderMiddleware
)位于 Scrapy 引擎和下载器之间,主要用来处理从 EGINE 传到 DOWLOADER 的请求 request 和已经从 DOWNLOADER 传到 EGINE 的响应 response。在这个过程中你可用该中间件做以下几件事,分别是:添加ip代理、添加cookie、添加UA 和 请求重试 等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ELGtKXDe-1661835244721)(https://img2022.cnblogs.com/blog/1143853/202208/1143853-20220817143557712-1145024781.svg)]
在下载中间件中,主要包含了 process_request(request, spider)、process_response(request, response, spider) 和 **process_exception(request, exception, spider)**三个函数。
当每个request通过下载中间件时,该方法被调用。process_request() 必须返回其中之一: 返回 None 、返回一个 Response 对象、返回一个 Request 对象或raise IgnoreRequest 。
当下载器完成HTTP请求,传递响应给引擎的时候调用,它会返回 Response、Request、IgnoreRequest三种对象的一种。
当下载处理器 (download handler) 或 process_request() 抛出异常(包括 IgnoreRequest 异常)时, Scrapy 调用 process_exception() ,通常返回 None,它会一直处理异常
import scrapy
from ..items import MyspiderItem
class TupianSpider(scrapy.Spider):
# 定义爬虫名,scrapy会根据该值定位爬虫程序,所以它必须要有且必须唯一
name = 'tupian'
# 定义允许爬取的域名,如果OffsiteMiddleware启动(默认就启动),
# 那么不属于该列表的域名及其子域名都不允许爬取
allowed_domains = ['www.quanjing.com', 'pic.quanjing.com']
# 如果没有指定url,就从该列表中读取url来生成第一个请求
start_urls = ['https://www.quanjing.com/tupian/meinv.html']
# 爬虫启动函数,必须定义成这个函数名称
def parse(self, response):
"""
爬虫具体内容
:param response:
:return:
"""
imgList = response.xpath('//*[@id="gallery-list"]/li')
ua_header = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Language': 'zh-CN,zh;q=0.9',
"Referer": "https://www.quanjing.com/"
}
for item in imgList:
imgSrcValue = item.xpath('./a/img/@src').extract_first()
yield scrapy.Request(
url=imgSrcValue,
callback=self.parse_detail,
headers=ua_header,
meta={ "img_url": imgSrcValue }
)
def parse_detail(self, response):
"""
图片详情
:param response:
:return:
"""
item = MyspiderItem()
item['img_url'] = response.meta['img_url']
item['img_body'] = response.body
yield item
import scrapy
class MyspiderItem(scrapy.Item):
# define the fields for your item here like:
img_url = scrapy.Field()
img_body = scrapy.Field()
class MyspiderPipeline:
def process_item(self, item, spider):
settings = get_project_settings()
with open(settings['IMAGES_STORE'] + item['img_url'].split("@")[0].split("/")[-1], 'wb') as f:
f.write(item['img_body'])
return item
IMAGES_STORE = "./quanjing/"
spider 中间件是一个与 Scrapy 的 spider 处理机制挂钩的框架,您可以在其中插入自定义功能来处理发送给spider进行处理的响应,并处理从 spider 生成的请求和项目。
要激活蜘蛛中间件组件,请将其添加到 SPIDER_MIDDLEWARES
设置中,这是一个 dict,其键是中间件类路径,它们的值是中间件顺序。
SPIDER_MIDDLEWARES = {
'mySpider.middlewares.MyspiderSpiderMiddleware': 543,
}
spider 中间件主要是作用于引擎和调度器之间的。
调用顺序为:from_crawler --> spider_opened --> process_start_requests --> process_spider_input --> process_spider_output。
scrapy 默认自带了一部分常用的中间件。下面我们举几个案例介绍一些 Scrapy 自带的中间件。
middlewares.py
class RequestProxyMiddleware(HttpProxyMiddleware):
def process_request(self, request, spider):
settings = get_project_settings()
self.proxies = settings.get("HTTP_PROXY")
request.meta["proxy"] = random.choice(self.proxies)
return None
settings.py
DOWNLOADER_MIDDLEWARES = {
'mySpider.middlewares.RequestProxyMiddleware': 544
}
HTTP_PROXY = {
"47.92.113.71:80",
"59.124.224.205:3128",
"118.163.13.200:8080",
"47.57.188.208:80",
"59.124.224.205:3128",
"59.124.224.205:3128",
"59.124.224.205:3128",
"112.250.107.37:53281",
"59.124.224.205:3128",
"47.92.113.71:80",
"59.124.224.205:3128"
}
CrawlSpider 其实是 Spider 的一个子类,除了继承到 Spider 的特性和功能外,还派生除了其自己独有的更加强大的特性和功能。其中最显著的功能就是 ”LinkExtractors链接提取器“。Spider 是所有爬虫的基类,其设计原则只是为了爬取 start_url 列表中网页,而从爬取到的网页中提取出的 url 进行继续的爬取工作使用 CrawlSpider 更合适。
$ scrapy startproject crawSpiderProject
$ cd crawSpiderProject
$ scrapy genspider -t crawl netbian pic.netbian.com
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class CrawlquanjingSpider(CrawlSpider):
name = 'crawlquanjing'
allowed_domains = ['so.gushiwen.cn']
start_urls = ['https://so.gushiwen.cn/guwen/default.aspx?p=1']
# 需求:
# 爬取所有的图片
# follow : 是否将该规则作用于 response
# 1 2 3 4 5 ... 77 78 79 5 3 4 5 6 ... 77 78 79
# 1 2 3 4 5 77 78 79 6 ~ 76
rules = (
# 列表页 规则
# Rule(LinkExtractor(allow=r'https://movie.douban.com/subject/\d+/'), callback='parse_item'),
# # 下一页 规则
Rule(LinkExtractor(allow=r'/guwen/default.aspx\?p=\d+',
deny_extensions=['xxx'],
deny=r'/user/\w+\.aspx'),
follow=True, callback='parse_item'),
)
def parse_item(self, response, **kwargs):
# 只需要出来结果,不需要处理请求
print(response.url)
item = {}
# item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
# item['name'] = response.xpath('//div[@id="name"]').get()
# item['description'] = response.xpath('//div[@id="description"]').get()
return item
搭建一个分布式的集群,让其对一组资源进行分布联合爬取,提升爬取效率。
pip install scrapy-redis
不可以!!!
scrapy-redis组件中为我们封装好了可以被多台机器共享的调度器和管道,我们可以直接使用并实现分布式数据爬取。
上述两种不同方式的分布式实现流程是统一的
pip install scrapy-redis
- linux或者mac:redis.conf
- windows:redis.windows.conf
修改
- 注释该行:bind 127.0.0.1,表示可以让其他ip访问redis
- 将yes改为no: protected-mode no,表示可以让其他ip操作redis
注意:如果原始爬虫文件是基于Spider的,则应该将父类修改成RedisSpider,如果原始爬虫文件是基于CrawlSpider的,则应该将其父类修改成RedisCrawlSpider。
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
# 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
SCHEDULER_PERSIST = True
REDIS_HOST = 'redis服务的ip地址'
REDIS_PORT = 6379
REDIS_ENCODING ='utf-8'
REDIS_PARAMS = {'password':'xx'}
scrapy runspider xxx.py
pip install scrapy-redis-bloomfilter
# 替换scrapy_redis的去重类
DUPEFILTER_CLASS = "scrapy_redis_bloomfilter.dupefilter.RFPDupeFilter"
# 替换原来的请求调度器的实现类,使用 scrapy-redis 中请求调度器
SCHEDULER = "scrapy_redis_bloomfilter.scheduler.Scheduler"
# 设置布隆过滤器散列函数的个数,默认为6,可以自行修改
BLOOMFILTER_HASH_NUMBER = 6
# Bloom Filter的bit参数,默认30,占用128MB空间,可存储数据量级1亿
# BLOOMFILTER_BIT决定了位图的位数。如果BLOOMFILTER_BIT为30,那么位数组位数为2的30次方,这将占用Redis 128 MB的存储空间,去重量级在1亿左右。
BLOOMFILTER_BIT = 30
配套视频地址:https://www.wotiecity.com/system/course/MTc=?cid=93332355