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定義模板。
如圖所示,即是模板,要點有二。
- Item類繼承scrapy.Item
- 欄位 = 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功能有:
- 清理HTML資料
- 驗證資料(檢查item包含某些欄位)
- 查重(並丟棄)
- 將爬取結果儲存到資料庫
在實際開發中,4的場景比較多。
定義Pipeline
Pipeline定義在pipeline.py中,這裡依舊先看看Pipeline給定的模板。
如圖,只實現了process_item()方法,來處理傳遞過來的Item。但是在實際開發中,我們通常要實現三個方法:
- __init__:用來構造物件屬性,例如資料庫連線等
- from_crawler:類方法,用來初始化變數
- 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)
這裡要重點強調一下上面程式碼中的幾個點。
- process_item()中為什麼使用isinstance來判斷item的型別?
這個是為了解決多種Item經過同一個Pipiline時,需要呼叫不同的方法來進行資料庫操作的場景。如下圖所示:
不同的Item具有不同的結構,意味著需要不同的sql來插入到資料庫中,所以會先判斷Item型別,再呼叫對應方法處理。
- 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,我基本上沒有用過,但是後面還是當做擴充套件來寫一下。期待下一次相遇。