當一個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聲明實現編碼自動發現
閱讀更多 新猿IT人 的文章