Python爬虫(十八)爬虫框架scrapy持久化存储

128 阅读6分钟

一:基于终端的持久化存储

1 :功能

只可以将parse方法的返回值存储到本地的文本文件中。

 

2 :代码实例

存储命令格式:

scrapy crawl 爬虫名称 -o 存储文件路径

 

我这里将上一篇中数据解析的代码重新利用一下:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
@Time    : 2022/3/31 18:01
@Author  : camellia
@Email   : 805795955@qq.com
@File    : beike.py
@Software: PyCharm

import time
import scrapy
import asyncio
import requests
import random

# 解析贝壳二手房页面的房源图片及标题
class BeikeSpider(scrapy.Spider):
    name = 'beike'
    # allowed_domains = ['www.beike.com']
    start_urls = ['https://dl.ke.com/ershoufang/']

    def parse(self, response):
        divList = response.xpath('/html/body/div[1]/div[4]/div[1]/div[4]/ul/li[1]')
        title = []
        for item in divList:
            temp = {}
            temp['title'] = item.xpath('//ul[@class="sellListContent"]//img[@class="lj-lazy"]/@title').extract()
            title.append(temp)
        print(title)
        return title

执行存储命令:

scrapy crawl beike -o ./data.json

 

上方代码输出:

[{'title': ['马栏广场没坡,精装修两室,辽师家属楼质量好,2号线''店长推荐!婚房装修,全明户型保持好拎包入住!',……]}]

 

查看项目目录,发现多了一个data.json文件,如下图所示:

q11.png

 

这个时候,我就很好奇了,我可以把他存在json中,我是不是也可以将他存在txt文本中呢?

试一下。

执行:

scrapy crawl beike -O ./data.txt

果然报错了,报错信息如下:

Usage
=====
  scrapy crawl [options] <spider>
crawl: error: Unrecognized output format 'txt'. Set a supported one (('json''jsonlines''jl''csv''xml''marshal''pickle')) after a colon at the end of the output URI (i.e. -o/-O <URI>:<FORMAT>) or as a file extension.

 

报错信息显示,持久化存储只能是 'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle' 格式

 

最后,总结一下基于终端指令的持久化存储的优缺点:

优点:简洁高效便捷

缺点:有较强的局限性(数据只可以存储到指定后缀的文本文件中)

 

二:基于管道的持久化存储

1 :编码流程

(1):数据解析

(2):在item类中定义相关的属性

(3):将解析的数据封装存储到item类型的对象

(4):将item类型的对象提交给管道进行持久化存储

(5):在管道类的process_item中要将其接收到的item对象中存储的数据进行持久化存储操作

(6):在配置文件中开启管道

 

接下来,我们用上方的的小例子修改一下,使用管道来持久化存储。

Beike.py

#!/usr/bin/python3
 # -*- coding: utf-8 -*-
 # @Time    : 2022/3/31 18:01
 # @Author  : camellia
 # @Email   : 805795955@qq.com
 # @File    : beike.py
 # @Software: PyCharm
 
 import time
 
 import scrapy
 import asyncio
 import requests
 import random
 from scrpayProject.items import ScrpayprojectItem
 
 # 解析贝壳二手房页面的房源图片及标题
 class BeikeSpider(scrapy.Spider):
     name = 'beike'
     # allowed_domains = ['www.beike.com']
     start_urls = ['https://dl.ke.com/ershoufang/']
 
     def parse(self, response):
         divList = response.xpath('/html/body/div[1]/div[4]/div[1]/div[4]/ul/li[1]')
         title = []
         for div in divList:
             temp = {}
             title = div.xpath('//ul[@class="sellListContent"]//img[@class="lj-lazy"]/@title').extract()
             for it in title:
                 item = ScrpayprojectItem()
                 item['author'] = it # author属性必须在items.py中声明

                # item['author'] = author
                yield item  # 将item提交给管道

接下来我们需要用到两个框架中的文件items.py以及pipelines.py,那么这两个文件在哪里呢?如下图所示:

q22.png

Items.py

import scrapy

class ScrpayprojectItem(scrapy.Item):
    # define the fields for your item here like:
    author = scrapy.Field()
    pass

 

pipelines.py

class ScrpayprojectPipeline:
    fp = None
    # 重写父类的一个方法,该方法只在开始爬虫的时候调用一次
    def open_spider(self,spider):
        print('开始爬虫……')
        self.fp = open('./data.txt','w',encoding='utf-8')

   # 该方法每接到一个item就会调用一次
     def process_item(self, item, spider):
         if item is not None:
             author = item['author']
             self.fp.write(author + ':' + author + '\n')
             return item
 
     # 重写父类的一个方法,该方法只在结束爬虫的时候调用一次
     def close_spider(self,spider):
         print('结束爬虫……')
         self.fp.close()

到这里为止,代码部分就结束了,对,你没看错,就是这么简单。

然后,我们需要在settings.py中将部分代码解开注释:

ITEM_PIPELINES = {
   'scrpayProject.pipelines.ScrpayprojectPipeline': 300,
}

这里的300代表代码优先级,数字越小,优先级越大。

 

到这里,基于管道的持久化存储就已经完成,我这里测试一下,打开终端,执行命令:

scrapy crawl beike

 

输出:

开始爬虫……
结束爬虫……

 

打开项目目录,我们可以发现多了一个data.txt。打开文件,发现其中内容,即可证明我们的持久化存储成功。

q33.png

 

那么现在就有一个小问题,如果我想将爬取到的数据,一份存储到本地的文件中,一份存储到数据库之中,该如何利用管道来实现这个功能呢?

解决方式很直接,就是在pipelines.py中再定义一个管道类

代码如下所示:

import pymysql

class mysqlPipeline:
    conn = None
    cursor = None
    # 重写父类的一个方法,该方法只在开始爬虫的时候调用一次
    def open_spider(self,spider):
        print('开始写入数据库……')
        self.conn = pymysql.Connect(host="39.107.142.167",port=3306,user='root',password='root@mule123',db='test')

    # 该方法每接到一个item就会调用一次
    def process_item(self, item, spider):
        self.cursor = self.conn.cursor()

        try:
            # 执行 插入语句
            self.cursor.execute('insert into test values("%s")'%(item["author"]))
            # 数据库提交
            self.conn.commit()
        except Exception as e:
            print(e)
            # 数据库回滚
            self.conn.rollback()

        return item

    # 重写父类的一个方法,该方法只在结束爬虫的时候调用一次
    def close_spider(self,spider):
        print('结束写入数据库……')
        self.conn.close()
        self.cursor.close()

 

然后,我们需要在settings.py中配置一下新增的管道类:

ITEM_PIPELINES = {
   'scrpayProject.pipelines.ScrpayprojectPipeline'300,
   'scrpayProject.pipelines.mysqlPipeline'301,
}

这里配置的数据库管道类优先级要比上边的写入文件管道类低。那么这里就有一个小问题,

爬虫文件提交的item类型的对象最终会提交给哪一个管道类呢?

答案是,交给优先级较高的那个管道类。

那么,问题又来了,提交给优先级较高的管道类之后,如何在传递给优先级低的管道类呢?

这部分,python早已经为我们想好了。

    # 该方法每接到一个item就会调用一次
    def process_item(self, item, spider):
        if item is not None:
            author = item['author']
            self.fp.write(author + ':' + author + '\n')
            return item

注意代码中的return item,这句话的意思就是将item继续传递给优先级较低的管道类。

所以,我们在编写管道类的方法的时候,随手在代码结束的位置写一下return item

不然,出问题,排查起来也很麻烦。

 

至此,代码完成了,接下来我们运行一下代码,打开终端,输入:

scrapy crawl beike

输出:

开始爬虫……
开始写入数据库……
结束写入数据库……
结束爬虫……

 

打开数据库,发现test表中已写入数据:

q44.png

我这里是从贝壳爬取的数据。

 

最后总结一下:

管道文件中的一个管道类对应的是将数据存储到一种平台。

爬虫文件提交的item只会给管道文件中第一个被执行的管道类接受

Process_item中的return item 标识将item传递给下一个即将被执行的管道类

 

以上大概就是 爬虫基于管道的持久化操作,慢慢理解,其实不难。

 

有好的建议,请在下方输入你的评论。