Scrapy學習篇:Item Pipeline

當一個item被蜘蛛爬取到之後會被髮送給Item Pipeline,然後多個組件按照順序處理這個item。 每個Item Pipeline組件其實就是一個實現了一個簡單方法的Python類。他們接受一個item並在上面執行邏輯,還能決定這個item到底是否還要繼續往下傳輸,如果不要了就直接丟棄。

使用Item Pipeline的常用場景:

  • 清理HTML數據
  • 驗證被抓取的數據(檢查item是否包含某些字段)
  • 重複性檢查(然後丟棄)
  • 將抓取的數據存儲到數據庫中

編寫自己的Pipeline

定義一個Python類,然後實現方法process_item(self, item, spider)即可,返回一個字典或Item,或者拋出DropItem異常丟棄這個Item。

或者還可以實現下面幾個方法:

  • open_spider(self, spider) 蜘蛛打開的時執行
  • close_spider(self, spider) 蜘蛛關閉時執行
  • from_crawler(cls, crawler) 可訪問核心組件比如配置和信號,並註冊鉤子函數到Scrapy中

Item Pipeline示例

價格驗證

我們通過一個價格驗證例子來看看怎樣使用

from scrapy.exceptions import DropItem

class PricePipeline(object):

vat_factor = 1.15

def process_item(self, item, spider):
if item['price']:
if item['price_excludes_vat']:
item['price'] = item['price'] * self.vat_factor
return item
else:
raise DropItem("Missing price in %s" % item)

將item寫入json文件

下面的這個Pipeline將所有的item寫入到一個單獨的json文件,一行一個item

import json

class JsonWriterPipeline(object):

def __init__(self):
self.file = open('items.jl', 'wb')

def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\\n"
self.file.write(line)
return item

將item存儲到MongoDB中

這個例子使用pymongo來演示怎樣講item保存到MongoDB中。 MongoDB的地址和數據庫名在配置中指定,這個例子主要是向你展示怎樣使用from_crawler()方法,以及如何清理資源。

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(dict(item))
return item

重複過濾器

假設我們的item裡面的id字典是唯一的,但是我們的蜘蛛返回了多個相同id的item

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

def __init__(self):
self.ids_seen = set()

def process_item(self, item, spider):
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item

激活一個Item Pipeline組件

你必須在配置文件中將你需要激活的Pipline組件添加到ITEM_PIPELINES中

ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}

後面的數字表示它的執行順序,從低到高執行,範圍0-1000

Feed exports

這裡順便提下Feed exports,一般有的爬蟲直接將爬取結果序列化到文件中,並保存到某個存儲介質中。只需要在settings裡面設置幾個即可:

* FEED_FORMAT= json # json|jsonlines|csv|xml|pickle|marshal
* FEED_URI= file:///tmp/export.csv|ftp://user:[email protected]/path/to/export.csv|s3://aws_key:aws_secret@mybucket/path/to/export.csv|stdout:
* FEED_EXPORT_FIELDS = ["foo", "bar", "baz"] # 這個在導出csv的時候有用

請求和響應

Scrapy使用Request和Response對象來爬取網站。Request對象被蜘蛛生成,然後被傳遞給下載器,之後下載器處理這個Request後返回Response對象,然後返回給生成Request的這個蜘蛛。

給回調函數傳遞額外的參數

Request對象生成的時候會通過關鍵字參數callback指定回調函數,Response對象被當做第一個參數傳入,有時候我們想傳遞額外的參數,比如我們構建某個Item的時候,需要兩步,第一步是鏈接屬性,第二步是詳情屬性,可以指定Request.meta

def parse_page1(self, response):
item = MyItem()
item['main_url'] = response.url
request = scrapy.Request("http://www.example.com/some_page.html",
callback=self.parse_page2)
request.meta['item'] = item
return request

def parse_page2(self, response):
item = response.meta['item']
item['other_url'] = response.url
return item

Request子類

Scrapy為各種不同的場景內置了很多Request子類,你還可以繼承它自定義自己的請求類。

FormRequest這個專門為form表單設計,模擬表單提交的示例

return [FormRequest(url="http://www.example.com/post/action",
formdata={'name': 'John Doe', 'age': '27'},
callback=self.after_post)]

我們再來一個例子模擬用戶登錄,使用了FormRequest.from_response()

import scrapy

class LoginSpider(scrapy.Spider):
name = 'example.com'
start_urls = ['http://www.example.com/users/login.php']

def parse(self, response):
return scrapy.FormRequest.from_response(
response,
formdata={'username': 'john', 'password': 'secret'},
callback=self.after_login
)

def after_login(self, response):
# check login succeed before going on
if "authentication failed" in response.body:
self.logger.error("Login failed")

return

# continue scraping with authenticated session...

Response子類

一個scrapy.http.Response對象代表了一個HTTP相應,通常是被下載器下載後得到,並交給Spider做進一步的處理。Response也有很多默認的子類,用於表示各種不同的響應類型。

  • TextResponse 在基本Response類基礎之上增加了編碼功能,專門用於二進制數據比如圖片、聲音或其他媒體文件
  • HtmlResponse 此類是TextResponse的子類,通過查詢HTML的meta http-equiv屬性實現了編碼自動發現
  • XmlResponse 此類是TextResponse的子類,通過查詢XML聲明實現編碼自動發現


分享到:


相關文章: