鍍金池/ 教程/ Java/ Scrapy項(xiàng)目加載器(Item Loader)
Scrapy項(xiàng)目加載器(Item Loader)
Scrapy快速入門
Scrapy蜘蛛(Spider)
Scrapy教程
Scrapy定義項(xiàng)目
Scrapy執(zhí)行爬行捉取
Scrapy項(xiàng)目(Items)
Scrapy安裝
Scrapy提取項(xiàng)目
Scrapy第一個(gè)Spider
Scrapy選擇器(Selector)
Scrapy Shell
Scrapy創(chuàng)建項(xiàng)目
Scrapy命令行工具
Scrapy日志
Scrapy使用項(xiàng)目

Scrapy項(xiàng)目加載器(Item Loader)

項(xiàng)目加載器提供了一個(gè)方便的方式來填補(bǔ)從網(wǎng)站上刮取的項(xiàng)目。

聲明項(xiàng)目加載器

項(xiàng)目加載器的聲明類:Items。例如:
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join

class DemoLoader(ItemLoader):

    default_output_processor = TakeFirst()

    title_in = MapCompose(unicode.title)
    title_out = Join()

    size_in = MapCompose(unicode.strip)

    # you can continue scraping here 

在上面的代碼可以看到,輸入處理器使用 _id 作為后綴以及輸出處理器聲明使用_out 作為后綴聲明。ItemLoader.default_input_processor 和 ItemLoader.default_output_processor 屬性用于聲明默認(rèn)輸入/輸出處理器。

使用項(xiàng)目加載器來填充項(xiàng)目

要使用項(xiàng)目加載器,先用類似字典的對象,或項(xiàng)目使用 Loader.default_item_class 屬性指定 Item 類實(shí)例化。
  • 可以使用選擇器來收集值到項(xiàng)目加載器。
  • 可以在同一項(xiàng)目字段中添加更多的值,項(xiàng)目加載器將使用相應(yīng)的處理程序來添加這些值。
下面的代碼演示項(xiàng)目是如何使用項(xiàng)目加載器來填充:
from scrapy.loader import ItemLoader
from demoproject.items import Demo

def parse(self, response):
    l = ItemLoader(item = Product(), response = response)
    l.add_xpath("title", "http://div[@class='product_title']")
    l.add_xpath("title", "http://div[@class='product_name']")
    l.add_xpath("desc", "http://div[@class='desc']")
    l.add_css("size", "div#size]")
    l.add_value("last_updated", "yesterday")
    return l.load_item()
如上圖所示,有兩種不同的XPath,使用 add_xpath()方法從標(biāo)題(title)字段提取:
1. //div[@class="product_title"] 

2. //div[@class="product_name"] 

此后,類似請求用于內(nèi)容描述(desc)字段。size數(shù)據(jù)使用 add_css()方法提取和last_updated 使用add_value()方法使用值“yesterday”來填充。

完成所有收集數(shù)據(jù)的,調(diào)用 ItemLoader.load_item() 方法返回填充并使用 add_xpath(),add_css()和 dadd_value()方法提取數(shù)據(jù)項(xiàng)。

輸入和輸出處理器

一個(gè)項(xiàng)目加載器的各個(gè)字段包含一個(gè)輸入處理器和一個(gè)輸出處理器。
  • 當(dāng)提取數(shù)據(jù)時(shí),輸入處理器處理結(jié)果,交將結(jié)果存儲在數(shù)據(jù)加載器。
  • 接下來,收集數(shù)據(jù)后,調(diào)用 ItemLoader.load_item() 方法來獲得 Item 對象。
  • 最后,指定輸出處理器到該項(xiàng)目的結(jié)果。
下面的代碼演示針對特定字段如何調(diào)用輸入和輸出處理器:
l = ItemLoader(Product(), some_selector)
l.add_xpath("title", xpath1) # [1]
l.add_xpath("title", xpath2) #  [2]
l.add_css("title", css) # [3]
l.add_value("title", "demo") # [4]
return l.load_item() # [5]
  • 第1行: 標(biāo)題(title)的數(shù)據(jù)是從xpath1提取并通過輸入處理器,其結(jié)果被收集并存儲在 ItemLoader 中。

  • 第2行: 同樣地,標(biāo)題(title)從xpath2提取并通過相同的輸入處理器,其結(jié)果收集的數(shù)據(jù)加到[1]中。

  • 第3行: 標(biāo)題(title)被從css選擇萃取和通過相同的輸入處理器傳遞并將收集的數(shù)據(jù)結(jié)果加到[1]及[2]。

  • 第4行: 接著,將“demo”值分配并傳遞到輸入處理器。

  • 第5行: 最后,數(shù)據(jù)是從所有字段內(nèi)部收集并傳遞給輸出處理器,最終值將分配給項(xiàng)目。

聲明輸入和輸出處理器

輸入和輸出的處理器在項(xiàng)目加載器(ItemLoader )定義聲明。除此之外,它們還可以在項(xiàng)目字段的元數(shù)據(jù)指定。

例如:

import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.htmll import remove_tags

def filter_size(value):
    if value.isdigit():
        return value

class Item(scrapy.Item):
    name = scrapy.Field(
        input_processor = MapCompose(remove_tags),
        output_processor = Join(),
    )
    size = scrapy.Field(
       input_processor = MapCompose(remove_tags, filter_price),
       output_processor = TakeFirst(),
    )
>>> from scrapy.loader import ItemLoader
>>> il = ItemLoader(item=Product())
>>> il.add_value('title', [u'Hello', u'<strong>world</strong>'])
>>> il.add_value('size', [u'<span>100 kg</span>'])
>>> il.load_item()
它顯示的輸出結(jié)果如下:
{'title': u'Hello world', 'size': u'100 kg'}

項(xiàng)目加載器上下文

項(xiàng)目加載器上下文是輸入和輸出的處理器中共享的任意鍵值的字典。
例如,假設(shè)有一個(gè)函數(shù)parse_length:
def parse_length(text, loader_context):
    unit = loader_context.get('unit', 'cm')
    # You can write parsing code of length here 
    return parsed_length 

通過接收loader_context參數(shù),它告訴項(xiàng)目加載器可以收到項(xiàng)目加載器上下文。有幾種方法可以改變項(xiàng)目加載器上下文的值:

  • 修改當(dāng)前的活動項(xiàng)目加載器上下文:
    loader = ItemLoader (product)
    loader.context ["unit"] = "mm"
  • 在項(xiàng)目加載器實(shí)例中修改:
    loader = ItemLoader(product, unit="mm")
  • 在加載器項(xiàng)目聲明與項(xiàng)目加載器上下文實(shí)例輸入/輸出處理器中修改:
    class ProductLoader(ItemLoader):
        length_out = MapCompose(parse_length, unit="mm")

ItemLoader對象

它是一個(gè)對象,它返回一個(gè)新項(xiàng)加載器到填充給定項(xiàng)目。它有以下類:
class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)
下面的表顯示 ItemReader 對象的參數(shù):
S.N. 參數(shù) & 描述
1 item
它是通過 calling add_xpath(), add_css() 或 add_value()的填充項(xiàng)
2 selector
它用來從網(wǎng)站提取數(shù)據(jù)
3 response
它是用 default_selector_class 來構(gòu)造選擇器
下表顯示項(xiàng)目加載器(ItemLoader)對象的方法:
S.N. 方法 & 描述 示例
1 get_value(value, *processors, **kwargs)
由一個(gè)給定的處理器和關(guān)鍵字參數(shù),該值在getValue()方法處理
    >>> from scrapy.loader.processors import TakeFirst
    >>> loader.get_value(u'title: demoweb', TakeFirst(), unicode.upper, re='title: (.+)')
    'DEMOWEB`
		
2 add_value(field_name, value, *processors, **kwargs)
它首先通過get_value傳遞處理值,并增加到字段中
    loader.add_value('title', u'DVD')
    loader.add_value('colors', [u'black', u'white'])
    loader.add_value('length', u'80')
    loader.add_value('price', u'2500')
    
3 replace_value(field_name, value, *processors, **kwargs)
它用一個(gè)新值替換所收集的數(shù)據(jù)
    loader.replace_value('title', u'DVD')
    loader.replace_value('colors', [u'black', u'white'])
    loader.replace_value('length', u'80')
    loader.replace_value('price', u'2500')
	
4 get_xpath(xpath, *processors, **kwargs)
它用于由接到的XPath給處理器和關(guān)鍵字參數(shù)提取unicode字符串
    # HTML code: <div class="item-name">DVD</div>
    loader.get_xpath("http://div[@class='item-name']")
    # HTML code: <div id="length">the length is 45cm</div>
    loader.get_xpath("http://div[@id='length']", TakeFirst(), re="the length is (.*)")
    
5 add_xpath(field_name, xpath, *processors, **kwargs)
它接收XPath提取unicode字符串到字段中
    # HTML code: <div class="item-name">DVD</div>
    loader.add_xpath('name', '//div[@class="item-name"]')
    # HTML code: <div id="length">the length is 45cm</div>
    loader.add_xpath('length', '//div[@id="length"]', re='the length is (.*)')
    
6 replace_xpath(field_name, xpath, *processors, **kwargs)
它使用XPath取換了從網(wǎng)站收集的數(shù)據(jù)
    # HTML code: <div class="item-name">DVD</div>
    loader.replace_xpath('name', '//div[@class="item-name"]')
    # HTML code: <div id="length">the length is 45cm</div>
    loader.replace_xpath('length', '//div[@id="length"]', re='the length is (.*)')
    
7 get_css(css, *processors, **kwargs)
它接收用于提取unicode字符串的CSS選擇器
    loader.get_css("div.item-name")
    loader.get_css("div#length", TakeFirst(), re="the length is (.*)")
    
8 add_css(field_name, css, *processors, **kwargs)
它類似于add_value()方法,它增加CSS選擇器到字段中
    loader.add_css('name', 'div.item-name')
    loader.add_css('length', 'div#length', re='the length is (.*)')
    
9 replace_css(field_name, css, *processors, **kwargs)
它使用CSS選擇器取代了提取的數(shù)據(jù)
    loader.replace_css('name', 'div.item-name')
    loader.replace_css('length', 'div#length', re='the length is (.*)')
    
10 load_item()
當(dāng)收集數(shù)據(jù)后,這個(gè)方法填充收集到數(shù)據(jù)的項(xiàng)目并返回
    def parse(self, response):
        l = ItemLoader(item=Product(), response=response)
        l.add_xpath('title', '//div[@class="product_title"]')
        loader.load_item()
    
11 nested_xpath(xpath)
它是通過XPath選擇器來創(chuàng)建嵌套加載器
    loader = ItemLoader(item=Item())
    loader.add_xpath('social', 'a[@class = "social"]/@href')
    loader.add_xpath('email', 'a[@class = "email"]/@href')
	
12 nested_css(css)
它被用來創(chuàng)建一個(gè)CSS選擇器嵌套加載器
    loader = ItemLoader(item=Item())
    loader.add_css('social', 'a[@class = "social"]/@href')
    loader.add_css('email', 'a[@class = "email"]/@href')	
	
下表顯示項(xiàng)目加載器對象的屬性:
S.N. 屬性 & 描述
1 item
它是項(xiàng)目加載器進(jìn)行解析的對象
2 context
這是項(xiàng)目加載器是活躍的當(dāng)前上下文
3 default_item_class
如果在構(gòu)造沒有給出,它用來表示項(xiàng)
4 default_input_processor
不指定輸入處理器中的字段,只有一個(gè)用于其默認(rèn)輸入處理器
5 default_output_processor
不指定輸出處理器中的字段,只有一個(gè)用于其默認(rèn)的輸出處理器
6 default_selector_class
如果它沒有在構(gòu)造給定,它是使用來構(gòu)造選擇器的一個(gè)類
7 selector
它是一個(gè)用來從站點(diǎn)提取數(shù)據(jù)的對象

嵌套加載器

這是使用從文檔解析分段的值來創(chuàng)建嵌套加載器。如果不創(chuàng)建嵌套裝載器,需要為您想提取的每個(gè)值指定完整的XPath或CSS。

例如,假設(shè)要從一個(gè)標(biāo)題頁中提取數(shù)據(jù):
<header>
  <a class="social" >facebook</a>
  <a class="social" >twitter</a>
  <a class="email" href="mailto:someone@example.com">send mail</a>
</header>
接下來,您可以通過添加相關(guān)的值到頁眉來創(chuàng)建頭選擇器嵌套裝載器:
loader = ItemLoader(item=Item())
header_loader = loader.nested_xpath('//header')
header_loader.add_xpath('social', 'a[@class = "social"]/@href')
header_loader.add_xpath('email', 'a[@class = "email"]/@href')
loader.load_item()

重用和擴(kuò)展項(xiàng)目加載器

項(xiàng)目加載器的設(shè)計(jì)以緩解維護(hù),當(dāng)要獲取更多的蜘蛛時(shí)項(xiàng)目變成一個(gè)根本的問題。

舉例來說,假設(shè)一個(gè)網(wǎng)站自己的產(chǎn)品名稱是由三條短線封閉的(例如: ---DVD---)。 您可以通過重復(fù)使用默認(rèn)產(chǎn)品項(xiàng)目加載器,如果你不希望它在最終產(chǎn)品名稱所示,下面的代碼刪除這些破折號:

from scrapy.loader.processors import MapCompose
from demoproject.ItemLoaders import DemoLoader

def strip_dashes(x):
    return x.strip('-')

class SiteSpecificLoader(DemoLoader):
    title_in = MapCompose(strip_dashes, DemoLoader.title_in)

可用內(nèi)置處理器

以下是一些常用的內(nèi)置處理器:
  • class scrapy.loader.processors.Identity

    它返回原始的值而并不修改它。 例如:
    >>> from scrapy.loader.processors import Identity
    >>> proc = Identity()
    >>> proc(['a', 'b', 'c'])
    ['a', 'b', 'c']
  • class scrapy.loader.processors.TakeFirst

    它返回一個(gè)值來自收到列表的值即非空/非null值。 例如:
    >>> from scrapy.loader.processors import TakeFirst
    >>> proc = TakeFirst()
    >>> proc(['', 'a', 'b', 'c'])
    'a'
  • class scrapy.loader.processors.Join(separator = u' ')

    它返回附連到分隔符的值。默認(rèn)的分隔符是 u'',這相當(dāng)于于 u' '.join 的功能。例如:
    >>> from scrapy.loader.processors import Join
    >>> proc = Join()
    >>> proc(['a', 'b', 'c'])
    u'a b c'
    >>> proc = Join('<br>')
    >>> proc(['a', 'b', 'c'])
    u'a<br>b<br>c'
  • class scrapy.loader.processors.SelectJmes(json_path)

    此類查詢使用提供JSON路徑值,并返回輸出。
    例如:
    >>> from scrapy.loader.processors import SelectJmes, Compose, MapCompose
    >>> proc = SelectJmes("hello")
    >>> proc({'hello': 'scrapy'})
    'scrapy'
    >>> proc({'hello': {'scrapy': 'world'}})
    {'scrapy': 'world'}
    下面是一個(gè)查詢通過導(dǎo)入JSON值的代碼:
    >>> import json
    >>> proc_single_json_str = Compose(json.loads, SelectJmes("hello"))
    >>> proc_single_json_str('{"hello": "scrapy"}')
    u'scrapy'
    >>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('hello')))
    >>> proc_json_list('[{"hello":"scrapy"}, {"world":"env"}]')
    [u'scrapy']