scrapy框架详解

11.scrapy框架详解

周围的人都比你厉害,你才会慢慢变强

一、介绍

1.基本介绍

  • 通用的网络爬虫框架,可以批量快速爬取网页数据

  • 相当于爬虫界的Django,用起来,跟djagno很像

  • 使用框架,在固定位置写固定代码,速度很快

2.起源

  • Scrapy 是基于 Twisted异步网络库框架来处理网络通讯
  • twisted是一个流行的事件驱动的python网络框架。

3.架构图

img

图分析: 各个组件
  1. spiders /ˈspaɪdə(r)z/ 网页爬虫

    • 作用: 开发人员自定义的类, 解析数据response, 提取items, 或者发送新的请求request
    • 在这里写爬虫代码(爬哪些网址),解析数据(自己写)
  2. ENGINE /ˈendʒɪn/ 引擎

    • 作用: 负责控制数据的流向(总管)
  3. SCHEDULER /ˈʃedjuːlə(r)/ 调度器 (先进先出,先进后出,优先级)

    • 作用: 去重, 决定下一个通过配置实现深度优先爬取还是广度优先爬取 (原理: 队列 -> 广度, 堆栈 -> 深度), 由它来决定下一个要抓取的网址是什么

      深度优先:

      • 摁着一条线爬取

      • 先进先出

      广度优先:

      • 后进先出

      注意:

      我们在创建一个项目时,在spider中会存在一个start_urls = [‘http://dig.chouti.com/’],他将是我们的初始url,会在项目启动后被引擎放入调度器中开始处理

  4. DOWNLOADER 下载器

    • 作用: 用于下载网页内容. 并将网页内容返回给EGINE, 下载器是建立在twisted这个搞笑的异步模型之上
    • 注意: 进过下载中间件过来. 中间件中可以使用user-agent, proxies
  5. ITEM PIPELINES 项目管道

    • 作用: 在items被提取后负责处理它们,主要包括清理、验证、持久化(比如: 存到数据库)等操作

Middewares两大中间件:

  • spiders和egine之间: 处理输入, 输出
  • downloader和egine之间: 加proxies, headers, 集成selenium
图分析:英文原版

The data flow in Scrapy is controlled by the execution engine, and goes like this:

  1. The Engine gets the initial Requests to crawl from the Spider.
  2. The Engine schedules the Requests in the Scheduler and asks for the next Requests to crawl.
  3. The Scheduler returns the next Requests to the Engine.
  4. The Engine sends the Requests to the Downloader, passing through the Downloader Middlewares (see process_request()).
  5. Once the page finishes downloading the Downloader generates a Response (with that page) and sends it to the Engine, passing through the Downloader Middlewares (see process_response()).
  6. The Engine receives the Response from the Downloader and sends it to the Spider for processing, passing through the Spider Middleware (see process_spider_input()).
  7. The Spider processes the Response and returns scraped items and new Requests (to follow) to the Engine, passing through the Spider Middleware (see process_spider_output()).
  8. The Engine sends processed items to Item Pipelines, then send processed Requests to the Scheduler and asks for possible next Requests to crawl.
  9. The process repeats (from step 1) until there are no more requests from the Scheduler.

官网链接:https://docs.scrapy.org/en/latest/topics/architecture.html

4.优点

  • Scrapy是一个异步非阻塞框架
  • 可扩展性很高

5.Scrapy运行流程

1.引擎从调度器中取出一个连接URL,用于接下来的抓取

2.引擎将URL封装为一个请求Request传给下载器

3.下载器将资源下载,封装为应答包Response

4.爬虫解析Response

5.解析出实体Item,将实体通过管道解析持久化操作

6.若是解析出URL,将其放入调度器中等待抓取

注意:第一步之前,会先去爬虫start_url中获取初始网址,进行操作

二、安装

# 运气好
pip3 install scrapy

#运气不好
1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
3、pip3 install lxml
4、pip3 install pyopenssl
5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
8、pip3 install scrapy

补充:

# windows上,如果有模块装不上,去网站下载对应的wheel文件,安装
pip3 install D:\Downloads\Twisted-20.3.0-cp36-cp36m-win_amd64.whl

三、scrapy创建项目,创建爬虫

项目创建的基本命令

1.创建项目(django中创建项目)
scrapy startproject 项目名

2.创建爬虫(django中创建app)
cd 项目名
scrapy genspider 爬虫名 初始url地址(后面可以修改)

3.展示爬虫应用列表
scrapy list
4.运行爬虫应用
scrapy crawl 爬虫应用名称

5 其他命令
	-scrapy view http://www.taobao.com # 检测这个页面的数据

示例:

# 创建项目
scrapy startproject myfirstscrapy

# 创建爬虫
cd myfirstscrapy
scrapy genspider chouti dig.chouti.com

# 展示爬虫应用列表
scrapy list
chouti
# 运行爬虫应用
scrapy crawl chouti --nolog  #--nolog不打印日志

命令行工具

#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 version 查看scrapy的版本,scrapy version -v查看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

四、目录介绍

myfirstscrapy/               # 项目名
├── myfirstscrapy/           # 包
    ├── spiders/            # 所有的爬虫文件存放路径
        ├── __init__.py
        ├── baidu.py        # 新建的爬虫文件1
        ├── chouti.py       # 新建的爬虫文件2
    ├── __init__.py
    ├── items.py            # 类比models,一个个的类,后期类实例化存储数据
    ├── main.py             # 自己创建, 作为启动脚本
    ├── middlewares.py      # 中间件(爬虫,下载中间件都写在这)
    ├── pipelines.py        # 持久化相关(items.py中类的对象)
    └── settings.py         # 配置文件
└── scrapy.cfg              # 上线相关

五、settings介绍

ROBOTSTXT_OBEY = False   # 关闭默认遵守的爬虫协议,  默认情况,scrapy会去遵循爬虫协议

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'

LOG_LEVEL = 'ERROR'

六、启动爬虫

1 启动爬虫 
scrapy crawl chouti
scrapy crawl chouti --nolog   # 不打印日志

2 把配置文件中改一下(不遵循爬虫协议)
ROBOTSTXT_OBEY = False

3 点击右键,使用pycharm直接运行
	需要在项目路径下新建一个main.py
    from scrapy.cmdline import execute
    #相当于在命令行中执行命令
    execute(['scrapy', 'crawl', 'chouti', '--nolog'])

image-20210523221125832

七、解析器的使用方式

1.在爬取页面以后爬取其他网址

def parse(self, response): #解析方法
    print(response.status)
    from scrapy.http.request import Request
    return Request('https://www.baidu.com', dont_filter=True)

2.使用第三方解析

def parse(self, response):
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(response.text, 'lxml')
    div_list = soup(class_='link-con')
    print(div_list)
    for div in div_list:
        image_url = div.a.get('data-pic')
        desc = div.find(class_='link-title').text
        print(f"""
        图片链接: {image_url}
        内容详情: {desc}
        """)  

3.使用自带解析

# 自带解析: xpath, css
resposne.xpath('xpath语法')
resposne.css('css语法')

# css
    取文本: css('::text')
    取属性: css('::attr(href)')

# xpath
    取文本: xpath("/text()")
     取属性: xpath("/@href")

# 取值:  注意: 如果指定取出来的结果是一个 Selector 对象
    取出列表: .extract()
    取出单个: .extract_first()
使用css解析
def parse(self, response):
    # 使用css解析
    div_list = response.css('.link-item')
    print(len(div_list))
    for div in div_list:
        desc = div.css('.link-title::text').extract_first()
        image_url = div.css('.common-matching-con::attr(data-pic)').extract_first()
        if not desc:
            continue
        print(f"""
        文本描述: {desc}
        图片链接: {image_url}
        """)
使用xpath解析
def parse(self, response):
    # 使用xpath解析
    div = response.xpath('//div[contains(@class, "link-item")]')[0]
    desc_list = div.xpath('//a[contains(@class, "link-title")]/text()').extract()
    image_url_list = div.xpath('//a[contains(@class, "common-matching-con")]/@data-pic').extract()
    for desc, image_url in zip(desc_list, image_url_list):
        if not desc:
            continue
        print(f"""
        文本描述: {desc}
        图片链接: {image_url}
        """)

八、数据持久化的两种方式

'''
持久化方案一: 
	提示: parse函数中必须返回列表套自己的格式 [{}, {}, ...]
	scrapy crawl chouti -o chouti.csv
	scrapy crawl chouti -o chouti.json
	持久化支持格式: 'json', 'jsonl
ines', 'jl', 'csv', 'xml', 'marshal', 'pickle'

持久化方案二: 通过pipline item存储
	1. items.py中定义items类
	2. 在spinder中导入, 实例化, 把数据放入(注意: 使用yield)
		item[key] = value
		yield item
	3. 配置文件中找到 ITEM_PIPELINES 配置pipelines.py中定义的pipelines类
        ITEM_PIPELINES = {
           'first_scrapy.pipelines.pipelines.py中定义的pipelines类': 300,  # 数子越小级别越高
        }		
	4. 在piplines.py中写配置的类
		open_spider(self, spider)  开
		process_item(self, item, spider)  
			注意: 一定要return item
		close_spider(self, spider) 关
'''

1.持久化方案一

def parse(self, response):
    li = []
    div = response.xpath('//div[contains(@class, "link-item")]')[0]
    desc_list = div.xpath('//a[contains(@class, "link-title")]/text()').extract()
    image_url_list = div.xpath('//a[contains(@class, "common-matching-con")]/@data-pic').extract()
    li = []
    for desc, image_url in zip(desc_list, image_url_list):
        li.append({'desc': desc, 'image_url': image_url})
    return li 

# 命令行终端中执行持久化生成对应格式的文件: 
scrapy crawl chouti -o chouti.csv

2.持久化方案二

items.py
class ChouTiScrapyItem(scrapy.Item):
    desc = scrapy.Field()
    image_url = scrapy.Field()
spiders/chouti.py
def parse(self, response):
    from first_scrapy.items import ChouTiScrapyItem
    item = ChouTiScrapyItem()

    div_list = response.css('.link-item')
    print(len(div_list))
    for div in div_list:
        desc = div.css('.link-title::text').extract_first()
        image_url = div.css('.common-matching-con::attr(data-pic)').extract_first()
        if not desc or not image_url:
            continue
        item['desc'] = desc
        item['image_url'] = image_url
        yield item
pipelines.py: 书写存储到文件, mysql, redis中的持久化类
import json
import pymysql
from redis import Redis
from itemadapter import ItemAdapter


class FirstScrapyPipeline:
    def process_item(self, item, spider):
        return item


class ChouTiFilePipline:
    """存储到文件中的持久化类"""
    def open_spider(self, spider):
        print('ChouTiFilePipline 开始!')
        self.f = open('chouti.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        data = {'desc': item.get('desc'), 'image_url': item['image_url']}
        json.dump(data, self.f, ensure_ascii=False)
        self.f.write('\n\n')
        # 注意: 这里一定要返回item, 不然后续的持久化类获取的值就是None
        return item

    def close_spider(self, spider):
        print("ChouTiFilePipline 结束!")
        self.f.close()


class ChouTiMySQLPipline:
    """存储到MySQL中的持久化类"""
    def open_spider(self, spider):
        print('ChouTiMySQLPipline 开始!')
        self.conn = pymysql.connect(
            host='127.0.0.1', user="root", password="123",
            database='crawler', port=3306, charset='utf8'
        )
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
        sql = 'insert into chouti(desc_content, image_url) values(%s, %s);'
        try:
            self.cursor.execute(sql, [item.get("desc"), item.get('image_url')])
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        print('ChouTiMySQLPipline 结束!')
        self.cursor.close()
        self.conn.close()


class ChouTiReidsPipline:
    """存储到redis中的持久化类"""
    def open_spider(self, spider):
        print('ChouTiReidsPipline 开始!')
        self.conn = Redis.from_url('redis://127.0.0.1/0')

    def process_item(self, item, spider):
        data = {'desc': item.get('desc'), 'image_url': item['image_url']}
        pipe = self.conn.pipeline(transaction=True)
        pipe.multi()    # 启动事务
        self.conn.rpush('chouti', json.dumps(data, ensure_ascii=False))
        pipe.execute()  # 提交
        return item

    def close_spider(self, spider):
        print('ChouTiReidsPipline 结束!')
        self.conn.close()

九、scrapy的请求传参

# 传递: 把要传递的数据放到meta中
	yield Request(url, meta={'item': item})
	
# 取值: 在response对象中取出来
	item = response.meta.get('item')

# 实现思路:
	downloader中将出去的request中包含的item, 保存. 在response返回的时候赋值给response. 之后通过engine调度返回给spider. 

实例: 全站爬取cnblogs

'''
爬取原则: scrapy默认是先进先出
	深度优先:详情页先爬   队列,先进先出
	广度优先:每一页先爬   堆栈,后进先出
'''

# spiders/cnblogs.py
import scrapy
from scrapy.http.request import Request
from first_scrapy.items import CnblogsScrapyItem


class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'
    allowed_domains = ['www.cnblogs.com']
    start_urls = ['http://www.cnblogs.com/']

    def parse(self, response):
        article_list = response.css('#post_list .post-item')
        for article in article_list:
            desc = article.css('.post-item-summary::text').extract_first().strip()
            title_url = article.css('.post-item-title::attr(href)').extract_first()
            title = article.css('.post-item-title::text').extract_first()
            item = CnblogsScrapyItem()
            item['title_url'] = title_url
            item['title'] = title
            item['desc'] = desc.strip()
            # 继续爬取详情. 提示: callback如果不写,默认回调到parse方法, 如果写了,响应回来的对象就会调到自己写的解析方法中.
            yield Request(title_url, callback=self.parse_article, meta={'item': item})

        next_page_url = 'https://www.cnblogs.com' + response.css(
            '#paging_block a:last-child::attr(href)').extract_first()
        yield Request(next_page_url)

    def parse_article(self, response):
        # content = response.css('//[@id="cnblogs_post_body"]').extract()
        content = response.css('#cnblogs_post_body').extract_first()
        item = response.meta.get('item')
        item['content'] = content
        yield item

        
# items.py
class CnblogsScrapyItem(scrapy.Item):
    """
    item['title_url'] = title_url
    item['title'] = title
    item['desc'] = desc
    item['content'] = content
    """
    title_url = scrapy.Field()
    content = scrapy.Field()
    desc = scrapy.Field()
    title = scrapy.Field()
    
# settings.py
ITEM_PIPELINES = {
   # cnblogs
   'first_scrapy.pipelines.CnblogsMySQLPipline': 301,
}

# pipelines.py
import json
import pymysql

class CnblogsMySQLPipline:
    """将爬取的Cnblogs存储到MySQL中进行持久化"""

    def open_spider(self, spider):
        print('CnblogsMySQLPipline 开始!')
        self.conn = pymysql.connect(
            host='127.0.0.1', user="root", password="123",
            database='crawler', port=3306, charset='utf8'
        )
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
        sql = 'insert into cnblogs(title, title_url, `desc`, content) values(%s, %s, %s, %s);'
        try:
            self.cursor.execute(sql, [item['title'], item['title_url'], item['desc'], item['content']])
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        print('CnblogsMySQLPipline 结束!')
        self.cursor.close()
        self.conn.close()

十、提升scrapy爬取数据的效率

提示: 以下的实现在配置文件中进行相关的配置即可(注意: 默认还有一套setting)

# 1. 增加并发:CONCURRENT_REQUESTS = 100
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改 CONCURRENT_REQUESTS = 100 值为100,并发设置成了为100# 2. 降低日志级别: LOG_LEVEL = 'INFO'
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = 'ERROR'

# 3. 禁止cookie:COOKIES_ENABLED = False
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False

# 4. 禁止重试:RETRY_ENABLED = False
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False

# 5. 减少下载超时:DOWNLOAD_TIMEOUT = 10
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

十一、scrapy的中间件: 下载中间件

1. process_request

def process_request(self, request, spider):
    # 返回值:
    # 1. return None: 请求正常继续往后走
    # 2. return Response: 请求不出去, 而是直接原路返回交给engine进行处理
    # 3. return Request:  请求不出去, 而是直接原路返回交给engine进行处理
    # 4. raise: 一旦抛出异常就会交给process_exception方法处理. 应用场景: 筛选排除不爬取的request.
1) 加随机请求头: fake-useragent 或者 Fake

方式一: 使用fake-useragent

# 1. 下载: 
pip3 install fake-useragent

# 2. 导入使用:
# 声明成中间件类属性
from fake_useragent import UserAgent
ua = UserAgent()

# 在process_request中使用: 返回值None
request.headers['User-Agent'] = ua.random

方式二: 使用Faker

# 1. 下载: 
pip3 install faker    	

# 2. 导入使用:
# 声明成中间件类属性
from faker import Faker
f = Faker(locale='zh_CN')

# 在process_request中使用: 返回值None
request.headers['User-Agent'] = f.user_agent()

注意: headers类最终继承了dict因此可以使用字段赋值的方式给请求头加user-agent

from scrapy.http.headers import Headers
2) 加cookie

加cookie

# 在process_request中使用: 返回值None
request.cookies = {'cookie1': 'value1'}

加cookie池

# 声明成spiders类属性
cookie_pool = [{'cookie1': 'value1'}, {'cookie2': 'value2'}, ...]

# 在process_request中使用: 返回值None
imoport random
request.cookies = random.choice(spider.cookie_pool)
3) 加代理

加代理

# 在process_request中使用: 返回值None
request.meta['download_timeout'] = 20
request.meta['proxy'] = 'http://111.72.149.117:30057'

加代理池

# spiders中
import requests

class DemoSpider(scrapy.Spider):
    def __init__(self, name=None, **kwargs):
        super().__init__(name=None, **kwargs)
        self.proxy = None

    @staticmethod
    def get_proxy():
        return requests.get("http://127.0.0.1:5010/get/").json().get("proxy")

    @staticmethod
    def delete_proxy(proxy):
        requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy))

    def close(self, reason):
        """self就是spider对象"""
        self.delete_proxy(self.proxy)
    ...
    
# 在process_request中使用: 返回值None
request.meta['download_timeout'] = 20
spider.proxy = spider.get_proxy()
request.meta['proxy'] = f'http://{spider.proxy}'
4) 加selenium
# spiders中
from selenium import webdriver

class DemoSpider(scrapy.Spider):
    browser = webdriver.Chrome()
    
    def close(self, reason):
        """self就是spider对象"""
        self.browser.close()
 
# 在process_request中使用: 返回response对象
from scrapy.http import HtmlResponse, Response

spider.browser.get(request.url)
response = HtmlResponse(url=request.url, body=spider.browser.page_source.encode('utf-8'), request=request)
return response

2. process_response

def process_response(self, request, response, spider):
    # 返回值
    # 1. return Response: 正常继续执行.
    # 2. return Request: 交给engine, 让engine进行处理以后继续往下走.
    # 3. raise: 一旦抛出异常就不因该走ITEMS进行持久化, 因为这样的数无法解析一解析就会抛出异常! 应用场景: 排除爬取网页频繁从而出现的验证校验.

3. process_exception

def process_exception(self, request, exception, spider):
    # 返回值:
    # 1. return None: 捕获process_request和process_response抛出的异常, 将本次数据及请求丢弃.
    # 2. return Response: 捕获process_request和process_response抛出的异常, 停止当连接将返回的response的结果丢给engine, 让engine继续进行处理
    # 3. return Request:  捕获process_request和process_response抛出的异常, 停止当连接将返回的request对象的结果丢给engine, 让engine继续进行处理

    # 应用场景: 捕获process_request和process_response抛出的异常, 停止当连接指定一个新的url使用Request进行切换
    '''
    from scrapy import Request

    # 注意: url不能直接修改
    request.url = 'https://www.cnblogs.com'
    # request = Request(url='https://www.cnblogs.com', callback=spider.spider, )
    return request
    '''

    pass

十二、去重

1. 内置去重

'''
# 去重关键: request_seen
# 默认内置: 用的是集合去重
'''
# 去重类: RFPDupeFilter
from scrapy.dupefilters import RFPDupeFilter

# 去重关键方法: RFPDupeFilter下的request_seen方法下的request_fingerprint方法
from scrapy.utils.request import request_fingerprint

# fingerprint指纹本质就是使用md5加密, 但是是通过对?号后面参数进行排序实现
from scrapy.utils.request import request_fingerprint
from scrapy import Request

url1 = Request(url='https://www.baidu.com/?age=18&name=yang')
url2 = Request(url='https://www.baidu.com/?name=yang&age=18')
res1 = request_fingerprint(url1)
res2 = request_fingerprint(url2)
print(res1)  # 5cee3ea26b02e17f343ebb143dbbb3cc69f406bc
print(res2)  # 5cee3ea26b02e17f343ebb143dbbb3cc69f406bc
print(res1 is res2)  # False

2. 布隆过滤器去重

1) 下载
  1. 先去这个网站下载bitarray这个依赖 https://www.lfd.uci.edu/~gohlke/pythonlibs/#bitarray
  2. 直接安装会报错error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio": https://visualstudio.microsoft.com/downloads/
  3. 安装wheel文件, 防止我们主动安装报这样的错误pip3 install bitarray-1.1.0-cp36-cp36m-win_amd64.whl
  4. pip3 install pybloom_live

wheel文件如何安装: https://www.cnblogs.com/justblue/p/13198202.html

2) 使用
'''
# 更高效方式实现: 布隆过滤器
	作用:去重, 解决缓存穿透
	缺点: 错误率存在, 不过取绝与数组的位数
	
# 自定义使用布隆过滤器去重流程
	1. 新建.py文件重写过滤类, 并继承BaseDupeFilter
	2. 重写request_seen方法
	3. 配置文件中配置. 使用它默认加载
		项目结构: first_scrapy/first_scrapy/bloom_filter.py/BloomFilter
		DUPEFILTER_CLASS = 'first_scrapy.bloom_filter.BloomFilter'  # 默认的去重规则帮我们去重,去重规则在内存中, settings.py配置文件中需要自己配置
'''
from scrapy.dupefilters import BaseDupeFilter
from pybloom_live import ScalableBloomFilter


class BloomFilter(BaseDupeFilter):
    """Request Fingerprint duplicates filter"""

    def __init__(self, path=None, debug=False):
        # ScalableBloomFilter可自动扩容的布隆过滤器
        self.bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
        super().__init__(path=None, debug=False)

    def request_seen(self, request):
        if request.url in self.bloom:
            return True
        self.bloom.add(request.url)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾维斯Echo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值