Scrapy-Item Loaders(项目加载器)
?
Item Loaders:http://doc.scrapy.org/en/latest/topics/loaders.html
Item Loaders(中文版):https://scrapy-chs.readthedocs.io/zh_CN/latest/topics/loaders.html
?
?
?
項目加載器
?
Item Loaders(項目加載器) 提供了一種方便的機制來填充抓取的項目。即使 可以使用自己的類似字典的API填充項目,Item Loaders(項目加載器)提供了一個更方便的API,通過自動化一些常見的任務,如解析原始提取的數據,然后分配它從剪貼過程中填充他們。
換句話說,items 提供了抓取數據的容器,而 Item Loader 提供了填充該容器的機制。
Item Loaders(項目加載器)旨在提供一種靈活,高效和容易的機制,通過爬蟲或源格式(HTML,XML等)擴展和覆蓋不同的字段解析規則,而不會成為維護的噩夢。
?
?
使用裝載機項目來填充的項目
?
要使用項目加載器,您必須首先實例化它。您可以使用類似dict的對象(例如Item或dict)實例化它,也可以不使用它,在這種情況下,項目將在Item Loader構造函數中使用屬性中指定的Item類自動ItemLoader.default_item_class?實例化。
然后,您開始收集值到項裝載程序,通常使用選擇器。您可以向同一項目字段添加多個值; 項目加載器將知道如何使用適當的處理函數“加入”這些值。
這里是?Spider?中典型的 Item Loader 用法,使用?Items部分?中聲明的?Product?項:
from scrapy.loader import ItemLoader from myproject.items import Productdef 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 valuesreturn l.load_item()通過快速查看該代碼,我們可以看到該?name字段正從頁面中兩個不同的XPath位置提取:
換句話說,通過使用?add_xpath()方法從兩個XPath位置提取數據來收集數據。這是稍后將分配給name字段的數據。
之后,類似的調用用于?price?和?stock?字段(后者使用帶有add_css()方法的CSS選擇器),最后使用不同的方法last_update直接使用文字值(today)填充字段add_value()。
最后,收集的所有數據時,該?ItemLoader.load_item()方法被稱為實際上返回填充先前提取并與收集到的數據的項目add_xpath(),?add_css()和add_value()調用。
?
?
輸入和輸出處理器
?
項目加載器對于每個(項目)字段包含一個輸入處理器和一個輸出處理器。輸入處理器只要它的接收處理所提取的數據(通過add_xpath(),add_css()或 add_value()方法)和輸入處理器的結果被收集并保持ItemLoader內部。收集所有數據后,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)所以會發生什么:
值得注意的是,處理器只是可調用對象,它們使用要解析的數據調用,并返回解析的值。所以你可以使用任何功能作為輸入或輸出處理器。唯一的要求是它們必須接受一個(也只有一個)位置參數,這將是一個迭代器。
注意
輸入和輸出處理器都必須接收一個迭代器作為它們的第一個參數。這些函數的輸出可以是任何東西。輸入處理器的結果將附加到包含收集的值(對于該字段)的內部列表(在加載程序中)。輸出處理器的結果是最終分配給項目的值。
另一件需要記住的事情是,輸入處理器返回的值在內部(在列表中)收集,然后傳遞到輸出處理器以填充字段。
最后,但并非最不重要的是,Scrapy自帶一些常用的處理器內置的方便。
?
?
聲明項目加載器
?
項目加載器通過使用類定義語法聲明為 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 Join, MapCompose, TakeFirst from w3lib.html import remove_tagsdef filter_price(value):if value.isdigit():return value 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(),)>>> from scrapy.loader import ItemLoader
>>> il = ItemLoader(item=Product())
>>> il.add_value('name', [u'Welcome to my', u'<strong>website</strong>'])
>>> il.add_value('price', [u'€', u'<span>1000</span>'])
>>> il.load_item()
{'name': u'Welcome to my website', 'price': u'1000'}
輸入和輸出處理器的優先級順序如下:
參見:重用和擴展項目加載器。
?
?
項目加載器上下文
?
項目加載器上下文是在項目加載器中的所有輸入和輸出處理器之間共享的任意鍵/值的dict。它可以在聲明,實例化或使用Item Loader時傳遞。它們用于修改輸入/輸出處理器的行為。
例如,假設您有一個parse_length接收文本值并從中提取長度的函數:
def parse_length(text, loader_context):unit = loader_context.get('unit', 'm')# ... length parsing code goes here ...return parsed_length通過接受一個loader_context參數,該函數顯式地告訴Item Loader它能夠接收一個Item Loader上下文,因此Item Loader在調用它時傳遞當前活動的上下文,因此處理器功能(parse_length在這種情況下)可以使用它們。
?
有幾種方法可以修改Item Loader上下文值:
- 1. 通過修改當前活動的Item Loader上下文(context屬性):
?loader = ItemLoader(product)
?loader.context['unit'] = 'cm' - 2.?On Item Loader實例化(Item Loader構造函數的關鍵字參數存儲在Item Loader上下文中):
?loader = ItemLoader(product, unit='cm') - 3.?On Item Loader聲明,對于那些支持使用Item Loader上下文實例化的輸入/輸出處理器。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。
當使用選擇器或響應參數實例化時,ItemLoader類提供了使用選擇器從網頁提取數據的方便的機制。
參數:
- item(Item對象)-項目實例來填充用以后調用 add_xpath(),add_css()或add_value()。
- selector(Selectorobject) - 當使用add_xpath()(或。add_css())或replace_xpath() (或replace_css())方法時,從中提取數據的選擇器 。
- response(Responseobject) - 用于使用構造選擇器的響應 default_selector_class,除非給出選擇器參數,在這種情況下,將忽略此參數。
item, selector, response?和 剩余的 關鍵字參數 被分配給 Loader上下文(可通過context屬性訪問)。
?
?
ItemLoader 實例有以下方法:
?
get_value(value,* processors,** kwargs )
處理給定value的給定processors和關鍵字參數。
可用的關鍵字參數:
參數: re(str 或compiled regex):一個正則表達式extract_regex(),用于使用方法從給定值提取數據,在處理器之前應用
例子:
>>> 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映射到值。
例子:
loader.add_value('name', u'Color TV') loader.add_value('colours', [u'white', u'blue']) loader.add_value('length', u'100') loader.add_value('name', u'name: foo', TakeFirst(), re='name: (.+)') loader.add_value(None, {'name': u'foo', 'sex': u'male'})replace_value(field_name,value,* processors,** kwargs )
類似于add_value()但是用新值替換收集的數據,而不是添加它。
get_xpath(xpath,* processors,** kwargs)
類似于ItemLoader.get_value()但接收XPath而不是值,用于從與此相關聯的選擇器提取unicode字符串的列表ItemLoader。
參數:
- xpath(str) - 從中??提取數據的XPath
- re(str 或compiled regex) - 用于從所選XPath區域提取數據的正則表達式
例子:
# HTML snippet: <p class="product-name">Color TV</p> loader.get_xpath('//p[@class="product-name"]') # HTML snippet: <p id="price">the price is $1200</p> loader.get_xpath('//p[@id="price"]', TakeFirst(), re='the price is (.*)')add_xpath(field_name,xpath,* processor,** kwargs)
類似于ItemLoader.add_value()但接收XPath而不是值,用于從與此相關聯的選擇器提取unicode字符串的列表ItemLoader。
見 get_xpath() 的 kwargs。
參數:xpath(str) - 從中??提取數據的XPath
例子:
# HTML snippet: <p class="product-name">Color TV</p> loader.add_xpath('name', '//p[@class="product-name"]') # HTML snippet: <p id="price">the price is $1200</p> loader.add_xpath('price', '//p[@id="price"]', re='the price is (.*)')replace_xpath(field_name,xpath,* processor,** kwargs)
類似于add_xpath()但替換收集的數據,而不是添加它。
get_css(css,* processors,** kwargs)
類似于ItemLoader.get_value()但接收一個CSS選擇器而不是一個值,用于從與此相關的選擇器提取一個unicode字符串列表ItemLoader。
參數:
- css(str) - 從中??提取數據的CSS選擇器
- re(str 或compiled regex) - 用于從所選CSS區域提取數據的正則表達式
例子:
# HTML snippet: <p class="product-name">Color TV</p> loader.get_css('p.product-name') # HTML snippet: <p id="price">the price is $1200</p> loader.get_css('p#price', TakeFirst(), re='the price is (.*)')add_css(field_name,css,* processors,** kwargs)
類似于ItemLoader.add_value()但接收一個CSS選擇器而不是一個值,用于從與此相關的選擇器提取一個unicode字符串列表ItemLoader。
見 get_css()的kwargs。
參數:css(str) - 從中??提取數據的CSS選擇器
例子:
# HTML snippet: <p class="product-name">Color TV</p> loader.add_css('name', 'p.product-name') # HTML snippet: <p id="price">the price is $1200</p> loader.add_css('price', 'p#price', re='the price is (.*)')replace_css(field_name,css,* processors,** kwargs )
類似于add_css()但替換收集的數據,而不是添加它。
load_item()
使用目前收集的數據填充項目,并返回。收集的數據首先通過輸出處理器,以獲得要分配給每個項目字段的最終值。
nested_xpath(xpath )
使用xpath選擇器創建嵌套加載器。所提供的選擇器應用于與此相關的選擇器ItemLoader。嵌套裝載機股份Item 與母公司ItemLoader這么調用add_xpath(), add_value(),replace_value()等會像預期的那樣。
nested_css(css )
使用css選擇器創建嵌套加載器。所提供的選擇器應用于與此相關的選擇器ItemLoader。嵌套裝載機股份Item 與母公司ItemLoader這么調用add_xpath(), add_value(),replace_value()等會像預期的那樣。
get_collected_values(field_name )
返回給定字段的收集值。
get_output_value(field_name )
返回給定字段使用輸出處理器解析的收集值。此方法根本不填充或修改項目。
get_input_processor(field_name )
返回給定字段的輸入處理器。
get_output_processor(field_name )
返回給定字段的輸出處理器。
?
?
ItemLoader 實例具有以下屬性:
?
item
Item此項目加載器解析的對象。
context
此項目Loader 的當前活動上下文。
default_item_class
Item類(或工廠),用于在構造函數中未給出時實例化項。
default_input_processor
用于不指定一個字段的字段的默認輸入處理器。
default_output_processor
用于不指定一個字段的字段的默認輸出處理器。
default_selector_class
所使用的類構造selector的此 ItemLoader,如果只響應在構造函數給出。如果在構造函數中給出了選擇器,則忽略此屬性。此屬性有時在子類中被覆蓋。
selector
Selector從中提取數據的對象。它是在構造函數中給出的選擇器,或者是從構造函數中使用的給定的響應創建的 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:whatever@example.com">Email Us</a> </footer>如果沒有嵌套加載器,則需要為要提取的每個值指定完整的xpath(或css)。
例:
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()您可以任意嵌套加載器,并且可以使用xpath或css選擇器。作為一般的指導原則,當他們使你的代碼更簡單,但不要超越嵌套或使用解析器可能變得難以閱讀使用嵌套加載程序。
?
?
重用和擴展項目加載器
?
隨著你的項目越來越大,越來越多的爬蟲,維護成為一個根本的問題,特別是當你必須處理每個爬蟲的許多不同的解析規則,有很多異常,但也想重用公共處理器。
項目加載器旨在減輕解析規則的維護負擔,同時不會失去靈活性,同時提供了擴展和覆蓋它們的方便的機制。因此,項目加載器支持傳統的Python類繼承,以處理特定爬蟲(或爬蟲組)的差異。
例如,假設某個特定站點以三個短劃線(例如)包含其產品名稱,并且您不希望最終在最終產品名稱中刪除那些破折號。---Plasma TV---
以下是如何通過重用和擴展默認產品項目Loader(ProductLoader)來刪除這些破折號:
from scrapy.loader.processors import MapCompose from myproject.ItemLoaders import ProductLoaderdef strip_dashes(x):return x.strip('-')class SiteSpecificLoader(ProductLoader):name_in = MapCompose(strip_dashes, ProductLoader.name_in)另一種擴展項目加載器可能非常有用的情況是,當您有多種源格式,例如XML和HTML。在XML版本中,您可能想要刪除CDATA事件。下面是一個如何做的例子:
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)這就是你通常擴展輸入處理器的方式。
對于輸出處理器,更常見的是在字段元數據中聲明它們,因為它們通常僅依賴于字段而不是每個特定站點解析規則(如輸入處理器)。另請參見: 聲明輸入和輸出處理器。
還有許多其他可能的方法來擴展,繼承和覆蓋您的項目加載器,不同的項目加載器層次結構可能更適合不同的項目。Scrapy只提供了機制; 它不強加任何特定的組織你的Loader集合 - 這取決于你和你的項目的需要。
?
?
可用內置處理器
?
即使您可以使用任何可調用函數作為輸入和輸出處理器,Scrapy也提供了一些常用的處理器,如下所述。其中一些,像MapCompose(通常用作輸入處理器)組成按順序執行的幾個函數的輸出,以產生最終的解析值。
下面是所有內置處理器的列表:
class scrapy.loader.processors.Identity
最簡單的處理器,什么都不做。它返回原始值不變。它不接收任何構造函數參數,也不接受Loader上下文。
例:
>>> from scrapy.loader.processors import Identity
>>> proc = Identity()
>>> proc(['one', 'two', 'three'])
['one', 'two', 'three']
class scrapy.loader.processors.TakeFirst
從接收到的值中返回第一個非空值/非空值,因此它通常用作單值字段的輸出處理器。它不接收任何構造函數參數,也不接受Loader上下文。
例:
>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'one', 'two', 'three'])
'one'
class scrapy.loader.processors.Join(separator=u' ')
返回與構造函數中給定的分隔符聯接的值,默認為。它不接受加載器上下文。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。
例:
>>> from scrapy.loader.processors import Compose
>>> proc = Compose(lambda v: v[0], str.upper)
>>> proc(['hello', 'world'])
'HELLO'
每個功能可以可選地接收loader_context參數。對于那些處理器,這個處理器將通過該參數傳遞當前活動的Loader上下文。
在構造函數中傳遞的關鍵字參數用作傳遞給每個函數調用的默認Loader上下文值。但是,傳遞給函數的最后一個Loader上下文值將被當前可用該屬性訪問的當前活動Loader上下文ItemLoader.context() 覆蓋。
class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)
與處理器類似,由給定功能的組成構成的Compose處理器。與此處理器的區別在于內部結果在函數之間傳遞的方式,如下所示:
該處理器的輸入值被迭代,并且第一函數被應用于每個元素。這些函數調用的結果(每個元素一個)被連接以構造新的迭代,然后用于應用??第二個函數,等等,直到最后一個函數被應用于收集的值列表的每個值遠。最后一個函數的輸出值被連接在一起以產生該處理器的輸出。
每個特定函數可以返回值或值列表,這些值通過應用于其他輸入值的相同函數返回的值列表展平。函數也可以返回None,在這種情況下,該函數的輸出將被忽略,以便在鏈上進行進一步處理。
此處理器提供了一種方便的方法來組合只使用單個值(而不是iterables)的函數。由于這個原因, MapCompose處理器通常用作輸入處理器,因為數據通常使用選擇器的 extract()方法提取,選擇器返回unicode字符串的列表。
下面的例子應該說明它是如何工作的:
>>> def filter_world(x):
... ? ? return None if x == 'world' else x
...
>>> from scrapy.loader.processors import MapCompose
>>> proc = MapCompose(filter_world, unicode.upper)
>>> proc([u'hello', u'world', u'this', u'is', u'scrapy'])
[u'HELLO, u'THIS', u'IS', u'SCRAPY']
與Compose處理器一樣,函數可以接收Loader上下文,并且構造函數關鍵字參數用作默認上下文值。有關Compose更多信息,請參閱 處理器。
class scrapy.loader.processors.SelectJmes(json_path)
使用提供給構造函數的json路徑查詢值,并返回輸出。需要運行jmespath(https://github.com/jmespath/jmespath.py)。該處理器一次只需要一個輸入。
例:
>>> from scrapy.loader.processors import SelectJmes, Compose, MapCompose
>>> proc = SelectJmes("foo") #for direct use on lists and dictionaries
>>> proc({'foo': 'bar'})
'bar'
>>> proc({'foo': {'bar': 'baz'}})
{'bar': 'baz'}
?
使用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']
?
?
?
總結
以上是生活随笔為你收集整理的Scrapy-Item Loaders(项目加载器)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scrapy源码阅读分析_3_核心组件
- 下一篇: Linux-----diff命令