# 項目加載器
> 譯者:[OSGeo 中國](https://www.osgeo.cn/)
物品裝載機為填充抓取物提供了一種方便的機制。 [Items](items.html#topics-items) . 即使可以使用它們自己的字典(如API)來填充項,項加載器也提供了一個更方便的API,通過自動化一些常見任務(如在分配原始提取數據之前對其進行解析)來從抓取過程填充項。
換言之, [Items](items.html#topics-items) 提供 _container_ 當項目加載器為 _populating_ 那個容器。
項目加載器的設計目的是提供一種靈活、高效和簡單的機制,通過 Spider 或源格式(HTML、XML等)擴展和重寫不同的字段解析規則,而不會成為維護的噩夢。
## 使用項目加載器填充項目
要使用項目加載器,必須首先實例化它。可以使用類似dict的對象(例如item或dict)或不使用對象來實例化它,在這種情況下,將使用在 [`ItemLoader.default_item_class`](#scrapy.loader.ItemLoader.default_item_class "scrapy.loader.ItemLoader.default_item_class") 屬性。
然后,開始將值收集到項加載器中,通常使用 [Selectors](selectors.html#topics-selectors) . 您可以向同一個項目字段添加多個值;項目加載器稍后將知道如何使用適當的處理函數“聯接”這些值。
下面是在 [Spider](spiders.html#topics-spiders) ,使用 [Product item](items.html#topics-items-declaring) [Items chapter](items.html#topics-items) ::
```py
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()
```
通過快速查看該代碼,我們可以看到 `name` 正在從頁面中的兩個不同的xpath位置提取字段:
1. `//div[@class="product_name"]`
2. `//div[@class="product_title"]`
換句話說,通過使用 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") 方法。這是將分配給 `name` 以后再說。
之后,類似的呼叫用于 `price` 和 `stock` 字段(后者使用CSS選擇器 [`add_css()`](#scrapy.loader.ItemLoader.add_css "scrapy.loader.ItemLoader.add_css") 方法),最后 `last_update` 直接用文字值填充字段( `today` )使用不同的方法: [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") .
最后,當收集所有數據時, [`ItemLoader.load_item()`](#scrapy.loader.ItemLoader.load_item "scrapy.loader.ItemLoader.load_item") 方法,它實際返回用以前提取和收集的數據填充的項 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") , [`add_css()`](#scrapy.loader.ItemLoader.add_css "scrapy.loader.ItemLoader.add_css") 和 [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") 電話。
## 輸入和輸出處理器
項目加載器為每個(項目)字段包含一個輸入處理器和一個輸出處理器。輸入處理器一旦接收到提取的數據(通過 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") , [`add_css()`](#scrapy.loader.ItemLoader.add_css "scrapy.loader.ItemLoader.add_css") 或 [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") 方法),輸入處理器的結果被收集并保存在itemloader中。在收集所有數據之后, [`ItemLoader.load_item()`](#scrapy.loader.ItemLoader.load_item "scrapy.loader.ItemLoader.load_item") 方法來填充和獲取填充的 [`Item`](items.html#scrapy.item.Item "scrapy.item.Item") 對象。這就是使用先前收集的數據(并使用輸入處理器處理)調用輸出處理器的時候。輸出處理器的結果是分配給該項的最終值。
讓我們看一個例子來說明如何為一個特定的字段調用輸入和輸出處理器(這同樣適用于任何其他字段)::
```py
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` 提取并通過 _input processor_ 的 `name` 字段。輸入處理器的結果被收集并保存在項目加載器中(但尚未分配給項目)。
2. 數據來自 `xpath2` 提取并通過 _input processor_ 用于(1)。輸入處理器的結果將附加到(1)中收集的數據(如果有)中。
3. 這種情況與以前的情況類似,只是數據是從 `css` 并通過相同的 _input processor_ 用于(1)和(2)。輸入處理器的結果將附加到(1)和(2)中收集的數據(如果有)中。
4. 這種情況也與前面的情況類似,只是要收集的值是直接分配的,而不是從xpath表達式或css選擇器中提取的。但是,該值仍然通過輸入處理器傳遞。在這種情況下,由于該值不可重設,因此在將其傳遞給輸入處理器之前,它將轉換為單個元素的可重設值,因為輸入處理器始終接收可重設值。
5. 步驟(1)、(2)、(3)和(4)中收集的數據通過 _output processor_ 的 `name` 字段。輸出處理器的結果是分配給 `name` 項目中的字段。
值得注意的是,處理器只是可調用的對象,用要解析的數據調用,并返回一個已解析的值。所以您可以使用任何函數作為輸入或輸出處理器。唯一的要求是他們必須接受一個(并且只有一個)位置參數,這將是一個迭代器。
注解
輸入和輸出處理器都必須接收一個迭代器作為它們的第一個參數。這些函數的輸出可以是任何東西。輸入處理器的結果將附加到包含收集值(針對該字段)的內部列表(在加載程序中)。輸出處理器的結果是最終分配給該項的值。
如果要將普通函數用作處理器,請確保它接收 `self` 作為第一個論點:
```py
def lowercase_processor(self, values):
for v in values:
yield v.lower()
class MyItemLoader(ItemLoader):
name_in = lowercase_processor
```
這是因為每當函數被指定為類變量時,它就會成為一個方法,并在被調用時作為第一個參數傳遞實例。見 [this answer on stackoverflow](https://stackoverflow.com/a/35322635) 了解更多詳細信息。
您需要記住的另一件事是,輸入處理器返回的值在內部收集(在列表中),然后傳遞給輸出處理器來填充字段。
最后,但并非最不重要的是,Scrapy有一些 [commonly used processors](#topics-loaders-available-processors) 內置方便。
## 聲明項加載器
通過使用類定義語法,將項加載器聲明為項。下面是一個例子:
```py
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`](#scrapy.loader.ItemLoader.default_input_processor "scrapy.loader.ItemLoader.default_input_processor") 和 [`ItemLoader.default_output_processor`](#scrapy.loader.ItemLoader.default_output_processor "scrapy.loader.ItemLoader.default_output_processor") 屬性。
## 聲明輸入和輸出處理器
如前一節所述,可以在項目加載器定義中聲明輸入和輸出處理器,并且用這種方式聲明輸入處理器是非常常見的。但是,還有一個地方可以指定要使用的輸入和輸出處理器:在 [Item Field](items.html#topics-items-fields) 元數據。下面是一個例子:
```py
import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.html import remove_tags
def 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(),
)
```
```py
>>> 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'}
```
輸入和輸出處理器的優先順序如下:
1. 項目加載器字段特定屬性: `field_in` 和 `field_out` (最優先)
2. 字段元數據( `input_processor` 和 `output_processor` 關鍵)
3. 項目加載器默認值: [`ItemLoader.default_input_processor()`](#scrapy.loader.ItemLoader.default_input_processor "scrapy.loader.ItemLoader.default_input_processor") 和 [`ItemLoader.default_output_processor()`](#scrapy.loader.ItemLoader.default_output_processor "scrapy.loader.ItemLoader.default_output_processor") (最低優先級)
參見: [重復使用和擴展項目加載器](#topics-loaders-extending) .
## 項目加載器上下文
項目加載器上下文是任意鍵/值的dict,在項目加載器中的所有輸入和輸出處理器之間共享。它可以在聲明、實例化或使用項加載器時傳遞。它們用于修改輸入/輸出處理器的行為。
例如,假設您有一個函數 `parse_length` 它接收文本值并從中提取長度:
```py
def parse_length(text, loader_context):
unit = loader_context.get('unit', 'm')
# ... length parsing code goes here ...
return parsed_length
```
接受一個 `loader_context` 參數該函數顯式地告訴項加載器它能夠接收項加載器上下文,因此項加載器在調用時傳遞當前活動的上下文和處理器函數( `parse_length` 在這種情況下)可以使用它們。
修改項目加載器上下文值有幾種方法:
1. 通過修改當前活動的項加載器上下文( [`context`](#scrapy.loader.ItemLoader.context "scrapy.loader.ItemLoader.context") 屬性):
```py
loader = ItemLoader(product)
loader.context['unit'] = 'cm'
```
2. 在項加載器實例化時(項加載器構造函數的關鍵字參數存儲在項加載器上下文中)::
```py
loader = ItemLoader(product, unit='cm')
```
3. 在項目加載器聲明中,用于那些支持用項目加載器上下文實例化它們的輸入/輸出處理器。 `MapCompose` 是其中之一:
```py
class ProductLoader(ItemLoader):
length_out = MapCompose(parse_length, unit='cm')
```
## 項加載器對象
```py
class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)
```
返回用于填充給定項的新項加載器。如果沒有給定任何項,則使用中的類自動實例化一個 [`default_item_class`](#scrapy.loader.ItemLoader.default_item_class "scrapy.loader.ItemLoader.default_item_class") .
當用 `selector` 或A `response` 參數: [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") 類提供了方便的機制,可以使用 [selectors](selectors.html#topics-selectors) .
| 參數: |
* **item** ([`Item`](items.html#scrapy.item.Item "scrapy.item.Item") object) -- 要使用后續調用填充的項實例 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") , [`add_css()`](#scrapy.loader.ItemLoader.add_css "scrapy.loader.ItemLoader.add_css") 或 [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") .
* **selector** ([`Selector`](selectors.html#scrapy.selector.Selector "scrapy.selector.Selector") object) -- 使用時要從中提取數據的選擇器 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") (RESP) [`add_css()`](#scrapy.loader.ItemLoader.add_css "scrapy.loader.ItemLoader.add_css") 或 [`replace_xpath()`](#scrapy.loader.ItemLoader.replace_xpath "scrapy.loader.ItemLoader.replace_xpath") (RESP) [`replace_css()`](#scrapy.loader.ItemLoader.replace_css "scrapy.loader.ItemLoader.replace_css") 方法。
* **response** ([`Response`](request-response.html#scrapy.http.Response "scrapy.http.Response") object) -- 用于使用 [`default_selector_class`](#scrapy.loader.ItemLoader.default_selector_class "scrapy.loader.ItemLoader.default_selector_class") ,除非給定了selector參數,否則在這種情況下,將忽略此參數。
|
| --- | --- |
item、selector、response和剩余的關鍵字參數被分配給加載程序上下文(可通過 [`context`](#scrapy.loader.ItemLoader.context "scrapy.loader.ItemLoader.context") 屬性)。
[`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") 實例具有以下方法:
```py
get_value(value, *processors, **kwargs)
```
處理給定的 `value` 根據給定的 `processors` 和關鍵字參數。
可用關鍵字參數:
| 參數: | **re** (_str_ _or_ _compiled regex_) -- 用于從給定值提取數據的正則表達式,使用 `extract_regex()` 方法,在處理器之前應用 |
| --- | --- |
實例:
```py
>>> from scrapy.loader.processors import TakeFirst
>>> loader.get_value(u'name: foo', TakeFirst(), unicode.upper, re='name: (.+)')
'FOO`
```
```py
add_value(field_name, value, *processors, **kwargs)
```
處理,然后添加給定的 `value` 對于給定字段。
首先傳遞值 [`get_value()`](#scrapy.loader.ItemLoader.get_value "scrapy.loader.ItemLoader.get_value") 通過給予 `processors` 和 `kwargs` ,然后通過 [field input processor](#topics-loaders-processors) 其結果附加到為該字段收集的數據中。如果字段已包含收集的數據,則添加新數據。
給定的 `field_name` 可以是 `None` ,在這種情況下,可以添加多個字段的值。處理后的值應該是字段名映射到值的dict。
實例:
```py
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'})
```
```py
replace_value(field_name, value, *processors, **kwargs)
```
類似 [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") 但將收集的數據替換為新值,而不是添加它。
```py
get_xpath(xpath, *processors, **kwargs)
```
類似 [`ItemLoader.get_value()`](#scrapy.loader.ItemLoader.get_value "scrapy.loader.ItemLoader.get_value") 但接收一個xpath而不是一個值,該值用于從與此關聯的選擇器中提取Unicode字符串列表。 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") .
| 參數: |
* **xpath** (_str_) -- 要從中提取數據的xpath
* **re** (_str_ _or_ _compiled regex_) -- 用于從選定的xpath區域提取數據的正則表達式
|
| --- | --- |
實例:
```py
# 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 (.*)')
```
```py
add_xpath(field_name, xpath, *processors, **kwargs)
```
類似 [`ItemLoader.add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") 但接收一個xpath而不是一個值,該值用于從與此關聯的選擇器中提取Unicode字符串列表。 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") .
見 [`get_xpath()`](#scrapy.loader.ItemLoader.get_xpath "scrapy.loader.ItemLoader.get_xpath") 對于 `kwargs` .
| 參數: | **xpath** (_str_) -- 要從中提取數據的xpath |
| --- | --- |
實例:
```py
# 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 (.*)')
```
```py
replace_xpath(field_name, xpath, *processors, **kwargs)
```
類似 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") 但替換收集的數據而不是添加它。
```py
get_css(css, *processors, **kwargs)
```
類似 [`ItemLoader.get_value()`](#scrapy.loader.ItemLoader.get_value "scrapy.loader.ItemLoader.get_value") 但接收CSS選擇器而不是值,該值用于從與此關聯的選擇器中提取Unicode字符串列表 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") .
| 參數: |
* **css** (_str_) -- 從中提取數據的CSS選擇器
* **re** (_str_ _or_ _compiled regex_) -- 用于從選定的CSS區域提取數據的正則表達式
|
| --- | --- |
實例:
```py
# 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 (.*)')
```
```py
add_css(field_name, css, *processors, **kwargs)
```
類似 [`ItemLoader.add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") 但接收CSS選擇器而不是值,該值用于從與此關聯的選擇器中提取Unicode字符串列表 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") .
見 [`get_css()`](#scrapy.loader.ItemLoader.get_css "scrapy.loader.ItemLoader.get_css") 對于 `kwargs` .
| 參數: | **css** (_str_) -- 從中提取數據的CSS選擇器 |
| --- | --- |
實例:
```py
# 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 (.*)')
```
```py
replace_css(field_name, css, *processors, **kwargs)
```
類似 [`add_css()`](#scrapy.loader.ItemLoader.add_css "scrapy.loader.ItemLoader.add_css") 但替換收集的數據而不是添加它。
```py
load_item()
```
用迄今收集的數據填充該項,然后返回它。收集的數據首先通過 [output processors](#topics-loaders-processors) 獲取要分配給每個項字段的最終值。
```py
nested_xpath(xpath)
```
使用xpath選擇器創建嵌套加載程序。提供的選擇器將相對于與此關聯的選擇器應用 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") . 嵌套加載程序共享 `Item` 與父母 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") 所以呼吁 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") , [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") , [`replace_value()`](#scrapy.loader.ItemLoader.replace_value "scrapy.loader.ItemLoader.replace_value") 等將按預期工作。
```py
nested_css(css)
```
使用CSS選擇器創建嵌套加載程序。提供的選擇器將相對于與此關聯的選擇器應用 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") . 嵌套加載程序共享 `Item` 與父母 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") 所以呼吁 [`add_xpath()`](#scrapy.loader.ItemLoader.add_xpath "scrapy.loader.ItemLoader.add_xpath") , [`add_value()`](#scrapy.loader.ItemLoader.add_value "scrapy.loader.ItemLoader.add_value") , [`replace_value()`](#scrapy.loader.ItemLoader.replace_value "scrapy.loader.ItemLoader.replace_value") 等將按預期工作。
```py
get_collected_values(field_name)
```
返回為給定字段收集的值。
```py
get_output_value(field_name)
```
返回使用輸出處理器為給定字段解析的收集值。此方法根本不填充或修改項。
```py
get_input_processor(field_name)
```
返回給定字段的輸入處理器。
```py
get_output_processor(field_name)
```
返回給定字段的輸出處理器。
[`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") 實例具有以下屬性:
```py
item
```
這個 [`Item`](items.html#scrapy.item.Item "scrapy.item.Item") 此項加載器正在分析的對象。
```py
context
```
當前活動的 [Context](#topics-loaders-context) 此項目加載器的。
```py
default_item_class
```
項類(或工廠),用于在構造函數中未給定項時實例化項。
```py
default_input_processor
```
用于那些未指定字段的默認輸入處理器。
```py
default_output_processor
```
用于未指定字段的默認輸出處理器。
```py
default_selector_class
```
用于構造 [`selector`](#scrapy.loader.ItemLoader.selector "scrapy.loader.ItemLoader.selector") 其中 [`ItemLoader`](#scrapy.loader.ItemLoader "scrapy.loader.ItemLoader") ,如果構造函數中只給出響應。如果在構造函數中給定了選擇器,則忽略此屬性。此屬性有時在子類中被重寫。
```py
selector
```
這個 [`Selector`](selectors.html#scrapy.selector.Selector "scrapy.selector.Selector") 從中提取數據的對象。它要么是構造函數中給定的選擇器,要么是使用 [`default_selector_class`](#scrapy.loader.ItemLoader.default_selector_class "scrapy.loader.ItemLoader.default_selector_class") . 此屬性是只讀的。
## 嵌套裝載機
從文檔的子部分分析相關值時,創建嵌套加載器可能很有用。假設您正在從一個頁面的頁腳提取細節,該頁面的外觀如下:
例子::
```py
<footer>
<a class="social" href="https://facebook.com/whatever">Like Us</a>
<a class="social" href="https://twitter.com/whatever">Follow Us</a>
<a class="email" href="mailto:whatever@example.com">Email Us</a>
</footer>
```
如果沒有嵌套加載程序,則需要為要提取的每個值指定完整的xpath(或css)。
例子::
```py
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()
```
相反,您可以使用頁腳選擇器創建嵌套加載程序,并添加相對于頁腳的值。功能相同,但避免重復頁腳選擇器。
例子::
```py
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選擇器。作為一般準則,當嵌套加載器使您的代碼更簡單,但不要過度嵌套,否則您的解析器可能會變得難以讀取。
## 重復使用和擴展項目加載器
隨著項目規模的擴大和 Spider 數量的增加,維護成為一個基本問題,特別是當您必須為每個 Spider 處理許多不同的解析規則時,有許多異常,但也希望重用公共處理器。
項目加載器旨在減輕解析規則的維護負擔,而不會失去靈活性,同時為擴展和重寫規則提供了方便的機制。因此,項目加載器支持傳統的Python類繼承來處理特定spider(或spider組)的差異。
例如,假設某個特定站點用三個破折號(例如 `---Plasma TV---` )你不想最后在最終的產品名稱中刪除這些破折號。
下面介紹如何通過重用和擴展默認的產品項加載器來刪除這些破折號。( `ProductLoader` ):
```py
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版本中,您可能希望刪除 `CDATA` 發生。下面是一個如何操作的示例:
```py
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)
```
這就是您通常如何擴展輸入處理器的方法。
對于輸出處理器,在字段元數據中聲明它們更為常見,因為它們通常只依賴于字段,而不依賴于每個特定的站點解析規則(與輸入處理器一樣)。參見: [聲明輸入和輸出處理器](#topics-loaders-processors-declaring) .
有許多其他可能的方法來擴展、繼承和重寫項加載器,不同的項加載器層次結構可能更適合不同的項目。Scrapy只提供了這種機制;它不會強制任何特定的裝載機集合組織——這取決于您和項目的需要。
## 可用的內置處理器
盡管您可以使用任何可調用函數作為輸入和輸出處理器,但Scrapy提供了一些常用的處理器,如下所述。其中一些,比如 [`MapCompose`](#scrapy.loader.processors.MapCompose "scrapy.loader.processors.MapCompose") (通常用作輸入處理器)組成按順序執行的多個函數的輸出,以生成最終的解析值。
以下是所有內置處理器的列表:
```py
class scrapy.loader.processors.Identity
```
最簡單的處理器,它什么都不做。它返回原始值不變。它不接收任何構造函數參數,也不接受加載程序上下文。
例子::
```py
>>> from scrapy.loader.processors import Identity
>>> proc = Identity()
>>> proc(['one', 'two', 'three'])
['one', 'two', 'three']
```
```py
class scrapy.loader.processors.TakeFirst
```
從接收的值返回第一個非空/非空值,因此它通常用作單值字段的輸出處理器。它不接收任何構造函數參數,也不接受加載程序上下文。
例子::
```py
>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'one', 'two', 'three'])
'one'
```
```py
class scrapy.loader.processors.Join(separator=u' ')
```
返回與構造函數中給定的分隔符聯接的值,默認為 `u' '` . 它不接受加載器上下文。
使用默認分隔符時,此處理器相當于以下函數: `u' '.join`
實例:
```py
>>> from scrapy.loader.processors import Join
>>> proc = Join()
>>> proc(['one', 'two', 'three'])
'one two three'
>>> proc = Join('<br>')
>>> proc(['one', 'two', 'three'])
'one<br>two<br>three'
```
```py
class scrapy.loader.processors.Compose(*functions, **default_loader_context)
```
一種由給定函數組成的處理器。這意味著該處理器的每個輸入值都傳遞給第一個函數,該函數的結果傳遞給第二個函數,依此類推,直到最后一個函數返回該處理器的輸出值為止。
默認情況下,停止進程 `None` 價值。可以通過傳遞關鍵字參數來更改此行為 `stop_on_none=False` .
例子::
```py
>>> from scrapy.loader.processors import Compose
>>> proc = Compose(lambda v: v[0], str.upper)
>>> proc(['hello', 'world'])
'HELLO'
```
每個函數可以選擇接收 `loader_context` 參數。對于那些執行此操作的處理器,此處理器將通過當前活動的 [Loader context](#topics-loaders-context) 通過那個參數。
構造函數中傳遞的關鍵字參數用作傳遞給每個函數調用的默認加載程序上下文值。但是,傳遞給函數的最終加載器上下文值將被通過 `ItemLoader.context()` 屬性。
```py
class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)
```
一種由給定函數組成的處理器,類似于 [`Compose`](#scrapy.loader.processors.Compose "scrapy.loader.processors.Compose") 處理器。與此處理器不同的是內部結果在函數之間傳遞的方式,如下所示:
此處理器的輸入值為 _iterated_ 第一個函數應用于每個元素。這些函數調用的結果(每個元素一個)被連接起來,以構造一個新的iterable,然后使用它來應用第二個函數,依此類推,直到最后一個函數應用到迄今為止收集的值列表的每個值為止。最后一個函數的輸出值被連接在一起以產生這個處理器的輸出。
每個特定函數都可以返回一個值或一個值列表,該值列表將被應用于其他輸入值的同一函數返回的值列表壓平。函數也可以返回 `None` 在這種情況下,該函數的輸出將被忽略,以便在鏈上進行進一步的處理。
該處理器提供了一種方便的方法來組合只使用單個值(而不是iterables)的函數。因此, [`MapCompose`](#scrapy.loader.processors.MapCompose "scrapy.loader.processors.MapCompose") 處理器通常用作輸入處理器,因為通常使用 `extract()` 方法 [selectors](selectors.html#topics-selectors) ,返回Unicode字符串列表。
下面的例子應該說明它是如何工作的:
```py
>>> def filter_world(x):
... return None if x == 'world' else x
...
>>> from scrapy.loader.processors import MapCompose
>>> proc = MapCompose(filter_world, str.upper)
>>> proc(['hello', 'world', 'this', 'is', 'scrapy'])
['HELLO, 'THIS', 'IS', 'SCRAPY']
```
與組合處理器一樣,函數可以接收加載器上下文,構造函數關鍵字參數用作默認上下文值。見 [`Compose`](#scrapy.loader.processors.Compose "scrapy.loader.processors.Compose") 處理器獲取更多信息。
```py
class scrapy.loader.processors.SelectJmes(json_path)
```
Queries the value using the json path provided to the constructor and returns the output. Requires jmespath ([https://github.com/jmespath/jmespath.py](https://github.com/jmespath/jmespath.py)) to run. This processor takes only one input at a time.
例子::
```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::
```py
>>> import json
>>> proc_single_json_str = Compose(json.loads, SelectJmes("foo"))
>>> proc_single_json_str('{"foo": "bar"}')
'bar'
>>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('foo')))
>>> proc_json_list('[{"foo":"bar"}, {"baz":"tar"}]')
['bar']
```
- 簡介
- 第一步
- Scrapy at a glance
- 安裝指南
- Scrapy 教程
- 實例
- 基本概念
- 命令行工具
- Spider
- 選擇器
- 項目
- 項目加載器
- Scrapy shell
- 項目管道
- Feed 導出
- 請求和響應
- 鏈接提取器
- 設置
- 例外情況
- 內置服務
- Logging
- 統計數據集合
- 發送電子郵件
- 遠程登錄控制臺
- Web服務
- 解決具體問題
- 常見問題
- 調試spiders
- Spider 合約
- 常用做法
- 通用爬蟲
- 使用瀏覽器的開發人員工具進行抓取
- 調試內存泄漏
- 下載和處理文件和圖像
- 部署 Spider
- AutoThrottle 擴展
- Benchmarking
- 作業:暫停和恢復爬行
- 延伸 Scrapy
- 體系結構概述
- 下載器中間件
- Spider 中間件
- 擴展
- 核心API
- 信號
- 條目導出器
- 其余所有
- 發行說明
- 為 Scrapy 貢獻
- 版本控制和API穩定性