scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫

文章目录

前言

Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫

一、架构介绍

scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫

; 引擎(EGINE)

引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。

调度器(SCHEDULER)

用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

下载器(DOWLOADER)

用于下载网页内容, 并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的

爬虫(SPIDERS)

SPIDERS是开发人员自定义的类,用来解析responses,并且提取items,或者发送新的请求

项目管道(ITEM PIPLINES)

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

下载器中间件(Downloader Middlewares)

位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到EGINE的响应response,

爬虫中间件(Spider Middlewares)

位于EGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)

一、安装

针对于mac、linux基本不会出现问题

pip3 install scrapy

针对windows有概率失败
失败解决方案:

1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
2、pip3 install lxml
3、pip3 install pyopenssl
4、下载并安装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

一、项目创建

scrapy与django类似

1 创建scrapy项目

scrapy startproject 项目名

2 创建爬虫

scrapy genspider 爬虫名 爬取网址

3启动爬虫,爬取数据

方法一指令启动:
scrapy crawl 爬虫名字 –nolog(该参数为不打印日志,可忽略)

方法二使用脚本来启动:
项目路径下新建main.py

from scrapy.cmdline import execute
execute(['scrapy','crawl','爬虫名','--nolog'])

二、目录介绍

scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫
firstscrapy
   firstscrapy
        spiders
            cnblogs.py
        items.py
        middlewares.py
        pipelines.py
        settings.py
    scrapy.cfg

三、解析数据

分析数据写在爬虫文件中的parse方法里(spiders下的爬虫文件,本文为cnblogs.py)

response对象有css方法和xpath方法

  1. css中写css选择器
  2. xpath中写xpath选择

俩种方法可以独立使用也可以混合使用,俩种方法找到的是标签对象,会转化为类对象,所以可以根据类对象再次进行标签查找

示例:

-xpath取文本内容
'.//a[contains(@class,"link-title")]/text()'
-xpath取属性
'.//a[contains(@class,"link-title")]/@href'
-css取文本
'a.link-title::text'
-css取属性
'img.image-scale::attr(src)'

取到的标签对象可以是多个或者一个再选择对应的方法即可(使用以下俩个方法获取的标签不再是类对象,无法再次对其下内容进行查找)
.extract_first() 取一个,使得提取内容转换为unicodez字符串
.extract() 取所有,使得提取内容转换为unicodez字符串,返回一个list

示例代码:

import scrapy

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

    def parse(self, response):
        articles = response.xpath('.//article[@class="post-item"]')
        for article in articles:
            title = article.xpath('.//a[@class="post-item-title"]/text()').extract_first()
            url = article.xpath('.//a[@class="post-item-title"]/@href').extract_first()
            desc = article.xpath('.//p[@class="post-item-summary"]/text()').extract()
            author = article.xpath('.//footer[@class="post-item-foot"]/a/span/text()').extract_first()

四、配置

修改settings.py

1.基础配置


ROBOTSTXT_OBEY = False

LOG_LEVEL='ERROR'

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'

DEFAULT_REQUEST_HEADERS = {
   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
   'Accept-Language': 'en',
}

SPIDER_MIDDLEWARES = {
    'cnblogs.middlewares.CnblogsSpiderMiddleware': 543,
}

DOWNLOADER_MIDDLEWARES = {
    'cnblogs.middlewares.CnblogsDownloaderMiddleware': 543,
}

ITEM_PIPELINES = {
    'cnblogs.pipelines.CnblogsPipeline': 300,
}

2.增加爬虫的爬取效率配置


CONCURRENT_REQUESTS = 100

LOG_LEVEL = 'INFO'

COOKIES_ENABLED = False

RETRY_ENABLED = False

DOWNLOAD_TIMEOUT = 10

五、持久化方案

第一种:parse返回值(很少使用)

解析函数中parse,要return [{},{},{}],返回一个列表套字典的数据

    def parse(self, response):
        articles = response.xpath('.//article[@class="post-item"]')
        article_list = []
        for article in articles:
            item = CnblogsItem()
            title = article.xpath('.//a[@class="post-item-title"]/text()').extract_first()
            url = article.xpath('.//a[@class="post-item-title"]/@href').extract_first()
            desc = article.xpath('.//p[@class="post-item-summary"]/text()').extract()
            author = article.xpath('.//footer[@class="post-item-foot"]/a/span/text()').extract_first()
            article_list.append({'title':title, 'url':url, 'desc':desc, 'desc':desc, 'author':author, })
        return article_list

使用指令 scrapy crawl cnblogs -o 文件名(json,csv等后缀)

scrapy crawl cnblogs -o data.json

第二种:pipline模式(通用的)

1.在items.py中写一个类,继承scrapy.Item
在类中编写属性

import scrapy

class CnblogsItem(scrapy.Item):
    title = scrapy.Field()
    desc = scrapy.Field()
    pub_time = scrapy.Field()
    author = scrapy.Field()
    url = scrapy.Field()
    content = scrapy.Field()

2.在爬虫中导入类,实例化得到对象,把要保存的数据放到对象中

import scrapy
from scrapy import Request
from testscrapy.items import CnblogsItem

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

    def parse(self, response):
        articles = response.xpath('.//article[@class="post-item"]')
        for article in articles:
            item = CnblogsItem()
            title = article.xpath('.//a[@class="post-item-title"]/text()').extract_first()
            url = article.xpath('.//a[@class="post-item-title"]/@href').extract_first()
            desc = article.xpath('.//p[@class="post-item-summary"]/text()').extract()
            author = article.xpath('.//footer[@class="post-item-foot"]/a/span/text()').extract_first()
            res_desc = desc[0].replace('\n', '').replace(' ', '')
            if not res_desc:
                res_desc = desc[1].replace('\n', '').replace(' ', '')
            item['title'] = title
            item['url'] = url
            item['desc'] = desc
            item['author'] = author
            item['desc'] = res_desc

            yield item

3.修改配置文件,指定pipline,数字表示优先级,越小越大

    ITEM_PIPELINES = {
    'crawl_cnblogs.pipelines.CrawlCnblogsPipeline': 300,
        }

4.写一个pipline:CrawlCnblogsPipeline
-open_spider:数据初始化,打开文件,打开数据库链接
-process_item:真正存储的地方
-一定不要忘了return item,交给后续的pipline继续使用
-close_spider:销毁资源,关闭文件,关闭数据库链接

pipelines.py


class CnblogsFilePipeline:
    def open_spider(self, spider):

        print('open')

        self.f = open('cnblogs.txt', 'w', encoding='utf-8')

    def close_spider(self, spider):

        print('close')

        self.f.close()

    def process_item(self, item, spider):

        self.f.write('标题:%s,作者:%s\n' % (item['title'], item['author']))

        return item

import pymysql

class CnblogsMysqlPipeline:
    def open_spider(self, spider):
        self.conn = pymysql.connect(user='root',
                                    password="密码",
                                    host='127.0.0.1',
                                    port=3306,
                                    database='cnblogs')
        self.cursor = self.conn.cursor()

    def close_spider(self, spider):

        self.cursor.close()
        self.conn.close()

    def process_item(self, item, spider):
        sql = 'insert into article (title,desc,url,pub_time,author,content) values (%s,%s,%s,%s,%s,%s)'
        self.cursor.execute(sql, args=[item['title'], item['desc'], item['url'], item['pub_time'], item['author'],
                                       item['content']])
        self.conn.commit()

        return item

六、爬虫和下载中间件

在settings.py中可看到以下配置

SPIDER_MIDDLEWARES 爬虫中间件 (了解即可,用的少)

SPIDER_MIDDLEWARES = {
    'cnblogs.middlewares.CnblogsSpiderMiddleware': 543,
}

DOWNLOADER_MIDDLEWARES 下载中间件(用的多)

DOWNLOADER_MIDDLEWARES = {
    'cnblogs.middlewares.CnblogsDownloaderMiddleware': 543,
}

最重要的是下载中间件,里面的两个方法
middlewares.py

class CnblogsDownloaderMiddleware:

    def process_request(self, request, spider):

        return None

    def process_response(self, request, response, spider):

        return response

七、加代理,cookie,header

1.加代理

在下载中间件中重写process_request

def process_request(self, request, spider):
    request.meta['proxy'] = 'http://221.6.215.202:9091'
    return None

2.cookie

在中间件中重写process_request

def process_request(self, request, spider):
    request.cookies['name']='bbc'
    return None

3.header

在中间件中重写process_request

def process_request(self, request, spider):
    request.headers['Auth']='asdfasdfasdfasdf'
    request.headers['USER-AGENT']='ssss'

八、集成selenium

scrapy框架的下载器,是无法触发js的,当我们需要的内容时通过js获取到的,可以在scrapy框架中对下载器进行selenium的集成,实质就是将一些需要触发js的请求交由selenium来处理,其他的请求照常走下载器。

第一步:
在爬虫类中为需要使用selenium的爬虫添加一个类属性以及一个类方法

import scrapy
from selenium import webdriver

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

def close(spider, reason):
      spider.driver.close()

第二步:
创建一个下载中间件,重写process_request

class CnblogsDownloaderMiddlewareProxy:
    def process_request(self, request, spider):
        url=request.url
        if 'sitehome' in url:
            spider.driver.get(url)
            response=HtmlResponse(url=url,body=spider.driver.page_source.encode('utf-8'))
            return response
        else:
            return None

九、去重规则源码分析

scrapy 实现了去重,爬过的网址不会再爬了

scrapy使用了集合进行去重

第一步:
找到scrapy的配置文件
scrapy.settings下的default_settings.py中
找到DUPEFILTER_CLASS = ‘scrapy.dupefilters.RFPDupeFilter’

第二步:
找到RFPDupeFilter,该类负责处理指纹判断是否爬取过

class RFPDupeFilter(BaseDupeFilter):
        def request_seen(self, request: Request) -> bool:
        fp = self.request_fingerprint(request)
        if fp in self.fingerprints:
            return True
        self.fingerprints.add(fp)
        if self.file:
            self.file.write(fp + '\n')
        return False

第三步:
找到爬虫对象执行的入口
在scrapy.spiders下的_ init _.py中

class Spider(object_ref):
    def start_requests(self):
        if not self.start_urls and hasattr(self, 'start_url'):
            raise AttributeError(
                "Crawling could not start: 'start_urls' not found "
                "or empty (but found 'start_url' attribute instead, "
                "did you miss an 's'?)")
        for url in self.start_urls:
            yield Request(url, dont_filter=True)

第四步:
爬虫执行后在调度器中去重
在scrapy.core下的scheduler中

class Scheduler(BaseScheduler):
    def enqueue_request(self, request: Request) -> bool:
        if not request.dont_filter and self.df.request_seen(request):
            self.df.log(request, self.spider)
            return False
        dqok = self._dqpush(request)
        if dqok:
            self.stats.inc_value('scheduler/enqueued/disk', spider=self.spider)
        else:
            self._mqpush(request)
            self.stats.inc_value('scheduler/enqueued/memory', spider=self.spider)
        self.stats.inc_value('scheduler/enqueued', spider=self.spider)
        return True

以上就是scrapy框架的去重解析

十、布隆过滤器使用

中小型的去重使用集合、字典等去重比较适合,并发量大了之后再使用这些方式进行就会极大的影响效率和内存。
这个时候可以使用布隆过滤器。

布隆过滤器是以bit为单位的数组,数组初始状态每一位都是0,当需要去重的数据经过哈希函数处理后,将数组对应位置的0变为1,一但哈希函数计算的位置都是1就表明该数据已存在,反之只要有一位不为1就表示该数据此前没有存在过。布隆过滤器,占用空间很小,但只能进行过滤对比,无法取出值,所以应用时需要考虑是否需要取出已存在的值。

scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫

; 安装

pip3 install pybloom_live

1.定长的布隆过滤器

from pybloom_live import BloomFilter

bf = BloomFilter(capacity=1000)
url='www.baidu.com'
bf.add(url)
print(url in bf)
print("www.liuqingzheng.top" in bf)

2.可自动扩容的布隆过滤器

from pybloom_live import ScalableBloomFilter

bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
url = "www.cnblogs.com"
url2 = "www.liuqingzheng.top"
bloom.add(url)
print(url in bloom)
print(url2 in bloom)

3.布隆过滤器方法

add、madd

布隆过滤器对象.add {key} {item}

布隆过滤器对象.madd {key} {item} [item...]

往过滤器中添加元素。如果key不存在,过滤器会自动创建。

exists、 mexists

布隆过滤器对象.exists {key} {item}

布隆过滤器对象.mexists {key} {item} [item...]

判断过滤器中是否存在该元素,不存在返回0,存在返回1。

十一、redis实现分布式爬虫

第一步:
安装scrapy-redis

pip3 install scrapy-redis

第二步:
改造爬虫类

from scrapy_redis.spiders import RedisSpider
    class CnblogSpider(RedisSpider):
        name = 'cnblog_redis'
        allowed_domains = ['cnblogs.com']

        redis_key = 'myspider:start_urls'

    def parse(self, response):
        ...

第三步:
配置文件配置


REDIS_HOST = 'localhost'
REDIS_PORT = 6379
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

ITEM_PIPELINES = {

   'scrapy_redis.pipelines.RedisPipeline': 400,
}

第四步:
在多台机器上启动scrapy项目

第五步:
向redis放入一个起始url,之后分布式项目才会开始爬取

可以在redis中添加、或者使用脚本添加
lpush myspider:start_urls value http://www.cnblogs.com/

Original: https://blog.csdn.net/kdq18486588014/article/details/126164451
Author: 山上有个车
Title: scrapy框架——架构介绍、安装、项目创建、目录介绍、使用、持久化方案、集成selenium、去重规则源码分析、布隆过滤器使用、redis实现分布式爬虫

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/789059/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球