1. 程式人生 > 其它 >Scrapy入門到放棄05:Item和Pipeline,資料和儲存

Scrapy入門到放棄05:Item和Pipeline,資料和儲存

前言

“又回到最初的起點,呆呆地站在鏡子前”。

本來這篇是打算寫Spider中介軟體的,但是因為這一塊涉及到Item,所以這篇文章先將Item講完,順便再講講Pipeline,然後再講Spider中介軟體。

Item和Pipeline

依舊是先上架構圖。

從架構圖中可以看出,當下載器從網站獲取了網頁響應內容,通過引擎又返回到了Spider程式中。我們在程式中將響應內容通過css或者xpath規則進行解析,然後構造成Item物件。

而Item和響應內容在傳遞到引擎的過程中,會被Spider中介軟體進行處理。最後Pipeline會將引擎傳遞過來的Item持久化儲存。

總結:Item是資料物件,Pipeline是資料管道。

Item

Item說白了就是一個類,裡面包含資料欄位。目的是為了讓你把從網頁解析出來的目標資料進行結構化。需要注意的是,我們通常要先確定Item的結構,然後再在程式中構造、在pipeline中處理。

這裡依舊還是以斗羅大陸為例。

Item類定義

Item在items.py中定義。我們先看看此py檔案中的Item定義模板。

如圖所示,即是模板,要點有二。

  1. Item類繼承scrapy.Item
  2. 欄位 = scrapy.Field()

這裡根據我們在斗羅大陸頁面需要採集的資料欄位,進行Item定義。

class DouLuoDaLuItem(scrapy.Item):
    name = scrapy.Field()
    alias = scrapy.Field()
    area = scrapy.Field()
    parts = scrapy.Field()
    year = scrapy.Field()
    update = scrapy.Field()
    describe = scrapy.Field()

Item資料構造

當我們將Item類定義之後,就要在spider程式中進行構造,即填充資料。

# 匯入Item類,ScrapyDemo是包名
from ScrapyDemo.items import DouLuoDaLuItem
# 構造Item物件
item = DouLuoDaLuItem
item['name'] = name
item['alias'] = alias
item['area'] = area
item['parts'] = parts
item['year'] = year
item['update'] = update
item['describe'] = describe

程式碼如上,一個Item資料物件就被構造完成。

發射Item到Pipeline

在Item物件構造完成之後,還需要一行程式碼就能將Item傳遞到Pipeline中。

yield item

至此,Pipeline,我來了。

Pipeline

Pipeline直譯就是管道,負責處理Item資料,從而實現持久化。說白了就是將資料放到各種形式的檔案、資料庫中。

功能

官方給出的Pipeline功能有:

  1. 清理HTML資料
  2. 驗證資料(檢查item包含某些欄位)
  3. 查重(並丟棄)
  4. 將爬取結果儲存到資料庫

在實際開發中,4的場景比較多。

定義Pipeline

Pipeline定義在pipeline.py中,這裡依舊先看看Pipeline給定的模板。

如圖,只實現了process_item()方法,來處理傳遞過來的Item。但是在實際開發中,我們通常要實現三個方法:

  1. __init__:用來構造物件屬性,例如資料庫連線等
  2. from_crawler:類方法,用來初始化變數
  3. process_item:核心邏輯程式碼,處理Item

這裡,我們就自定義一個Pipeline,將Item資料放入資料庫。

配置Pipeline

和middleware一樣在settings.py中進行配置,這裡對應的是ITEM_PIPELINE引數。

ITEM_PIPELINES = {
    'ScrapyDemo.pipelines.CustomDoLuoDaLuPipeline': 300
}

Key依舊對應的是類全路徑,Value為優先順序,數字越小,優先順序越高。Item會根據優先順序依此通過每個Pipeline,這樣可以在每個Pipeline中對Item進行處理。

為了直觀,後續我將Pipeline在程式碼中進行區域性配置。

pipeline連線資料庫

1. 配置資料庫屬性

我們首先在setttings.py中將資料庫的IP、賬號、密碼、資料庫名稱配置,這樣在pipeline中就可直接讀取,並建立連線。

MYSQL_HOST = '175.27.xx.xx'
MYSQL_DBNAME = 'scrapy'
MYSQL_USER = 'root'
MYSQL_PASSWORD = 'root'

2. 定義pipeline

主要使用pymysql驅動連線資料庫、twisted的adbapi來非同步操作資料庫,這裡非同步劃重點,基本上非同步就是效率、快的代名詞。

import pymysql
from twisted.enterprise import adbapi
from ScrapyDemo.items import DouLuoDaLuItem


class CustomDoLuoDaLuPipeline(object):

    def __init__(self, dbpool):
        self.dbpool = dbpool

    @classmethod
    def from_crawler(cls, crawler):
        # 讀取settings中的配置
        params = dict(
            host=crawler.settings['MYSQL_HOST'],
            db=crawler.settings['MYSQL_DBNAME'],
            user=crawler.settings['MYSQL_USER'],
            passwd=crawler.settings['MYSQL_PASSWORD'],
            charset='utf8',
            cursorclass=pymysql.cursors.DictCursor,
            use_unicode=False
        )
        # 建立連線池,pymysql為使用的連線模組
        dbpool = adbapi.ConnectionPool('pymysql', **params)
        return cls(dbpool)

    def process_item(self, item, spider):
        if isinstance(item, DouLuoDaLuItem):
            query = self.dbpool.runInteraction(self.do_insert, item)
            query.addErrback(self.handle_error, item, spider)
        return item

    # 執行資料庫操作的回撥函式
    def do_insert(self, cursor, item):
        sql = 'insert into DLDLItem(name, alias, area, parts, year, `update`, `describe`) values (%s, %s, %s, %s, %s, %s, %s)'
        params = (item['name'], item['alias'], item['area'], item['parts'], item['year'], item['update'], item['describe'])
        cursor.execute(sql, params)

    # 當資料庫操作失敗的回撥函式
    def handle_error(self, failue, item, spider):
        print(failue)

這裡要重點強調一下上面程式碼中的幾個點。

  1. process_item()中為什麼使用isinstance來判斷item的型別?

這個是為了解決多種Item經過同一個Pipiline時,需要呼叫不同的方法來進行資料庫操作的場景。如下圖所示:

不同的Item具有不同的結構,意味著需要不同的sql來插入到資料庫中,所以會先判斷Item型別,再呼叫對應方法處理。

  1. sql中update、describe欄位為什麼要加反引號?

update、describe和select一樣,都是MySQL的關鍵字,所以如果想要在欄位中使用這些單詞,在執行sql和建表語句彙總都要加上反引號,否則就會報錯。

3. 生成Item放入pipeline

即將迎面而來的依舊是熟悉的程式碼,Item結構在上面的items.py中已經定義。pipeline也將在程式碼內區域性配置,這個不清楚的可以看第二篇文章。

import scrapy
from ScrapyDemo.items import DouLuoDaLuItem

class DouLuoDaLuSpider(scrapy.Spider):
    name = 'DouLuoDaLu'
    allowed_domains = ['v.qq.com']
    start_urls = ['https://v.qq.com/detail/m/m441e3rjq9kwpsc.html']

    custom_settings = {
        'ITEM_PIPELINES': {
            'ScrapyDemo.pipelines.CustomDoLuoDaLuPipeline': 300
        }
    }

    def parse(self, response):
        name = response.css('h1.video_title_cn a::text').extract()[0]
        common = response.css('span.type_txt::text').extract()
        alias, area, parts, year, update = common[0], common[1], common[2], common[3], common[4]
        describe = response.css('span._desc_txt_lineHight::text').extract()[0]
        item = DouLuoDaLuItem()
        item['name'] = name
        item['alias'] = alias
        item['area'] = area
        item['parts'] = parts
        item['year'] = year
        item['update'] = update
        item['describe'] = describe
        print(item)
        yield item

4.程式測試

啟動程式,可以看到控制檯列印了已經啟用的pipeline列表,同時也可以看到item的內容。程式執行結束後,我們去資料庫檢視資料是否已經放到資料庫。

如圖,在資料庫的DLDLItem表中已經可以查到資料。

結語

Item和Pipeline讓資料結構儲存流程化,我們可以定義並配置多個Pipeline,當yield item之後,資料就會根據儲存在檔案裡、資料庫裡

與之相關的還有一個ItemLoaders,我基本上沒有用過,但是後面還是當做擴充套件來寫一下。期待下一次相遇。