爬虫 + 数据分析 - 7 CrawlSpider(全站爬取), 分布式, 增量式爬虫

一.全站爬取(CrawlSpider)

  1.基本概念

作用:就是用于进行全站数据的爬取
    - CrawlSpider就是Spider的一个子类
    - 如何新建一个基于CrawlSpider的爬虫文件
        - scrapy genspider -t crawl xxx www.xxx.com
    - LinkExtractor连接提取器:根据指定规则(正则)进行连接的提取
    - Rule规则解析器:将链接提取器提取到的链接进行请求发送,然后对获取的页面数据进行
        指定规则(callback)的解析
    - 一个链接提取器对应唯一一个规则解析器

  2.项目示例

①.爬取抽屉网多页数据对象

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

class ChoutiSpider(CrawlSpider):
    name = ‘chouti‘
    # allowed_domains = [‘www.ccc.coim‘]
    start_urls = [‘https://dig.chouti.com/all/hot/recent/1‘]

    #链接提取器:从起始url对应的页面中提取符合规则的链接。allow=》正则
    link= LinkExtractor(allow=r‘/all/hot/recent/\d+‘)
    rules = (
        #规则解析器:将链接提取器提取到的链接对应的页面源码进行指定规则的解析
        Rule(link, callback=‘parse_item‘, follow=True),
        #follow:True 将连接提取器 继续 作用到 连接提取器提取出来的链接 对应的页面源码中
        #False:只提取当前页匹配的地址
    )

    def parse_item(self, response):
        print(response)

②爬取阳光热线 多页及详情页数据,持久化存储

#爬虫文件中:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from sunLinePro.items import SunlineproItem,ContentItem
class SunSpider(CrawlSpider):
    name = ‘sun‘
    # allowed_domains = [‘www.xxx.com‘]
    start_urls = [‘http://wz.sun0769.com/index.php/question/questionType?type=4&page=‘]

    link= LinkExtractor(allow=r‘type=4&page=\d+‘)#提取页码连接

    link1 = LinkExtractor(allow=r‘question/2019\d+/\d+\.shtml‘)#提取详情页的链接
    rules = (
        Rule(link, callback=‘parse_item‘, follow=False),
        Rule(link1, callback=‘parse_detail‘),
    )
    #解析出标题和网友名称
    def parse_item(self, response):
        tr_list = response.xpath(‘//*[@id="morelist"]/div/table[2]//tr/td/table//tr‘)
        for tr in tr_list:
            title = tr.xpath(‘./td[2]/a[2]/text()‘).extract_first()
            netFriend = tr.xpath(‘./td[4]/text()‘).extract_first()
            item = SunlineproItem()
            item[‘title‘] = title
            item[‘netFriend‘] = netFriend

            yield item
    #解析出新闻的内容
    def parse_detail(self,response):
        content = response.xpath(‘/html/body/div[9]/table[2]//tr[1]/td/div[2]//text()‘).extract()
        content = ‘‘.join(content)

        item = ContentItem()
        item[‘content‘] = content

        yield item
#在 items.py 文件中:

import scrapy

class SunlineproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    netFriend = scrapy.Field()

class ContentItem(scrapy.Item):
    # define the fields for your item here like:
    content = scrapy.Field()
#在管道文件中:

class SunlineproPipeline(object):
    def process_item(self, item, spider):
        #接收到的item究竟是什么类型的
        if item.__class__.__name__ ==‘SunlineproItem‘:
            print(item[‘title‘],item[‘netFriend‘])
        else:
            print(item[‘content‘])
        return item

二.分布式

  1.基本概念

    - 概念:可以将一组程序执行在多台机器上(分布式机群),使其进行数据的分布爬取。
    - 原生的scrapy框架是否可以实现分布式?
        - 不可以?
            - 调度器不可以被分布式机群共享
            - 管道不可以被分布式机群共享

    - 借助scrapy-redis这个模块帮助scrapy实现分布式
    - scrapy-redis作用:
        - 可以提供可以被共享的管道和调度器

   安装模块:
    - pip install scrapy-redis

  2.实现流程

   - 分布式的实现流程:
        - 导包:from scrapy_redis.spiders import RedisCrawlSpider
        - 修改爬虫文件的代码:
            - 将当前爬虫类的父类修改成RedisCrawlSpider
            - 将start_urls删除
            - 添加一个新属性redis_key = ‘ts‘:可以被共享的调度器中的队列名称

       - 设置管道:
                    ITEM_PIPELINES = {
                ‘scrapy_redis.pipelines.RedisPipeline‘: 400
               # ‘scrapyRedisPro.pipelines.ScrapyredisproPipeline‘: 300,
            }

       - 设置调度器:
        # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
        # 使用scrapy-redis组件自己的调度器
        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
        SCHEDULER_PERSIST = True

        - 指定redis服务器
            REDIS_HOST = ‘192.168.12.154‘
            REDIS_PORT = 6379

        - 配置redis:
            修改Redis的配置文件:redis.windows.conf
                - #bind 127.0.0.1
                - protected-mode no
        - 携带配置文件启动redis服务
            - redis-server ./redis.windows.conf
        - 启动redis客户端

        - 执行工程:scrapy runspider xxx.py (执行爬虫py文件)

        - 手动将起始url扔入调度器的队列中(redis-cli):lpush ts www.xxx.com

        - redis-cli: items:xxx

  3.示例(阳光热线的爬取):

# 在爬虫文件中

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapyRedisPro.items import ScrapyredisproItem
from scrapy_redis.spiders import RedisCrawlSpider
from scrapy_redis.spiders import RedisSpider

class TestSpider(RedisCrawlSpider):
    name = ‘test‘
    # allowed_domains = [‘www.xxx.com‘]
    # start_urls = [‘http://www.xxx.com/‘]
    redis_key = ‘ts‘ #可以被共享的调度器中的队列名称
    rules = (
        Rule(LinkExtractor(allow=r‘type=4&page=\d+‘), callback=‘parse_item‘, follow=True),
    )

    def parse_item(self, response):
        tr_list = response.xpath(‘//*[@id="morelist"]/div/table[2]//tr/td/table//tr‘)
        for tr in tr_list:
            title = tr.xpath(‘./td[2]/a[2]/text()‘).extract_first()
            netFriend = tr.xpath(‘./td[4]/text()‘).extract_first()
            item = ScrapyredisproItem()
            item[‘title‘] = title
            item[‘net‘] = netFriend

            yield item
            #提交的item必须保证提交到可以被共享的管道中
#在items.py 文件中:

import scrapy

class ScrapyredisproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    net = scrapy.Field()
#在setting.py文件中:

# -*- coding: utf-8 -*-

# Scrapy settings for scrapyRedisPro project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://doc.scrapy.org/en/latest/topics/settings.html
#     https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://doc.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = ‘scrapyRedisPro‘

SPIDER_MODULES = [‘scrapyRedisPro.spiders‘]
NEWSPIDER_MODULE = ‘scrapyRedisPro.spiders‘
USER_AGENT = ‘Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36‘

# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = ‘scrapyRedisPro (+http://www.yourdomain.com)‘

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   ‘Accept‘: ‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8‘,
#   ‘Accept-Language‘: ‘en‘,
#}

# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    ‘scrapyRedisPro.middlewares.ScrapyredisproSpiderMiddleware‘: 543,
#}

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    ‘scrapyRedisPro.middlewares.ScrapyredisproDownloaderMiddleware‘: 543,
#}

# Enable or disable extensions
# See https://doc.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    ‘scrapy.extensions.telnet.TelnetConsole‘: None,
#}

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    ‘scrapy_redis.pipelines.RedisPipeline‘: 400
   # ‘scrapyRedisPro.pipelines.ScrapyredisproPipeline‘: 300,
}

# 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
SCHEDULER_PERSIST = True

REDIS_HOST = ‘192.168.12.154‘
REDIS_PORT = 6379

# 网速号可开多个线程(影响不大)
CONCURRENT_REQUESTS = 2   

三.增量式爬虫

概念:通过爬虫程序监测某网站数据更新的情况,以便可以爬取到该网站更新出的新数据。
如何进行增量式的爬取工作:
  在发送请求之前判断这个URL是不是之前爬取过
  在解析内容后判断这部分内容是不是之前爬取过
  写入存储介质时判断内容是不是已经在介质中存在
    分析:
        不难发现,其实增量爬取的核心是去重, 至于去重的操作在哪个步骤起作用,只能说各有利弊。在我看来,      前两种思路需要根据实际情况取一个(也可能都用)。第一种思路适合不断有新页面出现的网站,比如说小说的新章节,      每天的最新新闻等等;第二种思路则适合页面内容会更新的网站。第三个思路是相当于是最后的一道防线。      这样做可以最大程度上达到去重的目的。

去重方法:
  将爬取过程中产生的url进行存储,存储在redis的set中。当下次进行数据爬取时,    首先对即将要发起的请求对应的url在存储的url的set中做判断,如果存在则不进行请求,否则才进行请求。
  对爬取到的网页内容进行唯一标识的制定,然后将该唯一表示存储至redis的set中。当下次爬取到网页数据的时候,    在进行持久化存储之前,首先可以先判断该数据的唯一标识在redis的set中是否存在,在决定是否进行持久化存储。
- 概念:监测网上数据更新的情况。

    1.对url去重        - 利用 redis中的 sadd存储类型    2.对数据去重        - 数据指纹

  1.对url去重(爬取4567电影网数据)

#在爬虫文件中:

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from redis import Redis
from moviePro.items import MovieproItem
class MovieSpider(CrawlSpider):
    name = ‘movie‘
    # allowed_domains = [‘www.xxx.com‘]
    start_urls = [‘https://www.4567tv.tv/frim/index1.html‘]
    link = LinkExtractor(allow=r‘/frim/index1-\d+.html‘)
    rules = (
        Rule(link, callback=‘parse_item‘, follow=False),
    )
    conn = Redis(host=‘127.0.0.1‘,port=6379)
    #解析电影的名称和详情页的url
    def parse_item(self, response):
        li_list = response.xpath(‘/html/body/div[1]/div/div/div/div[2]/ul/li‘)
        for li in li_list:
            title = li.xpath(‘./div/a/@title‘).extract_first()
            detail_url = ‘https://www.4567tv.tv‘+li.xpath(‘./div/a/@href‘).extract_first()
            item = MovieproItem()
            item[‘title‘] = title

            #判断该详情页的url是否进行请求发送
            ex = self.conn.sadd(‘movie_detail_urls‘,detail_url)

            if ex == 1:#说明detail_url不存在于redis的set中
                print(‘已有最新数据更新,请爬......‘)
                yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={‘item‘:item})
            else:
                print(‘暂无新数据的更新!!!‘)

    def parse_detail(self,response):
        item = response.meta[‘item‘]
        desc = response.xpath(‘/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()‘).extract_first()
        item[‘desc‘] = desc

        yield item
#在items.py 文件中

import scrapy

class MovieproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    desc = scrapy.Field()
#在管道文件中:

class MovieproPipeline(object):
    def process_item(self, item, spider):
        dic = {
            ‘title‘:item[‘title‘],
            ‘desc‘:item[‘desc‘]
        }
        conn = spider.conn
        #存储
        conn.lpush(‘movie_data‘,dic)
        return item

  2.对数据的去重(糗事百科)

用hashlib.sha256生成唯一的 数据指纹

存放在redis中的  sadd数据类型中
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from incrementByDataPro.items import IncrementbydataproItem
from redis import Redis
import hashlib
class QiubaiSpider(CrawlSpider):
    name = ‘qiubai‘
    # allowed_domains = [‘www.xxx.com‘]
    start_urls = [‘https://www.qiushibaike.com/text/‘]

    rules = (
        Rule(LinkExtractor(allow=r‘/text/page/\d+/‘), callback=‘parse_item‘, follow=True),
        Rule(LinkExtractor(allow=r‘/text/$‘), callback=‘parse_item‘, follow=True),
    )
    #创建redis链接对象
    conn = Redis(host=‘127.0.0.1‘,port=6379)
    def parse_item(self, response):
        div_list = response.xpath(‘//div[@id="content-left"]/div‘)

        for div in div_list:
            item = IncrementbydataproItem()
            item[‘author‘] = div.xpath(‘./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()‘).extract_first()
            item[‘content‘] = div.xpath(‘.//div[@class="content"]/span/text()‘).extract_first()

            #将解析到的数据值生成一个唯一的标识进行redis存储
            source = item[‘author‘]+item[‘content‘]
            source_id = hashlib.sha256(source.encode()).hexdigest()
            #将解析内容的唯一表示存储到redis的data_id中
            ex = self.conn.sadd(‘data_id‘,source_id)

            if ex == 1:
                print(‘该条数据没有爬取过,可以爬取......‘)
                yield item
            else:
                print(‘该条数据已经爬取过了,不需要再次爬取了!!!‘)
# 在管道文件中:

from redis import Redis
class IncrementbydataproPipeline(object):
    conn = None

    def open_spider(self, spider):
        self.conn = Redis(host=‘127.0.0.1‘, port=6379)

    def process_item(self, item, spider):
        dic = {
            ‘author‘: item[‘author‘],
            ‘content‘: item[‘content‘]
        }
        # print(dic)
        self.conn.lpush(‘qiubaiData‘, dic)
        return item

原文地址:https://www.cnblogs.com/lw1095950124/p/11130251.html

时间: 2024-07-29 10:47:46

爬虫 + 数据分析 - 7 CrawlSpider(全站爬取), 分布式, 增量式爬虫的相关文章

python爬虫---CrawlSpider实现的全站数据的爬取,分布式,增量式,所有的反爬机制

CrawlSpider实现的全站数据的爬取 新建一个工程 cd 工程 创建爬虫文件:scrapy genspider -t crawl spiderName www.xxx.com 连接提取器LinkExtractor 可以根据指定的规则对指定的连接进行提取 提取的规则就是构造方法中的allow('正则表达式')参数决定 规则解析器Rule 可以将将连接提取器提取到的连接进行请求发送,可以根据指定的规则(callback)对请求到的数据进行解析 follow=True:将连接提取器 继续作用到

【Scrapy框架之CrawlSpider全站爬取】--2019-08-06 15:17:42

原创链接: http://106.13.73.98/__/144/ 起 提问: 如果想要快速爬取网站的全站数据,有几种实现方法? 基于Scrapy框架中 Spider 的递归爬取来实现(Request模块递归回调parse方法) 基于 CrawlSpider 的自动爬取来实现(更加高效简洁) ???????CrawlSpider 是 Spider 的一个子类,除了继承了 Spider 的特性和功能外,还派生了其自己独有的更加强大的特性和功能.其中最为显著的功能就是 LinkExtractors:

【Scrapy框架之CrawlSpider全站爬取】 𓄛

原文: http://blog.gqylpy.com/gqy/369 起 提问: 如果想要快速爬取网站的全站数据,有几种实现方法? 基于Scrapy框架中 Spider 的递归爬取来实现(Request模块递归回调parse方法) 基于 CrawlSpider 的自动爬取来实现(更加高效简洁) ???????CrawlSpider 是 Spider 的一个子类,除了继承了 Spider 的特性和功能外,还派生了其自己独有的更加强大的特性和功能.其中最为显著的功能就是 LinkExtractors

爬虫5 scrapy框架2 全站爬取cnblogs, scarpy请求传参, 提高爬取效率, 下载中间件, 集成selenium, fake-useragent, 去重源码分析, 布隆过滤器, 分布式爬虫, java等语言概念补充, bilibili爬视频参考

1 全站爬取cnblogs # 1 scrapy startproject cnblogs_crawl # 2 scrapy genspider cnblogs www.cnblogs.com 示例: # cnblogs_crawl/cnblogs_crawl/spiders/cnblogs.py import scrapy from cnblogs_crawl.items import CnblogsCrawlItem from scrapy.http import Request class

【python爬虫】根据查询词爬取网站返回结果

最近在做语义方面的问题,需要反义词.就在网上找反义词大全之类的,但是大多不全,没有我想要的.然后就找相关的网站,发现了http://fanyici.xpcha.com/5f7x868lizu.html,还行能把"老师"-"学生","医生"-"病人"这样对立关系的反义词查出来. 一开始我想把网站中数据库中存在的所有的词语都爬出来(暗网爬虫),但是分析了url的特点: http://fanyici.xpcha.com/5f7x86

python爬虫实例详细介绍之爬取大众点评的数据

python 爬虫实例详细介绍之爬取大众点评的数据 一. Python作为一种语法简洁.面向对象的解释性语言,其便捷性.容易上手性受到众多程序员的青睐,基于python的包也越来越多,使得python能够帮助我们实现越来越多的功能.本文主要介绍如何利用python进行网站数据的抓取工作.我看到过利用c++和Java进行爬虫的代码,c++的代码很复杂,而且可读性.可理解性较低,不易上手,一般是那些高手用来写着玩加深对c++的理解的,这条路目前对我们不通.Java的可读性还可以,就是代码冗余比较多,

爬虫学习 Python网络爬虫第三弹《爬取get请求的页面数据》

爬虫学习 Python网络爬虫第三弹<爬取get请求的页面数据> 一.urllib库 urllib是Python自带的一个用于爬虫的库,其主要作用就是可以通过代码模拟浏览器发送请求.其常被用到的子模块在Python3中的为urllib.request和urllib.parse,在Python2中是urllib和urllib2. 二.由易到难的爬虫程序: 1.爬取百度首页面所有数据值 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #导包 4

全站爬取cnblogs

全站爬取cnblogs 创建 """ # 1 scrapy startproject cnblogs_crawl # 2 scrapy genspider cnblogs www.cnblogs.com """ 代码演示 """ # -*- coding: utf-8 -*- import scrapy from cnblogs_crawl.items import CnblogsCrawlItem from scr

增量式爬虫

目录 增量式爬虫 增量式爬虫 案例: 爬取4567tv网站中所有的电影详情数据 需求:爬取糗事百科中的段子和作者数据. 增量式爬虫 当我们在浏览相关网页的时候会发现,某些网站定时会在原有网页数据的基础上更新一批数据,例如某电影网站会实时更新一批最近热门的电影.小说网站会根据作者创作的进度实时更新最新的章节数据等等.那么,类似的情景,当我们在爬虫的过程中遇到时,我们是不是需要定时更新程序以便能爬取到网站中最近更新的数据呢? 增量式爬虫 概念:通过爬虫程序监测某网站数据更新的情况,以便可以爬取到该网