Scarpy框架持久化存儲

1、介紹

  持久化存儲操做分爲兩類:磁盤文件數據庫
  而磁盤文件存儲方式又分爲:基於終端指令基於管道html

2、基於終端指令的持久化存儲

  Scrapy是經過 scrapy 命令行工具進行控制的。 這裏咱們稱之爲 「Scrapy tool」 以用來和子命令進行區分。 對於子命令,咱們稱爲 「command」 或者 「Scrapy commands」。python

一、保證parse方法返回一個可迭代類型的對象(存儲解析到的頁面內容)

  改寫parse方法,讓方法返回值爲迭代器。mysql

class QiubaiproSpider(scrapy.Spider):
    name = 'qiubaipro'
    # allowed_domains = ['www.qiushibaike.com/text']  # 圖片等信息可能不屬於指定域名之下
    start_urls = ['https://www.qiushibaike.com/text/']  # 注意修改默認協議頭
    def parse(self, response):
        # 建議使用xpath來執行指定內容的解析(Scrapy已經集成好了xpath解析的接口)
        # 段子的內容和做者
        div_list = response.xpath('//div[@id="content-left"]/div')

        # 存儲解析到的頁面數據
        data_list = []

        for div in div_list:
            author = div.xpath('./div/a[2]/h2/text()').extract_first()  # './'表示解析當前局部div; a[2]表示第二個a標籤
            content = div.xpath('.//div[@class="content"]/span/text()').extract_first()  # './/'表示當前局部全部元素;@class匹配類屬性

            dict = {
                'author': author,
                'content': content
            }
            data_list.append(dict)

        # parse方法的返回值必須是迭代器或空
        return data_list

二、使用終端指令完成數據存儲到指定磁盤文件中

  執行輸出指定格式進行存儲:將爬取到的數據寫入不一樣格式的文件中進行存儲。
  經常使用文件格式:json、xml、csv、txt等。web

# scrapy crawl 爬蟲文件名稱 -o 磁盤文件.後綴
$ scrapy crawl qiubaipro -o qiubai.csv --nolog
$ ls
firstBlood  qiubai.csv  readme.md   scrapy.cfg

三、csv持久化存儲效果

csv表格

3、基於管道的持久化存儲

  scrapy框架中已經爲咱們專門集成好了高效、便捷的持久化操做功能,咱們直接使用便可。redis

一、持久化須要的項目文件

  想使用scrapy的持久化操做功能,咱們首先來認識以下兩個文件:sql

items.py:數據結構模板文件,定義數據屬性。存儲解析到的頁面數據
pipelines.py:管道文件。接收數據(items),進行持久化存儲的相關操做。

二、持久化實現流程

  1. 爬蟲文件爬取到數據後,須要將數據封裝到items對象中。
  2. 使用yield關鍵字將items對象提交給pipelines管道進行持久化操做。
  3. 在管道文件中的process_item方法中接收爬蟲文件提交過來的item對象,而後編寫持久化存儲的代碼將item對象中存儲的數據進行持久化存儲
  4. settings.py配置文件中開啓管道

三、代碼示例

(1)將解析到的數據值(author和content)存儲到items對象(存儲前先聲明item屬性)

  在爬蟲文件qiubaipro.py中引入了項目的items.py文件中的FirstbloodItem類。數據庫

# -*- coding: utf-8 -*-
import scrapy
from firstBlood.firstBlood.items import FirstbloodItem

class QiubaiproSpider(scrapy.Spider):
    name = 'qiubaipro'
    # allowed_domains = ['www.qiushibaike.com/text']  # 圖片等信息可能不屬於指定域名之下
    start_urls = ['https://www.qiushibaike.com/text/']  # 注意修改默認協議頭

    def parse(self, response):
        div_list = response.xpath('//div[@id="content-left"]/div')
        # 存儲解析到的頁面數據
        data_list = []
        for div in div_list:
            author = div.xpath('./div/a[2]/h2/text()').extract_first()
            content = div.xpath('.//div[@class="content"]/span/text()').extract_first() 
            
            # 一、將解析到的數據值(author和content)存儲到items對象(存儲前先聲明item屬性)
            item = FirstbloodItem()
            item['author'] = author
            item['content'] = content

注意:
  1)解析的數據存儲到items對象前要先聲明item屬性。
  2)items.py文件內容配置以下:json

import scrapy

class FirstbloodItem(scrapy.Item):
    # 必須聽從以下屬性聲明規則
    # name = scrapy.Field()

    # 聲明item屬性
    author = scrapy.Field()  # 存儲解析到的做者
    content = scrapy.Field()  # 存儲解析到的內容信息

(2)yield將item對象提交給管道進行持久化存儲操做

  qiubaipro.py文件中:bash

import scrapy
from firstBlood.items import FirstbloodItem

class QiubaiproSpider(scrapy.Spider):
    def parse(self, response):
        """省略代碼"""
        for div in div_list:
            """省略代碼"""

            # 二、yield將item對象提交給管道進行持久化存儲操做
            yield item

(3)在管道文件(pipelines.py)中編寫代碼完成數據存儲的操做

class FirstbloodPipeline(object):
    fp = None
    def open_spider(self, spider):
        """
        該方法只會在爬蟲開始爬數據的時候被調用一次
        :param spider:
        :return:
        """
        print("開始爬蟲")
        # 在該方法中打開文件
        self.fp = open('./qiubai_pipe.txt', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        """
        接收爬蟲文件中提交的item對象,並對item對象中存儲的頁面數據進行持久化存儲
        每當爬蟲文件向管道提交一次item,porcess_item方法就會執行一次
        :param item: 表示接收到的item對象
        :param spider:
        :return:
        """
        # 取出Item對象中存儲的數據值
        author = item['author']
        content = item['content']
        # 將item對象中存儲的數據進行持久化存儲
        # with open('./qiubai_pipe.txt', 'w', encoding='utf-8') as fp:
        self.fp.write(author + ":" + content+ '\n\n\n')  # 寫入數據
        return item

    def close_spider(self, spider):
        """
        該方法只會在爬蟲結束時被調用一次
        :param spider:
        :return:
        """
        print("爬蟲結束")
        # 關閉文件
        self.fp.close()

注意:
  1)每當爬蟲文件向管道提交一次item,process_item方法就會執行一次,所以最後輸出的爬蟲結果只保存了最後一次打開寫入的數據,前面的數據均被覆蓋。
  2)使用open_spider方法特性:只會在爬蟲開始爬數據的時候被調用一次。可解決屢次打開文件的問題。
  3)使用close_spider方法特性:只會在爬蟲結束時被調用一次。可在爬蟲結束後關閉文件。數據結構

(4)在配置文件(settings.py)中開啓管道操做

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'firstBlood.pipelines.FirstbloodPipeline': 300,      # 優先級
}

(5)執行爬蟲操做

$ scrapy crawl qiubaipro --nolog

4、基於數據庫的持久化存儲

  1)建立爬蟲項目和爬蟲應用:

# 建立項目
$ scrapy startproject qiubaiDB
# 切換到項目目錄,建立爬蟲應用
$ scrapy genspider qiubaiMysql www.qiushibaike.com/text
$ scrapy genspider qiubaiRedis www.qiushibaike.com/text

  2)settings.py配置文件(同pip):

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' # 假裝請求載體身份

# Obey robots.txt rules
ROBOTSTXT_OBEY = False          # 不聽從門戶網站robots協議,避免某些信息爬取不到

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'qiubaiDB.pipelines.QiubaidbPipeline': 300,     # 注意這裏須要取消註釋(不能替換)
}

  3)items.py中聲明屬性:

import scrapy

class QiubaidbItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 聲明屬性
    author = scrapy.Field()
    content = scrapy.Field()

一、基於數據庫編碼流程

  1. 將解析到的頁面數據存儲到items對象
  2. 使用yield關鍵字將items提交給管道文件進行處理
  3. 在管道文件中編寫代碼完成數據存儲的操做
  4. 在配置文件中開啓管道操做

二、基於mysql的管道存儲

(1)建立數據庫

# 登陸數據庫
$ mysql -uroot -p1234
# 建立qiubai數據庫
mysql> create database qiubai;
mysql> use qiubai
Database changed
# 建立qiubai表
mysql> create table qiubai(
    -> author varchar(50),
    -> content varchar(1000)
    -> );
Query OK, 0 rows affected (0.13 sec)

(2)編寫qiubaiMysql.py:

# -*- coding: utf-8 -*-
import scrapy
from qiubaiDB.items import QiubaidbItem

class QiubaimysqlSpider(scrapy.Spider):
    name = 'qiubaiMysql'
    # allowed_domains = ['www.qiushibaike.com/text']  # 圖片等信息可能不屬於指定域名之下
    start_urls = ['https://www.qiushibaike.com/text/']  # 注意修改默認協議頭

    def parse(self, response):
        # 段子的內容和做者
        div_list = response.xpath('//div[@id="content-left"]/div')  # 使用xpath來執行指定內容的解析

        for div in div_list:
            # 經過xpath解析到的指定內容被存儲到了selector對象中
            # 須要經過extract()方法來提取selector對象中存儲的數據值
            author = div.xpath('./div/a[2]/h2/text()').extract_first()   # './'表示解析當前局部div; a[2]表示第二個a標籤
            content = div.xpath('.//div[@class="content"]/span/text()').extract_first()  # './/'表示當前局部全部元素;@class匹配類屬性

            # 建立item對象
            item = QiubaidbItem()
            # 數據值寫入item對象中
            item['author'] = author
            item['content'] = content

            # 提交給管道(循環幾回就提交幾回)
            yield  item

(3)編寫pipelines.py:

import pymysql

class QiubaidbPipeline(object):
    conn = None    # 鏈接對象聲明爲全局屬性
    cursor = None   # 遊標對象
    def open_spider(self, spider):
        print("開始爬蟲")
        # 鏈接數據庫
        self.conn = pymysql.connect(host='127.0.0.1', port=3306,
                        user='root', password='1234', db='qiubai')    # 注意端口是整數不是字符串

    def process_item(self, item, spider):
        """
        編寫向數據庫中存儲數據的相關代碼
        :param item:
        :param spider:
        :return:
        """
        # 一、鏈接數據庫:open_spider()
        # 二、執行sql語句
        sql = 'insert into qiubai values("%s", "%s")' % (item['author'], item['content'])
        self.cursor = self.conn.cursor()  # 建立遊標對象
        try:
            self.cursor.execute(sql)   # 執行sql語句
            # 三、提交事務
            self.conn.commit()
        except Exception as e:
            # 出現錯誤的時候:打印錯誤並回滾
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        print("爬蟲結束")
        self.cursor.close()  # 關閉遊標
        self.conn.close()    # 關閉鏈接對象

(4)執行爬蟲程序

$ scrapy crawl qiubaiMysql --nolog

# 查看數據庫
$ mysql -uroot -p1234
mysql> use qiubai
mysql> select * from qiubai;

三、基於redis數據庫存儲

(1)編寫爬蟲文件:qiubaiRedis.py

import scrapy
from qiubaiDB.items import QiubaidbItem

class QiubairedisSpider(scrapy.Spider):
    name = 'qiubaiRedis'
    allowed_domains = ['www.qiushibaike.com/text']
    start_urls = ['https://www.qiushibaike.com/text/']   # 注意修改默認協議頭

    def parse(self, response):
        # 段子的內容和做者
        div_list = response.xpath('//div[@id="content-left"]/div')  # 使用xpath來執行指定內容的解析

        for div in div_list:
            # 經過xpath解析到的指定內容被存儲到了selector對象中
            # 須要經過extract()方法來提取selector對象中存儲的數據值
            author = div.xpath('./div/a[2]/h2/text()').extract_first()  # './'表示解析當前局部div; a[2]表示第二個a標籤
            content = div.xpath('.//div[@class="content"]/span/text()').extract_first()  # './/'表示當前局部全部元素;@class匹配類屬性

            # 1、建立item對象
            item = QiubaidbItem()
            # 數據值寫入item對象中
            item['author'] = author
            item['content'] = content

            # 2、提交給管道(循環幾回就提交幾回)
            yield item

(2)從新編寫pipelines.py

import redis
import json

class QiubaidbPipeline(object):
    conn = None   # 聲明全局鏈接對象
    def open_spider(self, spider):
        print("開始爬蟲")
        # 鏈接redis數據庫
        self.conn = redis.Redis(host='127.0.0.1', port=6379)

    def process_item(self, item, spider):
        """編寫向redis中存儲數據的相關代碼"""
        dic = {      # dic中封裝item對象中獲取的頁面數據
            'author': item['author'],
            'content': item['content']
        }
        dic_str = json.dumps(dic)   # 轉爲字符串(執行時,要求轉化爲byte\string\number)
        # redis數據庫寫入
        self.conn.lpush('data', dic_str)  # 每一次獲取的值追加到列表當中
        return item

(3)執行爬蟲查看結果

  redis的安裝部署和服務啓動方法詳見:
Redis介紹

$ scrapy crawl qiubaiRedis --nolog

# 啓動redis客戶端
$ pwd
/Users/hqs/redis-5.0.2
$ src/redis-cli 
127.0.0.1:6379> lrange data 0 -1   # 返回列表中指定區間內的元素;START:0 表示列表的第一個元素;END:-1 表示列表的最後一個元素

5、將數據分別存儲到磁盤、redis、mysql中(管道高級操做)

  若是最終須要將爬取到的數據一份存儲到磁盤文件,一份存儲到redis數據庫,一份保存在mysql數據庫中。

一、修改配置管道文件pipelines.py

  須要在管道文件中編寫對應平臺的管道類:

# Mysql版本
import pymysql

class QiubaiMysqlPipeline(object):
    conn = None    # 鏈接對象聲明爲全局屬性
    cursor = None   # 遊標對象
    def open_spider(self, spider):
        print("開始爬蟲")
        # 鏈接數據庫
        self.conn = pymysql.connect(host='127.0.0.1', port=3306,
                        user='root', password='1234', db='qiubai')

    def process_item(self, item, spider):
        """
        編寫向數據庫中存儲數據的相關代碼
        :param item:
        :param spider:
        :return:
        """
        # 一、鏈接數據庫:open_spider()
        # 二、執行sql語句
        sql = 'insert into qiubai values("%s", "%s")' % (item['author'], item['content'])
        self.cursor = self.conn.cursor()  # 建立遊標對象
        try:
            self.cursor.execute(sql)   # 執行sql語句
            # 三、提交事務
            self.conn.commit()
        except Exception as e:
            # 出現錯誤的時候:打印錯誤並回滾
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        print("爬蟲結束")
        self.cursor.close()  # 關閉遊標
        self.conn.close()    # 關閉鏈接對象

# redis版本
import redis
import json

class QiubaidbPipeline(object):
    conn = None   # 聲明全局鏈接對象
    def open_spider(self, spider):
        print("開始爬蟲")
        # 鏈接redis數據庫
        self.conn = redis.Redis(host='127.0.0.1', port=6379)

    def process_item(self, item, spider):
        """編寫向redis中存儲數據的相關代碼"""
        dic = {      # dic中封裝item對象中獲取的頁面數據
            'author': item['author'],
            'content': item['content']
        }
        dic_str = json.dumps(dic)   # 轉爲字符串
        # redis數據庫寫入
        # lpush:從左往右添加元素。在key對應list的頭部添加字符串元素
        self.conn.lpush('data', dic_str)  # 每一次獲取的值追加到列表當中
        return item


# 文件保存
class QiubaiByFilesPipeline(object):
    """實現將數據值存儲到本地磁盤中"""
    fp = None
    def open_spider(self, spider):
        print("開始爬蟲")
        # 在該方法中打開文件
        self.fp = open('./qiubai_pipe.txt', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        # 取出Item對象中存儲的數據值
        author = item['author']
        content = item['content']
        # 持久化存儲
        self.fp.write(author + ":" + content+ '\n\n\n')  # 寫入數據
        return item

    def close_spider(self, spider):
        print("爬蟲結束")
        # 關閉文件
        self.fp.close()

二、settings.py中開啓管道操做

  在配置文件中對自定義的管道類進行生效操做:

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {   # 數值多少無所謂,數值大小表明優先級順序: 500>400>300
    'qiubaiDB.pipelines.QiubaidbPipeline': 300,   # redis
    'qiubaiDB.pipelines.QiubaiMysqlPipeline': 500,  # mysql
    'qiubaiDB.pipelines.QiubaiByFilesPipeline': 400   # 文件
}

6、遞歸爬取解析多頁頁面數據

一、多頁爬取需求分析

  需求:將糗事百科全部頁碼的做者和段子內容數據進行爬取切持久化存儲。
  需求分析:每個頁面對應一個url,則scrapy工程須要對每個頁碼對應的url依次發起請求,而後經過對應的解析方法進行做者和段子內容的解析。

二、實現方案

  1. 將每個頁碼對應的url存放到爬蟲文件的起始url列表(start_urls)中。(不推薦)
  2. 使用Request方法手動發起請求。(推薦

三、項目建立

$ pwd
/Users/hqs/ScrapyProjects
$ scrapy startproject qiubaiByPages
New Scrapy project 'qiubaiByPages', using template directory '/Users/hqs/anaconda3/lib/python3.7/site-packages/scrapy/templates/project', created in:
    /Users/hqs/ScrapyProjects/qiubaiByPages

You can start your first spider with:
    cd qiubaiByPages
    scrapy genspider example example.com
$ cd qiubaiByPages/
$ scrapy genspider qiubai www.qiushibaike.com/text
Created spider 'qiubai' using template 'basic' in module:
  qiubaiByPages.spiders.qiubai

四、單url編碼實現(準備工做)

(1)爬蟲文件:qiubai.py

import scrapy
from qiubaiByPages.items import QiubaibypagesItem

class QiubaiSpider(scrapy.Spider):
    name = 'qiubai'
    # allowed_domains = ['www.qiushibaike.com/text']    # 容許的域名(不少網頁不在域名下)
    start_urls = ['https://www.qiushibaike.com/text/']  # 默認消息頭是http,這裏手動調整

    def parse(self, response):
        div_list = response.xpath('//*[@id="content-left"]/div')
        for div in div_list:
            author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()').extract_first()  # selector對象
            content = div.xpath('.//div[@class="content" ]/span/text()').extract_first()

            # 將解析的數據值存儲到items對象中
            item = QiubaibypagesItem()
            item["author"] = author
            item["content"] = content

            # 將item對象提交給管道文件
            yield item

(2)數據存儲模板:items.py文件

import scrapy

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

(3)數據持久化處理:pipelines.py

class QiubaibypagesPipeline(object):
    fp = None
    def open_spider(self, spider):
        print("開始爬蟲")
        self.fp = open('./qiubai.txt', 'w', encoding="utf-8")

    def process_item(self, item, spider):
        self.fp.write(item['author']+ ":" + item['content'])
        return item

    def close_spider(self, spider):
        self.fp.close()
        print("爬蟲結束")

(4)配置文件:settings.py

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' # 假裝請求載體身份

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {      # 開啓管道
    'qiubaiByPages.pipelines.QiubaibypagesPipeline': 300,
}

五、請求手動發送實現多url數據爬取

  修改爬蟲文件:qiubai.py

import scrapy
from qiubaiByPages.items import QiubaibypagesItem


class QiubaiSpider(scrapy.Spider):
    name = 'qiubai'
    # allowed_domains = ['www.qiushibaike.com/text']    # 容許的域名(不少網頁不在域名下)
    start_urls = ['https://www.qiushibaike.com/text/']  # 默認消息頭是http,這裏手動調整

    # 設計通用的url模板
    url = 'https://www.qiushibaike.com/text/page/%d'   # 分頁的通用格式
    pageNum = 1

    def parse(self, response):
        div_list = response.xpath('//*[@id="content-left"]/div')
        for div in div_list:
            author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()').extract_first()  # selector對象
            content = div.xpath('.//div[@class="content" ]/span/text()').extract_first()

            # 將解析的數據值存儲到items對象中
            item = QiubaibypagesItem()
            item["author"] = author
            item["content"] = content

            # 將item對象提交給管道文件
            yield item

        # 請求的手動發送
        if self.pageNum <= 13:    # 遞歸終止條件(13是最後一頁的頁碼)
            print('爬取到了第%d頁的頁面數據' % self.pageNum)
            self.pageNum += 1   # 從第二個頁碼開始手動請求
            new_url = format(self.url % self.pageNum)   # 'https://www.qiushibaike.com/text/page/2/'
            # scrapy.Request對指定url發請求
            # callback:將請求獲取到的頁面數據進行解析
            yield scrapy.Request(url=new_url, callback=self.parse)

注意:
  1)第一頁仍是使用的起始url列表這個機制來進行請求發送,從第二頁開始採用手動請求發送
  2)執行請求手動發送必須結合 yield 和 Request 函數一塊來使用。
  3)在調用scrapy.Request函數時,有一個參數叫 callback 。這是一個回調函數,會進行遞歸的調用。爲了防止無限循環,須要設置遞歸終止條件。

六、執行驗證

$ scrapy crawl qiubai --nolog
開始爬蟲
爬取到了第1頁的頁面數據
爬取到了第2頁的頁面數據
爬取到了第3頁的頁面數據
爬取到了第4頁的頁面數據
爬取到了第5頁的頁面數據
爬取到了第6頁的頁面數據
爬取到了第7頁的頁面數據
爬取到了第8頁的頁面數據
爬取到了第9頁的頁面數據
爬取到了第10頁的頁面數據
爬取到了第11頁的頁面數據
爬取到了第12頁的頁面數據
爬取到了第13頁的頁面數據
爬蟲結束
相關文章
相關標籤/搜索