首页 > 其他分享 >scrapy框架之持久化存储

scrapy框架之持久化存储

时间:2024-04-05 21:45:53浏览次数:19  
标签:存储 持久 image Item scrapy 数据

一、为什么需要持久化存储?

持久化存储是指将数据在程序结束后仍然保持在存储介质中的能力。这种存储方式对于许多应用程序和系统至关重要,原因如下:

  1. 数据持久性: 持久化存储确保数据在程序关闭后不会丢失。这对于需要长期保存数据的应用程序至关重要,如数据库系统、文件存储等。

  2. 状态保存: 对于需要保存应用程序状态的情况,持久化存储可以确保用户下次访问时可以恢复到之前的状态,提供更好的用户体验。

  3. 数据共享: 通过持久化存储,不同的应用程序或用户可以共享数据,从而实现数据的持久性和共享性。

  4. 数据恢复: 在系统或应用程序发生故障或意外关闭时,持久化存储可以帮助恢复数据,避免数据丢失。

  5. 离线访问: 持久化存储使得应用程序可以在离线状态下访问数据,提高了应用程序的灵活性和可用性。

  6. 数据分析和历史记录: 持久化存储允许数据长期保存,以便进行数据分析、生成历史记录或用于回顾和审计等目的。

总的来说,持久化存储对于确保数据的安全性、可靠性和持续性至关重要,使得应用程序能够更好地处理数据、管理状态,并提供更好的用户体验。

二、如何进行持久化存储?

进行持久化存储通常涉及以下几种常见的方法:

  1. 数据库系统:

    • 关系型数据库: 如MySQL、PostgreSQL、SQL Server等,通过SQL语言进行数据操作。
    • 非关系型数据库: 如MongoDB、Redis、Cassandra等,适用于不同类型的数据存储需求。
  2. 文件系统:

    • 将数据以文件的形式存储在磁盘上,可以是文本文件、JSON文件、XML文件等。
    • 可以通过文件读写操作来实现数据的持久化存储。
  3. 键值存储:

    • 使用键值对的方式存储数据,如Redis、Memcached等。
    • 适用于需要快速读写和简单数据结构的场景。
  4. 对象存储:

    • 将数据以对象的形式存储在云存储服务中,如AWS S3、Google Cloud Storage等。
    • 适用于需要大规模存储和访问的场景。
  5. 日志文件:

    • 通过记录应用程序的操作日志,可以实现数据的持久化存储和恢复。
    • 适用于需要追踪操作历史和进行故障恢复的场景。
  6. ORM(对象关系映射):

    • 将对象和数据库之间的映射关系抽象出来,简化数据持久化操作。
    • ORM框架如Hibernate、Sequelize等可以帮助开发者进行持久化存储。
  7. 缓存:

    • 使用缓存技术如Redis、Memcached等,可以将数据暂时存储在内存中,提高数据访问速度。
    • 缓存可以与持久化存储结合使用,提高系统性能和响应速度。

选择合适的持久化存储方式取决于应用程序的需求、数据量、访问模式等因素。开发人员需要根据具体情况选择最适合的持久化存储方案,并确保数据的安全性、一致性和可靠性。

三、Scrapy数据持久化

1、方法简介

  • Scrapy内建了Item Pipeline机制,可以通过编写自定义Pipeline实现数据的持久化存储。
  • 在Pipeline中可以调用内置方法将Item对象序列化为JSON或CSV格式,并写入文件。

2、parse 函数返回值

在Scrapy中,parse函数是用于处理下载的响应并返回提取的数据以及跟进的URL的函数。parse函数可以返回两种类型的对象:

(1)字典(Dictionary)或 Item 对象

  • 最常见的做法是从响应中提取数据,然后将数据以字典或 Scrapy 的 Item 对象的形式返回。
  • 这些数据将会被传递到 Item Pipeline 进行后续处理,比如数据清洗、验证和持久化存储。
def parse(self, response):
    data = {
        'title': response.css('h1::text').get(),
        'content': response.css('p::text').getall(),
    }
    return data

(2)Request 对象

  • parse 函数还可以返回一个或多个 Request 对象,用于继续爬取其他页面。
  • 返回的 Request 对象可以指向新的URL,并指定一个回调函数来处理该URL对应的响应。
def parse(self, response):
    # 提取页面中的链接并跟进
    for link in response.css('a::attr(href)').getall():
        yield response.follow(link, callback=self.parse_details)

def parse_details(self, response):
    # 处理详情页面的数据
    data = {
        'title': response.css('h1::text').get(),
        'content': response.css('p::text').getall(),
    }
    return data

在以上示例中,parse 函数返回了一个 Request 对象,让 Scrapy 继续爬取链接并调用 parse_details 函数处理响应。

总的来说,parse 函数的返回值可以是字典、Item 对象或 Request 对象。根据需求,可以选择返回不同类型的对象来实现数据提取、处理和跟进链接的功能。

3、基于终端指令的存储

Scrapy提供了命令行工具来执行数据的导出操作,无需修改代码即可完成。

以下是一些常用的命令行参数和对应的存储方式:

(1)存储为 JSON 文件

  • 使用 -o 参数可以将提取的数据存储为 JSON 文件。
  • 例如,将提取的数据存储为 data.json 文件:
    scrapy crawl spider_name -o data.json
    

(2)存储为 CSV 文件

  • 使用 -o 参数可以将提取的数据存储为 CSV 文件。
  • 例如,将提取的数据存储为 data.csv 文件:
    scrapy crawl spider_name -o data.csv
    

(3)存储为 XML 文件

  • 使用 -o 参数可以将提取的数据存储为 XML 文件。
  • 例如,将提取的数据存储为 data.xml 文件:
    scrapy crawl spider_name -o data.xml
    

(4)存储为 JSON Lines 文件

  • 使用 -o 参数可以将提取的数据存储为 JSON Lines 文件。
  • 例如,将提取的数据存储为 data.jl 文件:
    scrapy crawl spider_name -o data.jl
    

总的来说,这些命令会在项目运行结束后,将爬取到的数据自动按照指定格式保存到本地文件中。这样不仅便于后期数据分析和处理,也方便与其他系统进行数据集成。

4、基于管道的持久化存储

在Scrapy中,可以通过编写自定义的Item Pipeline来实现持久化存储数据的逻辑。以下是实现基于管道的持久化存储的一般步骤:

(1)创建自定义的Item Pipeline

在Scrapy项目中的pipelines.py文件中定义一个自定义的Item Pipeline类,该类需要包含处理数据的方法,例如process_item(self, item, spider)。在这个方法中,你可以编写逻辑来处理和存储数据。

(2)配置Item Pipeline

在Scrapy项目的settings.py文件中,找到ITEM_PIPELINES设置,并为你的自定义Item Pipeline指定一个优先级。优先级是一个整数,数字越小,优先级越高。例如:

ITEM_PIPELINES = {
    'myproject.pipelines.MyCustomPipeline': 300,
}

(3)实现数据存储逻辑

在自定义的Item Pipeline类中,实现process_item方法来处理数据。你可以在这个方法中将数据存储到文件、数据库或其他地方。例如,将数据存储到数据库可以使用ORM框架或者直接执行SQL语句。

(4)数据存储的示例

下面是一个简单的示例,展示如何将提取的数据存储到文件中:

class MyCustomPipeline:
    def process_item(self, item, spider):
        with open('data.txt', 'a') as f:
            f.write(f"{item}\n")
        return item

(5)注意事项

  • 在处理数据时,记得处理可能出现的异常情况,确保数据存储的稳定性和完整性。
  • 在编写自定义的Item Pipeline时,可以根据需要添加数据处理、清洗、验证等逻辑。

通过编写自定义的Item Pipeline,可以根据需求实现特定的数据存储逻辑,将提取的数据持久化存储到文件、数据库或其他存储介质中。

5、总结

基于终端指令的存储和基于管道的持久化存储各有优缺点,具体如下:

(1)基于终端指令的存储

优点:

  1. 简单直接: 使用终端指令进行存储通常是直接的方法,不需要编写额外的代码或配置。

  2. 快速操作: 通过终端指令可以快速将数据存储到文件或其他目标中,适用于一次性的简单存储需求。

缺点:

  1. 有限灵活性: 终端指令的存储通常是静态的,难以实现复杂的数据处理逻辑或自定义存储需求。

  2. 不易扩展: 难以实现数据的实时处理、筛选、清洗等操作,不适用于需要复杂数据处理的场景。

(2)基于管道的持久化存储

优点:

  1. 灵活性高: 可以编写自定义的Item Pipeline来实现复杂的数据处理逻辑,满足各种存储需求。

  2. 可扩展性强: 可以根据需求扩展和定制数据处理流程,方便实现数据清洗、验证、存储等功能。

  3. 结构清晰: 使用Item Pipeline可以将数据处理逻辑模块化,使代码结构更清晰,易于维护和扩展。

缺点:

  1. 复杂性: 编写自定义的Item Pipeline需要一定的开发经验和技能,对于初学者可能有一定的学习曲线。

  2. 维护成本: 需要花费一定的时间和精力来编写和维护自定义的Item Pipeline,特别是在处理复杂数据处理逻辑时。

综上所述,基于终端指令的存储适用于简单、快速的存储需求,而基于管道的持久化存储适用于需要复杂数据处理和定制化存储逻辑的场景。根据具体需求和项目规模,选择合适的存储方式可以提高效率和代码质量。

四、完整示例

1、修改settings.py 主要参数

#关闭robot.txt协议
ROBOTSTXT_OBEY = False

#页面延迟下载,我这里测试,可以先不设置
DOWNLOAD_DELAY = 1

# 是否启用Cookie
COOKIES_ENABLED = True

#请求头 
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'
}
#打开下载器
DOWNLOADER_MIDDLEWARES = {
    'ScrapyDemmo.middlewares.ScrapydemmoDownloaderMiddleware': 543,
}
#打开优先级,并添加自己编写的图片下载管道
ITEM_PIPELINES = {
   'ScrapyDemmo.pipelines.ScrapydemmoPipeline': 300,
   'ScrapyDemmo.pipelines.ImageDownloadPipeline': 300,
}
#添加下载储存目录
IMAGES_STORE = 'D:\Python\pic'

# 文件保存时间
#IMAGES_EXPIRES = 90

2、定义Item字段(Items.py)

本项目用于下载图片,因此可以仅构建图片名和图片地址字段。

import scrapy


class ScrapydemmoItem(scrapy.Item):
	#图片下载链接
    image_url = scrapy.Field()
    #图片名称
    image_name = scrapy.Field()

3、编写爬虫文件(spiders目录下)

这里文件名为:image_download.py

以前用requests库和BeautifulSoup库下载图片,这里就不需要了,scrapy自带相关函数和方法。

scrapy元素定位,提供三种方式,正则、Xpath表达式、css。
我这里有xpath定位方式。

import scrapy
import re
from ..items import ScrapydemmoItem


class ImageSpider(scrapy.Spider):
    name = 'image_download'
    allowed_domains = ['pic.netbian.com']
    start_urls = ['https://pic.netbian.com/index.html']

    def parse(self, response):
        # 导入Items.py字段
        items = ScrapydemmoItem()
        # 获取所有链接列表
        lists = response.xpath('//*[@id="main"]/div[3]/ul/li')
        # 点位元素循环获取图片链接和图片名称
        try:
            for i in lists:
                # 图片名称
                image_name = i.xpath('./a/span/img/@alt').get()
                print(image_name)
                # 图片链接
                image_url = 'https://pic.netbian.com/'+ i.xpath('./a/span/img/@src').get()
                items['image_url'] = image_url.replace('.278.154.jpg', '')
                # 图片格式类型
                image_type = re.sub(r'h.*\d+.', '', items['image_url'])
                # 拼接文件名,图片名称+图片格式
                items['image_name'] = '{}.{}'.format(image_name, image_type)
                yield items
        except:
            pass
        # 循环跳转下一页,并重复返回数据,这里测试先下载1页的图片,总共23页。
        for i in range(2, 3):
            next_url = 'https://desk.3gbizhi.com/deskMV/index_{}.html'.format(i)
            yield scrapy.Request(next_url, callback=self.parse)

关于 yield 的理解,⾸先,如果你还没有对yield有个初步分认识,那么你先把yield看做“return”,这个是直观的,它⾸先是个return。

最主要的不同在于yield在返回值后还可以继续运行接下来的代码,使用的函数会返回一个生成器,而return在返回后就不在执行代码。

以上两个yield:

  • yield items:这里我们通过 yield 返回的不是 Request 对象,而是一个 ScrapydemmoItem 对象。

    • scrap有框架获得这个对象之后,会将这个对象传递给 pipelines.py来做进一步处理。
    • 我们将在 pipelines.py里将传递过来的 scrapy.Item 对象保存到数据库里去。
  • yield scrapy.Request:这里是在爬取完一页的信息后,我们在当前页面获取到了下一页的链接,然后通过 yield 发起请求,并且将 parse 自己作为回调函数来处理下一页的响应。

4、修改管道文件pipelines.py用于下载图片

除了爬取文本,我们可能还需要下载文件、视频、图片、压缩包等,这也是一些常见的需求。scrapy提供了FilesPipeline和ImagesPipeline,专门用于下载普通文件及图片。

继承 Scrapy 内置的 ImagesPipeline,只需要重写get_media_requests 和item_completed函数即可。

from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
from scrapy import Request


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


class ImageDownloadPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        # 下载图片,如果传过来的是集合需要循环下载
        # meta里面的数据是从spider获取,然后通过meta传递给下面方法:file_path
        yield Request(url=item['image_url'], meta={'filename': item['image_name']})

    def item_completed(self, results, item, info):
        # 分析下载结果并剔除下载失败的图片
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        return item

    def file_path(self, request, response=None, info=None):
        # 接收上面meta传递过来的图片名称
        file_name = request.meta['filename']
        return file_name
  • **get_media_requests(): **它的第一个参数 item 是爬取生成的 Item 对象。我们将它的 url 字段取出来,然后直接生成 Request 对象。此 Request 加入调度队列,等待被调度,执行下载。
  • **item_completed(): **它是当单个 Item 完成下载时的处理方法。因为可能有个别图片未成功下载,所以需要分析下载结果并剔除下载失败的图片。该方法的第一个参数 results 就是该 Item 对应的下载结果,它是一个列表形式,列表每一个元素是一个元组,其中包含了下载成功或失败的信息。这里我们遍历下载结果找出所有成功的下载列表。如果列表为空,那么说明该 Item 对应的图片下载失败了,随即抛出异常DropItem,该 Item 忽略。否则返回该 Item,说明此 Item 有效。

以上两个函数即可下载图片了,图片名称为自动已哈希值命名,如:0db6e07054d966513f0a6f315b687f205c7ced90.jpg 这种命名方式不友好,所以我们需要重写 file_path函数,自定义图片名称。

  • file_path():它的第一个参数 request 就是当前下载对应的 Request 对象。这个方法用来返回保存的文件名,接收上面meta传递过来的图片名称,将图片以原来的名称和定义格式进行保存。

5、编写执行文件run.py运行

在项目下新建run.py作为执行文件

from scrapy import cmdline

#cmdline.execute('scrapy crawl image_download --nolog'.split())
cmdline.execute('scrapy crawl image_download'.split())

6、小结

除了 ImagesPipeline 处理图片外,还有 FilesPipeline 可以处理文件,使用方法与图片类似,事实上 ImagesPipeline 是 FilesPipeline 的子类,因为图片也是文件的一种。

标签:存储,持久,image,Item,scrapy,数据
From: https://www.cnblogs.com/xiao01/p/18116251

相关文章

  • scrapy框架之基本使用
    一、需要了解的命令1、查看帮助scrapy-hscrapy<command>-h第一个命令用于查看全部可用命令的帮助信息第二个命令用于查看特定命令的帮助信息2、全局命令和项目命令Project-only必须切到项目文件夹下才能执行Global的命令则不需要Globalcommands:startproject#......
  • scrapy框架之介绍与安装
    一、Scrapy前言Scrapy是由Python语言开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据,只需要实现少量的代码,就能够快速的抓取。目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(......
  • 【VMware vSAN】创建vSAN Max集群并配置挂载远程数据存储。
    VMwareExplore2023大会上,VMware正式发布了vSANMax,这是VMware的一种全新分解存储架构,可以为vSphere集群提供PB级分解存储。vSANMax是基于vSANExpressStorageArchitecture(ESA)架构构建的,也就是说vSANMax仅在vSAN8ESA架构中可用。vSANHCI是传统的超融合架构,该架构是把计......
  • 基于开源软件构建存储解决方案思考
    近来看了一些IBM的存储产品的资料,有一些收获。依据存储软件和搭配硬件,IBM存储产品的组合,大致分类如下:自研存储软件,搭配自研专有硬件自研存储软件,搭配通用服务器硬件,比如IBMStorageScale自研存储软件,开放云服务,比如IBMCloudstorage开源软件,搭配通用服务器,比如IBMStorage......
  • 深入探究Hibernate:优雅、强大的Java持久化框架(二)
    本系列文章简介:        作为一个优雅而强大的Java持久化框架,Hibernate为开发人员提供了一个简单且高效的方式来处理对象关系映射(ORM)。无论您是一个经验丰富的Hibernate开发者还是一个新手,本系列文章都将带您深入了解Hibernate的内部工作原理和使用技巧。      ......
  • 数据在内存中的存储
    ......
  • [MySQL]存储过程
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/18032044出自【进步*于辰的博客】存储过程的细节很多,而在实际工作中又未必都能涉及这些细节,工作时间一长,就可能忘记,于是特来写这篇文章,既是为自己做个笔记,也是跟大家分享存......
  • 【RISC-V 指令集】RISC-V 向量V扩展指令集介绍(五)- 向量加载和存储
      1.引言以下是《riscv-v-spec-1.0.pdf》文档的关键内容:这是一份关于向量扩展的详细技术文档,内容覆盖了向量指令集的多个关键方面,如向量寄存器状态映射、向量指令格式、向量加载和存储操作、向量内存对齐约束、向量内存一致性模型、向量算术指令格式、向量整数和浮点算术......
  • 03-A64指令集1——加载与存储指令
    本章思考题A64指令集有什么特点?答:它可以处理64位宽的寄存器和数据并且使用64位的指针来访问内存。A64指令集有如下特点。具有特有的指令编码格式。只能运行在AArch64状态。指令的宽度为32位。A64指令集支持64位宽的数据和地址寻址,为什么指令的编码宽度只有32位?答:因为A......
  • 初始scrapy
    楔子Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。安装方式1pipinstallscrapy方式2如果方式1安装不了,在看此方式,如果方式1能正常安装,那就不需要查看此方式了。#1.安装wheel文件......