1. 程式人生 > >Scrapy—— Item Loaders

Scrapy—— Item Loaders

用Item Loader來填充Item

Item Loaders 提供了一種便捷的方式填充抓取到的Items。雖然Items可以使用自帶的類字典形式API填充,但是Items Loaders提供了更便捷的API,可以分析原始資料並對Item進行賦值。 另一方面,Items提供儲存抓取資料的容器,而Item Loaders提供的時填充容器的機制。Item Loaders提供的是一種靈活,高效的機制,可以更方便的被spider或source format擴充套件,並且庚日哦歌喲維護的、不同的內容分析規則。 要使用Item Loader,必須先將它例項化,可以使用類似字典的物件來進行例項化,當不使用物件進行例項化時,Item會自動使用ItemLoader.default_item_class

屬性中指定ltem類在Item Loader constructor中例項化。 然後開始收集數值到Item Loader時,通常使用Selectors。可以在item field裡面新增多個數值,Item Loader 將知道如何使用合適的處理函式來新增這些數值。 典型用法

from scrapy.loader import ItemLoader from myproject.items import Product def parse(self, response): l = ItemLoader(item=Product(), response=response) l.add_xpath('name'
, '//div[@class="product_name"]') l.add_xpath('name', '//div[@class="product_title"]') l.add_xpath('price', '//p[@id="price"]') l.add_css('stock', 'p#stock]') l.add_value('last_updated', 'today') # you can also use literal values return l.load_item()

使用add_xpath()方法從兩個Xpath位置提取資料來收集資料,這是稍後將分配給name欄位的資料。之後,可誒四的呼叫用於ptice和stock欄位,最後使用不同的方法last_update直接使用文字值填充欄位add_value()。收集資料時,該ItemLoader.load_item()

方法被稱為實際上返回調重先前提取並收集到的資料的專案add_xpath()add_css()add_value()呼叫。

輸出和輸入處理器

專案載入器對於每個欄位包含一個輸入和輸出處理器。輸入處理器只要它的接收處理所提取的資料和輸入處理器的結果被收集並保持Item Loader內部。收集所有資料後,ItemLoader.load_item()呼叫該方法來填充Item物件。這是當輸出處理器使用先前收集的資料呼叫時,輸出處理器的結果時分配給專案的最終值。 如何為特定欄位呼叫輸入和輸出處理器

l = ItemLoader(Product(), some_selector) l.add_xpath('name', xpath1) # (1) l.add_xpath('name', xpath2) # (2) l.add_css('name', css) # (3) 
l.add_value('name', 'test') # (4)
 return l.load_item() # (5)

內部執行過程:

  • (1)從資料xpath1提取出來,並通過所傳遞的輸入處理器的name欄位。輸入處理器的結果被收集並儲存在專案皆在其中(單尚未分配給專案);
  • (2)從xpath2提取資料。並通過1中使用的同一輸入處理器。輸入處理器的結果附加到1中收集的資料。
  • (3)類似於1和2,除了資料從CSS選擇器提取,並且通過在1和2中使用的相同的輸入處理器。輸入處理器的結果附加到1和2中收集的資料。
  • (4)類似於之前的情況,除了要收集的值直接分配。但是該值人仍然通過輸入處理器。在這種情況下,由於該值不可迭代,一次在將其傳遞給輸入處理器之前,它將轉換為單個元素的可迭代,因為輸入處理器總是接收迭代。
  • (5)在步驟1,2,3,4中收集的資料通過name欄位的輸出處理器。輸出處理器的結果是分配給name專案中欄位的值。

處理器值時可呼叫的物件,它們使用要解析的資料呼叫,並返回解析的值。所以你可以使用任何功能作為輸入或輸出處理器。唯一要求是它們必須接受一個位置引數,這將是一個迭代器。 深刻理解

  • 輸入處理器用於收集提取資料;
  • 輸出處理器用於將資料分配給欄位;

宣告專案載入器

通過使用類定義語法宣告為items。

from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst,MapCompose,Join




class ProductLoader(ItemLoader):

    default_output_processor = TakeFirst()

    name_in=MapCompose(unicode.title)
    name_out=Join()


    price_in=MapCompose(unicode.strip)

    #...

輸入處理器使用_in字尾宣告,而輸出處理器使用_out字尾宣告。號可以使用ItemLoader.default_input_processor和 ItemLoader.default_output_processor屬性宣告預設輸入和輸出處理器。

宣告輸入和輸出處理器

輸入和輸出處理器可以在Item Loader定義中宣告,這種方式宣告輸入處理器比較常見。還有一種方式是在專案欄位元資料中。

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


def filter_price(args):
    if args.isdigit():
        return args


class Product(scrapy.Item):

    name=scrapy.Field(
        input_processor=MapCompose(remove_tags),
        output_processor=Join(),

    )

    price=scrapy.Field(
        input_processor=MapCompose(remove_tags,filter_price),
        output_processor=TakeFirst(),

    )

if __name__=="__main__":
    i1=ItemLoader(item=Product())
    i1.add_value('name',[u'Welcome to my',u'<strong>website</strong>'])
    i1.add_value('price',[u'%',u'<span>1000</span>'])
    print i1.load_item()


# {'name': u'Welcome to my website', 'price': u'1000'}

輸入輸出處理器優先順序順序:

1,專案載入程式欄位特定屬性field_in和field_out; 2,欄位元資料input_processor和output_processor; 3,專案載入預設值ItemLoader.default_input_processor()和 ItemLoader.default_output_processor();

專案載入其上下文

專案載入器上下文是在專案載入器中的所有輸入和輸出處理器之間供像的任意鍵/值的dict。它可以在宣告,例項化或使用Item Loader時傳遞,用於修改輸入/輸出處理器的行為。

假設有一個parse_lengh接收文字值並從中提取長度的函式。

def  parse_length(text,loader_context ):
    unit = loader_context.get('unit''m' )
    #...長度解析程式碼在這裡... 
    return  parsed_length

通過接收一個loader_context引數,該函式顯式的告訴Item Loader它能夠接受一個Item Loader上下文,因此Item Loader在呼叫它時傳遞當前活動的上下文,因此處理器功能可以使用它們。

有幾種方法可以修改當前活動的Item Loader上下文:

1,通過修改當前或定的Item Loader上下文:

loader = ItemLoader(product)
loader.context['unit'] = 'cm'

2,On Item Loader例項化

loader = ItemLoader(product, unit='cm')

3,On Item Loader宣告,對於那些支援使用ItemLoader上下文例項化的輸入和輸出處理器。MapCompose是其中之一。

class ProductLoader(ItemLoader):
    length_out = MapCompose(parse_length, unit='cm')

ItemLoader物件

class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)

返回一個新的Item Loader來填充給定的Item。如果沒有給出專案,則使用其中的類自動例項化default_item_class。 當使用選擇器或響應引數例項化時,Item Loader類提供了使用選擇器從網頁中提取資料的方便機制。

引數:

  • item:專案例項來填充用以後呼叫add_xpath(),add_css()或add_value()。
  • selector:當使用add_xpath()(或。add_css())或replace_xpath() (或replace_css())方法時,從中提取資料的選擇器 。
  • response(Responseobject) - 用於使用構造選擇器的響應 default_selector_class,除非給出選擇器引數,在這種情況下,將忽略此引數。 專案,選擇器,響應和剩餘的關鍵字引數被分配給Loader上下文。

ItemLoader例項方法

  • get_value(value,* processors,** kwargs )處理給定value的給丁processors和關鍵字引數。 可用的關鍵字引數: re:用於使用方法從給定值提取資料,在處理器之前應用:
>>> from scrapy.loader.processors import TakeFirst
>>> loader.get_value(u'name: foo', TakeFirst(), unicode.upper, re='name: (.+)')
'FOO
  • add_value(field_name,value,* processors,** kwargs處理,然後新增給定value給給定欄位。該值首先通過get_value()賦予 processors和kwargs,然後通過 欄位輸入處理器及其結果追加到為該欄位收集的資料。如果欄位已包含收集的資料,則會新增新資料。給定field_name可以是None,在這種情況下可以新增多個欄位的值。並且已處理的值應為一個欄位,其中field_name對映到值。
  • replace_value(field_name,value,* processors,** kwargs )類似於add_value()單是用新值替換收集的資料,而不是新增它。
  • get_xpath(xpath,* processors,** kwargs )類似於ItemLoader.get_value()但接收的是XPath而不是值,用於從此相關聯的選擇器提取unicode字串的列表 ItemLoader。 xpath:從中提取資料的xpath re:用於從所選Xpath區域提取資料的正則表示式。

  • add_xpath(field_name,xpath,* processor,** kwargs )類似ItemLoader.add_value()但接收XPath而不是值,用於從與此相關聯的選擇器提取unicode字串的列表ItemLoader。

  • replace_xpath(field_name,xpath,* processor,** kwargs )類似於add_xpath()但替換收集的資料,而不是新增它。
  • get_css(css,* processors,** kwargs )類似於ItemLoader.get_value()但接收一個CSS選擇器而不是一個值,用於從與此相關的選擇器提取一個unicode字串列表ItemLoader。
  • add_css(field_name,css,* processors,** kwargs )類似於ItemLoader.add_value()但接收一個CSS選擇器而不是一個值,用於從與此相關的選擇器提取一個unicode字串列表ItemLoader。
  • replace_css(field_name,css,* processors,** kwargs )類似於add_css()但替換收集的資料,而不是新增它。
  • load_item()使用目前收集的資料填充專案,並返回。收集的資料首先通過輸出處理器,已獲得要分配給每個專案欄位的最終值。
  • nested_xpath(xpath )使用xpath選擇器建立巢狀載入器。所提供的選擇器應用於與此相關的選擇器。
  • nested_css(css )使用css選擇器建立巢狀載入器。所提供的選擇器應用於與此相關的選擇器ItemLoader。
  • get_collected_values(field_name )返回給定欄位的收集值。
  • get_output_value(field_name )返回給定欄位使用輸出處理器解析的收集值。此方法根本不填充或修改專案。
  • get_input_processor(field_name )返回給定欄位的輸入處理器。
  • get_output_processor(field_name )返回給定欄位的輸出處理器。

ItemLoader例項具有以下屬性

  • itemItem此專案載入器解析的物件;
  • context此專案Loader的當前活動上下文;
  • default_item_classItem類,用於在建構函式中未給出時例項化項;
  • default_input_processor用於不指定一個欄位的欄位的預設輸入處理器。
  • default_output_processor用於不指定一個欄位的欄位的預設輸出處理器。
  • default_selector_class所使用的類構造selector的此 ItemLoader,如果只響應在建構函式給出。如果在建構函式中給出了選擇器,則忽略此屬性。此屬性有時在子類中被覆蓋。
  • selectorSelector從中提取資料的物件。它是在建構函式中給出的選擇器,或者是從建構函式中使用的給定的響應建立的 default_selector_class。此屬性意味著是隻讀的。

巢狀裝載器

示例文件

<footer> <a class="social" href="http://facebook.com/whatever">Like Us</a> <a class="social" href="http://twitter.com/whatever">Follow Us</a> <a class="email" href="mailto:[email protected]">Email Us</a> </footer>

如果沒有巢狀載入器,則需要為要提取的每個值指定完整的xpath。像這樣:

loader = ItemLoader(item=Item()) # load stuff not in the footer loader.add_xpath('social', '//footer/a[@class = "social"]/@href') loader.add_xpath('email', '//footer/a[@class = "email"]/@href') loader.load_item()

如果使用頁尾選擇器建立巢狀載入器,並相對於頁尾新增值,功能是相同的,但是你避免重複頁尾選擇器。

loader = ItemLoader(item=Item()) # load stuff not in the footer footer_loader = loader.nested_xpath('//footer') footer_loader.add_xpath('social', 'a[@class = "social"]/@href') footer_loader.add_xpath('email', 'a[@class = "email"]/@href') # no need to call footer_loader.load_item() loader.load_item()

重用和擴充套件專案載入器

當你的專案很大時,維護成為一個根本的問題,特別是當你必須處理每個爬蟲的許多不同的解析規劃,有很多異常,但也想重用公共處理器。 專案載入器旨在減輕解析規則的維護負擔,同時不會失去靈活性,同時提供了擴充套件和覆蓋它們的方便機制。

例如,假設某個特定站點以三個短劃線(例如)包含其產品名稱,並且您不希望最終在最終產品名稱中刪除那些破折號。

from scrapy.loader.processors import MapCompose from myproject.ItemLoaders import ProductLoader def strip_dashes(x): return x.strip('-') class SiteSpecificLoader(ProductLoader): name_in = MapCompose(strip_dashes, ProductLoader.name_in)

另一種擴充套件專案,當你有多種源格式,例如XML和HTML,在XML版本中,你可能想要刪除CDTA事件。

from scrapy.loader.processors import MapCompose from myproject.ItemLoaders import ProductLoader from myproject.utils.xml import remove_cdata class XmlProductLoader(ProductLoader): name_in = MapCompose(remove_cdata, ProductLoader.name_in)

對於處理器,更常見的是在欄位元資料中宣告它們,因為它們通常僅依賴於欄位而不是特定站點解析規則。

可用內建處理器

  • class scrapy.loader.processors.Identity最簡單的處理器,神魔都不做。它返回原始值不變,不接受任何建構函式引數,也不接受Loader上下文。
  • class scrapy.loader.processors.TakeFirst從接收到的值中返滬ui第一個非空值,因此它通常用作單值字典的輸出處理器。它不接受任何建構函式引數,也不接受Loader上下文。
>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'one', 'two', 'three'])
'one'
  • class scrapy.loader.processors.Join(separator=u' ')返回與建構函式中給定的分隔符連線的值,預設為,不接受上下文。當使用預設分隔符時,此處理器相當於:u”.join.
>>> from scrapy.loader.processors import Join >>> proc = Join() >>> proc(['one', 'two', 'three']) u'one two three' >>> proc = Join('<br>') >>> proc(['one', 'two', 'three']) u'one<br>two<br>three'
  • class scrapy.loader.processors.Compose(*functions, **default_loader_context)由給定函式的組合構成的處理器。這意味著該處理器的每個輸入值都被傳遞給第一個函式,並且該函式的結果被傳遞給第二個函式,以此類推,直到最後一個函式返回該處理器的輸出值。預設情況下,停止進行None值,可以通過傳遞關鍵字引數來更改此行為stop_on_none=False。 每個功能可以可選的接收loader_cntext引數。在建構函式中傳遞的關鍵字引數用作傳遞給每個函式呼叫的預設Loader上下文值。但是傳遞給函式的最後一個Loader上下文值將被當前可用該屬性訪問的當前活動Loader上下文覆蓋。
  • class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)與處理器類似,由給定功能的組成的compose處理器,與此處理器的區別在與內部結果在函式之間傳遞的方式。(傳遞的值可迭代)

該處理器的輸入值被迭代,並且第一函式被應用於每個元素。這些函式呼叫的結果被連線以構造新的迭代,然後用於應用第二個函式,以此類推。直到最後一個函式被應用於收集的值列表的每個值。最後一個函式的輸出值被連線在一起已產生該處理器的輸出。 每個特定函式可以返回值或值列表,這些值通過應用於其他輸入值的相同函式返回的值列表展平。函式也可以返回None在這種情況下該函式的輸被忽略。 此處理器提供了一種方便方法來組合只使用單個只的函式。由於這個原因,MapCompose處理器通常用作輸入處理器,因為資料通常使用選擇器的 extract()方法提取,選擇器返回unicode字串的列表

  • class scrapy.loader.processors.SelectJmes(json_path)使用提供給建構函式的json路徑查詢值,並返回輸出。
>>> import json >>> proc_single_json_str = Compose(json.loads, SelectJmes("foo")) >>> proc_single_json_str('{"foo": "bar"}') u'bar' >>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('foo'))) >>> proc_json_list('[{"foo":"bar"}, {"baz":"tar"}]') [u'bar']