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
    评论
### 回答1: Scrapy是一个Python爬虫框架,它提供了一种快速、高效、可扩展的方式来爬取网站数据。Scrapy的主要特点包括: 1. 基于Twisted异步网络框架,可以高效地处理大量的并发请求。 2. 支持多种数据格式,包括XML、JSON、CSV等。 3. 提供了强大的数据提取功能,可以通过XPath或CSS选择器来提取网页中的数据。 4. 支持分布式爬取,可以通过Scrapy-Redis等插件实现。 5. 提供了丰富的中间件和扩展机制,可以方便地定制爬虫行为。 Scrapy的使用流程一般包括以下几个步骤: 1. 定义Item:定义需要爬取的数据结构。 2. 编写Spider:编写爬虫代码,定义如何爬取网站数据。 3. 定义Pipeline:定义数据处理流程,对爬取到的数据进行处理和存储。 4. 配置Settings:配置爬虫的一些参数,如请求头、下载延迟等。 5. 运行爬虫:使用命令行工具或Scrapy API启动爬虫。 总之,Scrapy是一个功能强大、易于使用的Python爬虫框架,可以帮助开发者快速地构建高效、可扩展的爬虫应用。 ### 回答2: Scrapy是一个基于Python的开源网络爬虫框架。它可以在一个爬虫工程师的控制下自动执行爬取任务,不仅可方便地快速抓取各类网站数据,而且还能够轻松地对爬取到的数据进行处理、存储和展示。 Scrapy的功能包括了爬虫组件、下载器、中间件框架、优化器和调度器。其中,爬虫组件是Scrapy的核心模块,它负责实现对爬取网站的访问和数据解析,并抽象成Scrapy的Item类型。下载器用于获取相应网页的HTTP数据,中间件框架可以进行层层拦截和处理各种网络请求,以支持一些高级事务。另外,优化器和调度器则主要负责控制整个爬虫工程师的流程和顺序。 Scrapy的使用方式极为简单。在使用Scrapy之前,首先需要使用命令“scrapy startproject”来创建一个工程,然后可在该工程下进一步创建一到多个爬虫组件并指定需要爬取的网址。接下来,我们可定义一个Item类型,来解决需要爬取的数据对象的问题。在爬虫组件中,我们需要定义如何爬取和解析数据。同时,如果希望实现登录功能,我们可在中间件框架中进行相应设置。而对于数据存储和展示等操作,我们也可以在Item Pipeline中定义。 总结起来,Scrapy框架解决了网页数据爬取的问题,提供了简单易用的API以及丰富的库,可以完成高效而优质的爬取,而且功能上也足以满足个人爬虫开发的需求。如果我们希望进一步学习更多Scrapy的应用,我们可以参考Scrapy官方文档、StackOverflow和GitHub的相关资源,以及优秀的一些自媒体博文和经验分享。 ### 回答3: Scrapy是一个Python编写的用于Web数据采集的高级抓取框架。它是一个基于Twisted框架的异步网络库,可以更高效地处理网页的并发请求和响应。Scrapy框架的架构模式和流程非常清晰,包括了一系列数据处理工具和插件,方便用户进行数据的爬取、处理、存储和展示。 Scrapy框架主要包括以下几个模块: 1. Engine: 引擎控制所有模块进行协作,调度模块接收引擎发来的请求,并将其转发给下载器,同时将下载器获得的响应反馈给引擎。 2. Scheduler: 调度器负责接收并存储引擎发来的请求,并按照一定的策略进行调度,将请求发送给下载器。 3. Downloader: 下载器负责请求网络数据,将响应返回给引擎。 4. Spider: 爬虫负责解析、处理响应,并产生需要的请求数据,将其发给引擎。爬虫是用户自定义的模块,用于指定如何对网站进行数据抓取和处理。 5. Item Pipeline: 项目管道用于处理从Spider中获取到的Item,可以对Item进行过滤、验证、清洗和存储等操作。 6. Middlewares: 中间件用于修改在引擎和下载器之间传递的请求和响应,可以被用于添加请求头、代理、IP池、处理Cookie等操作。 使用Scrapy,可以很好地解决数据采集过程中遇到的各种问题,如并发请求、防反爬虫、分布式爬取、自动重试、日志管理等。同时,Scrapy还提供了强大的数据处理工具,如XPath和CSS选择器,能够使得用户更加轻松地解析网页并提取所需要的信息。因此,Scrapy在反爬虫、数据采集等领域具有广泛的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

贾维斯Echo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值