scrapy框架的持久化存储

一 . 基于终端指令的持久化存储 

  保证爬虫文件的parse方法中有可迭代类型对象(一般为列表or字典)的返回,该返回值能够经过终端指令的形式写入指定格式的文件中进行持久化操做。html

  注意 : python

    1).只能够将parse方法的返回值表示的数据进行持久化储存.mysql

执行输出指定格式进行存储:将爬取到的数据写入不一样格式的文件中进行存储
    scrapy crawl 爬虫名称 -o xxx.json
    scrapy crawl 爬虫名称 -o xxx.xml
    scrapy crawl 爬虫名称 -o xxx.csv

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

  scrapy框架已经为咱们专门集成了高效 , 便捷的持久化操做功能,咱们直接用便可.redis

  在使用scrapy的持久化操做功能以前,咱们要知道这两个文件是什么 ? sql

 items.py数据结构模板文件。定义数据属性。
    pipelines.py管道文件。接收数据items),进行持久化操做。

持久化流程:
    1.爬虫文件爬取到数据后,须要将数据封装到items对象中。
    2.使用yield关键字将items对象提交给pipelines管道进行持久化操做。
    3.在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,而后编写持久化存储的代码将item对象中存储的数据进行持久化存储
    4.settings.py配置文件中开启管道

  注意 : process_item函数的返回值做用 : 能够将item给下一个管道类数据库

三 . 实例 

1 . 进行本地的持久化存储json

  ⑴ . 爬虫文件 first_hh.py 数据结构

# -*- coding: utf-8 -*-
import scrapy
from frist_boll.items import FristBollItem


class FirstHhSpider(scrapy.Spider):
    # 爬虫文件的名称
    name = 'first_hh'
    # 容许访问的域名 , 可是有时候图片的地址与访问的网站不是同一域名,
    # 为了能够正常访问,就注释了就好
    # allowed_domains = ['www.xxx.com']
    # 起始的url列表,能够放多个url
    start_urls = ['https://www.qiushibaike.com/text/']

    def parse(self, response):
        div_list = response.xpath("//div[@id='content-left']/div")
        # all_data = []

        for div in div_list:
            # 若是xpath返回的列表中只有一个类别元素-就用extract_first
            # 列表中有多个列表元素-用extract()
            author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
            content = div.xpath('./a[1]/div/span//text()').extract()

            # 储存的时候要是字符串,不能是列表
            content = "".join(content)

            # 实例化item对象
            item = FristBollItem()
            # 将解析到的数据所有封装到item中,传给管道文件pipelines.py
            item['author'] = author
            item['content'] = content
            # 将item提交给管道文件(pipelines.py)
            yield item

  ⑵ . items.py 文件 python爬虫

import scrapy


class FristBollItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    author = scrapy.Field()     #存储做者
    content = scrapy.Field()    # 存储段子内容

  ⑶ . 管道文件 : pipelines.py框架

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html


class FristBollPipeline(object):
    f1 = None  #定义一个文件描述符属性

    # 打开文件,由于函数process_item 要进行循环,全部只要打开一次文件就能够,
    # 下面都是重写父类方法,全部spider参数必须有
    def open_spider(self, spider):
        print("start")
        self.f1 = open('./趣事百科.csv', 'w', encoding='utf-8')

   #由于该方法会被执行调用屡次,因此文件的开启和关闭操做写在了另外两个只会各自执行一次的方法中。
    def process_item(self, item, spider):
         #将爬虫程序提交的item进行持久化存储
        self.f1.write(item['author'] + '\n' + item['content'])
        print()
        return item

    # 结束爬虫 , 关闭文件
    def close_spider(self, spider):
        print("end")
        self.f1.close()

  ⑷ . 配置文件 : settings.py

#开启管道
ITEM_PIPELINES = {
    'secondblood.pipelines.SecondbloodPipeline': 300, #300表示为优先级,值越小优先级越高
}

 

2 . 基于mysql的管道存储 

  爬取boss直聘的爬虫职位

  先将 mysql打开,并建立一个文件夹和建立个文件,并且你要存储的字段也要建立好,scrapy框架只负责添加数据到mysql,不负责建立.

  建立项目 :  scrapy startproject  boss 

  进入目录 :  cd boss    

  建立应用 :  scrapy genspider boss_hh www.xxx.com

  ⑴ . 爬虫文件 : boss_hh.py

# -*- coding: utf-8 -*-
import scrapy
from boss.items import BossItem


class BossHhSpider(scrapy.Spider):
    name = 'boss_hh'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.zhipin.com/job_detail/?query=%E7%88%AC%E8%99%AB&scity=101010100&industry=&position=']
    # 由于有分页的效果,并且第一页与后面几页的url不同,全部要进行判断 page=%d  进行页码
    url = 'https://www.zhipin.com/c101010100/?query=python爬虫&page=%d&ka=page-2'
    # 默认第一页,起始页面
    page = 1

    def parse(self, response):
        li_list = response.xpath('//div[@class="job-list"]/ul/li')
        for li in li_list:
            job_name = li.xpath('.//div[@class="info-primary"]/h3/a/div/text()').extract_first()
            salary = li.xpath('.//div[@class="info-primary"]/h3/a/span/text()').extract_first()
            company = li.xpath('.//div[@class="company-text"]/h3/a/text()').extract_first()
            # 实例化
            item = BossItem()
            # 解析到的数据加到item对象中
            item['job_name'] = job_name
            item['salary'] = salary
            item['company'] = company
            # 提交到管道进行保存
            yield item
        if self.page <= 3:
            print("执行 if 判断 ")
            # 从第二页开始,封装集成了一个新的页码的url
            self.page += 1
            # 占位
            new_url = format(self.url % self.page)
# 手动请求的发送,callback 表示指定的解析方式 , 回调
        #递归爬取数据:callback参数的值为回调函数(将url请求后,获得的相应数据继续进行parse解析),递归调用parse函数
yield scrapy.Request(url=new_url,callback=self.parse)

  ⑵ . items文件 : 

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

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class BossItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()

    job_name = scrapy.Field()
    salary = scrapy.Field()
    company = scrapy.Field()

  ⑶ . 管道文件 : pipelines.py

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import pymysql
from redis import Redis


class mysqlPipeline(object):
    conn = None
    cursor = None
     
    # 打开mysql
    def open_spider(self, spider):
        self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='scrapy', charset='utf8')


    def process_item(self, item, spider):
        self.cursor = self.conn.cursor()
        try:
            # 将数据写入mysql
            self.cursor.execute('insert into boss(job_name,salary,company) values("%s","%s","%s")' % (item['job_name'], item['salary'], item['company']))

            self.conn.commit()
        except Exception as e:
            self.conn.rollback()
        return item       # 注意
    # 关闭mysql
    def close_spider(self, spider):
        self.conn.close()
        self.cursor.close()

  ⑷ . settings文件 : 

ITEM_PIPELINES = {
   # 'boss.pipelines.BossPipeline': 300,
   'boss.pipelines.mysqlPipeline': 301,
}
# 前一个属于子代文件,若是只想执行mysql,就能够将前面的注释

  注意 : 在管道文件 : pipelines.py 中 , 函数 mysqlPipeline,最后返回了一个 return item . 这个是给下面的函数进行的,由于能够在一个管道文件中进行本地硬盘存储和mysql存储,能够经过 settings里面的开启管道的优先级设置那个先进行存储,return item 就会从先执行的传给后执行的. 必定要返回,不然后面的不会执行

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import pymysql
from redis import Redis


class BossPipeline(object):
    f1 = None

    def open_spider(self, spider):
        print("start ----------->")
        self.f1 = open('boss.txt', 'w', encoding='utf-8')

    def close_spider(self, spider):
        print("end ------------->")
        self.f1.close()

    def process_item(self, item, spider):
        self.f1.write(item['job_name'] + ":" + item['salary'] + ":" + item['company'] + '\n')
        return item  # 返回给mysql进行


class mysqlPipeline(object):
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='', db='scrapy', charset='utf8')


    def process_item(self, item, spider):
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute('insert into boss(job_name,salary,company) values("%s","%s","%s")' % (item['job_name'], item['salary'], item['company']))
            self.conn.commit()
        except Exception as e:
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        self.conn.close()
        self.cursor.close()
管道文件 : 本地和mysql都存储

 

3 . 基于redis的管道存储  

   只须要将 管道文件 修改就能够了 !!!!!

from redis import Redis

class redisPileLine(object):
    conn = None

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

    def process_item(self, item, spider):
        # print(item)
        dic = {
            'name': item['job_name'],
            'salary': item['salary'],
            'company': item['company']
        }
        self.conn.lpush('boss', dic)

   settings文件 : 

ITEM_PIPELINES = {
    'qiubaiPro.pipelines.QiubaiproPipelineByRedis': 300,
}

 

四 . 总结  

   若是最终须要将爬取到的数据值一份存储到磁盘文件,一份存储到数据库中,则应该如何操做scrapy?

   答 :  管道文件中的代码 

#该类为管道类,该类中的process_item方法是用来实现持久化存储操做的。
class DoublekillPipeline(object):

    def process_item(self, item, spider):
        #持久化操做代码 (方式1:写入磁盘文件)
        return item

#若是想实现另外一种形式的持久化操做,则能够再定制一个管道类:
class DoublekillPipeline_db(object):

    def process_item(self, item, spider):
        #持久化操做代码 (方式1:写入数据库)
        return item

  settings.py文件的开启管道的代码 :

#下列结构为字典,字典中的键值表示的是即将被启用执行的管道文件和其执行的优先级。
ITEM_PIPELINES = {
   'doublekill.pipelines.DoublekillPipeline': 300,
    'doublekill.pipelines.DoublekillPipeline_db': 200,
}

#上述代码中,字典中的两组键值分别表示会执行管道文件中对应的两个管道类中的process_item方法,实现两种不一样形式的持久化操做。