這些組件最重要的思路就是攔截,即過(guò)濾
item管道:作用一:入庫(kù)
校驗(yàn):一是可以在管道,但主要是在item定義字段校驗(yàn)
管道是什么
Item管道(Item Pipeline):
- 主要負(fù)責(zé)處理有蜘蛛從網(wǎng)頁(yè)中抽取的Item,主要任務(wù)是清洗、驗(yàn)證和存儲(chǔ)數(shù)據(jù)。
- 當(dāng)頁(yè)面被蜘蛛解析后,將被發(fā)送到Item管道,并經(jīng)過(guò)幾個(gè)特定的次序處理數(shù)據(jù)。
- 每個(gè)Item管道的組件都是有一個(gè)簡(jiǎn)單的方法組成的Python類(lèi)。
- 它們獲取了Item并執(zhí)行它們的方法,同時(shí)還需要確定是否需要在Item管道中繼續(xù)執(zhí)行下一步或是直接丟棄掉不處理。
類(lèi)(Class): 用來(lái)描述具有相同的屬性和方法的對(duì)象的集合。它定義了該集合中每個(gè)對(duì)象所共有的屬性和方法。對(duì)象是類(lèi)的實(shí)例。
數(shù)據(jù)去重在管道里面做是下策。在管道里面去重只是預(yù)防。好的去重是在請(qǐng)求url和解析url時(shí)候就去重,清洗html建議在spider里面做。
每一個(gè)管道都是一個(gè)類(lèi),每個(gè)類(lèi)里面都有這幾種方法。
一個(gè)管道一個(gè)管道,一層濾網(wǎng)一層濾網(wǎng)
最后一個(gè)方法少修改初始化。
必須return item 不然下面的管道就接不上了。給價(jià)格增加增值稅。
item去重,見(jiàn)到粗暴,不推薦。要么拋異常,要么返回item,不要?jiǎng)e的。
set集合是不重復(fù)的。
打開(kāi)一個(gè)文件名。
序列號(hào),寫(xiě)進(jìn)去一系列字符串
加入了mongodb的兩個(gè)參數(shù),并且用init初始化。mongouri和數(shù)據(jù)庫(kù)名兩個(gè)參數(shù)。
向mongodb里面插入數(shù)據(jù)。
Item管道主要函數(shù):
1. process_item(self, item, spider) —— 必須實(shí)現(xiàn)(也是用的最多的方法);
每個(gè) Item Pipeline 組件都需要調(diào)用該方法,這個(gè)方法必須返回一個(gè) Item (或任何繼承類(lèi))對(duì)象, 或是拋出 DropItem 異常,被丟棄的 item 將不會(huì)被之后的 pipeline 組件所處理
需要傳入的參數(shù)為:
- item (Item 對(duì)象) : 被爬取的 item
- spider (Spider 對(duì)象) : 爬取該 item 的 spider
該方法會(huì)被每一個(gè) item pipeline 組件所調(diào)用,process_item 必須返回以下其中的任意一個(gè)對(duì)象:
- 一個(gè) dict
- 一個(gè) Item 對(duì)象或者它的子類(lèi)對(duì)象
- 一個(gè) Twisted Deferred 對(duì)象
- 一個(gè) DropItem exception;如果返回此異常,則該 item 將不會(huì)被后續(xù)的 item pipeline 所繼續(xù)訪問(wèn)
注意:該方法是Item Pipeline必須實(shí)現(xiàn)的方法,其它三個(gè)方法(open_spider/close_spider/from_crawler)是可選的方法
舉例說(shuō)明1
以下假設(shè)的管道,它調(diào)整 price那些不包括增值稅(price_excludes_vat屬性)的項(xiàng)目的價(jià)格,并刪除那些不包含價(jià)格的項(xiàng)目
from scrapy.exceptions import DropItem
class PricePipeline(object):
vat_factor = 1.15
def process_item(self, item, spider):
if item['price']: #是否有價(jià)格
if item['price_excludes_vat']: #如果價(jià)格不包括增值稅,則把價(jià)格乘上一個(gè)增值稅系數(shù)
item['price'] = item['price'] * self.vat_factor
return item
else: #如果沒(méi)有價(jià)格,則拋棄這個(gè)item
raise DropItem("Missing price in %s" % item)
舉例說(shuō)明2
此例主要是用于查找重復(fù)Item并刪除已處理的Item的過(guò)濾器。假設(shè)我們的Item具有唯一的ID,但是我們的Spider會(huì)返回具有相同id的多個(gè)Item:
from scrapy.exceptions import DropItem
class DuplicatesPipeline(object):
def init(self):
self.ids_seen = set() #初始化中,創(chuàng)建一個(gè)空集合
def process_item(self, item, spider):
查看id是否在ids_seen中,如果在,就拋棄該Item,如果不在就添加到ids_seen中,下一次其它Item有相同的id就拋棄那個(gè)Item
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item #記住一定要返回Item
2.open_spider(self, spider) —— 非必需,為爬蟲(chóng)啟動(dòng)的時(shí)候調(diào)用;
當(dāng) spider 被開(kāi)啟時(shí),這個(gè)方法被調(diào)用??梢詫?shí)現(xiàn)在爬蟲(chóng)開(kāi)啟時(shí)需要進(jìn)行的操作,比如說(shuō)打開(kāi)一個(gè)待寫(xiě)入的文件,或者連接數(shù)據(jù)庫(kù)等
需要傳入的參數(shù):
- spider (Spider 對(duì)象) : 被開(kāi)啟的 spider
3. close_spider(self, spider) —— 非必需, 為爬蟲(chóng)關(guān)閉的時(shí)候調(diào)用;
當(dāng) spider 被關(guān)閉時(shí),這個(gè)方法被調(diào)用??梢詫?shí)現(xiàn)在爬蟲(chóng)關(guān)閉時(shí)需要進(jìn)行的操作,比如說(shuō)關(guān)閉已經(jīng)寫(xiě)好的文件,或者關(guān)閉與數(shù)據(jù)庫(kù)的連接
需要傳入的參數(shù):
- spider (Spider 對(duì)象) : 被關(guān)閉的 spider
舉例說(shuō)明:
將項(xiàng)目寫(xiě)入JSON文件
以下管道將所有抓取的Item(來(lái)自所有蜘蛛)存儲(chǔ)到單個(gè)items.json文件中,每行包含一個(gè)項(xiàng)目,以JSON格式序列化:
import json
class JsonWriterPipeline(object):
def open_spider(self, spider):
在爬蟲(chóng)開(kāi)始時(shí)打開(kāi)文件
self.file = open('items.json', 'w')
def close_spider(self, spider):
在爬蟲(chóng)結(jié)束時(shí)關(guān)閉文件
self.file.close()
def process_item(self, item, spider):
把爬取到的item轉(zhuǎn)換為json格式,保存進(jìn)文件
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item #注意要返回item
4. from_crawler(cls, crawler) —— 非必需,也是在啟動(dòng)的時(shí)候調(diào)用,比 open_spider早。
該類(lèi)方法用來(lái)從 Crawler 中初始化得到一個(gè) pipeline 實(shí)例;它必須返回一個(gè)新的 pipeline 實(shí)例;Crawler 對(duì)象提供了訪問(wèn)所有 Scrapy 核心組件的接口,包括 settings 和 signals
需要傳入的參數(shù):
- crawler (Crawler 對(duì)象) : 使用該管道的crawler
舉例說(shuō)明:
此例主要使用pymongo將項(xiàng)目寫(xiě)入MongoDB。MongoDB地址和數(shù)據(jù)庫(kù)名稱(chēng)在Scrapy設(shè)置中指定; MongoDB集合以item類(lèi)命名
from_crawler()方法是創(chuàng)建通往Crawler的pipeline,返回一個(gè)新的pipeline實(shí)例
這個(gè)例子的要點(diǎn)是顯示如何使用from_crawler()方法和如何正確清理資源
通過(guò)類(lèi)方法 from_crawler() 在內(nèi)部初始化得到了一個(gè) pipeline 實(shí)例,初始化的過(guò)程中,使用了 mongo_uri 以及 mongo_db 作為構(gòu)造參數(shù)
import pymongo
class MongoPipeline(object):
collection_name = 'scrapy_items'
def init(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db[self.collection_name].insert_one(dict(item))
return item