返回首页 Scrapy 中文指南

Item Loaders

Item Loaders 提供了一种便捷的方式填充抓取到的:Items。虽然 Items 可以使用自带的类字典形式 API 填充,但是 Items Loaders 提供了更便捷的 API,可以分析原始数据并对 Item 进行赋值。

从另一方面来说,Items 提供保存抓取数据的容器,而 Item Loaders 提供的是 填充 容器的机制。

Item Loaders 提供的是一种灵活,高效的机制,可以更方便的被 spider 或 source format (HTML,XML,etc)扩展,并 override 更易于维护的、不同的内容分析规则。

Using Item Loaders to populate items

要使用 Item Loader, 你必须先将它实例化。你可以使用类似字典的对象(例如: Item or dict)来进行实例化,或者不使用对象也可以,当不用对象进行实例化的时候,Item 会自动使用 ItemLoader.default\_item_class 属性中指定的 Item 类在 Item Loader constructor 中实例化。

然后,你开始收集数值到 Item Loader 时,通常使用 Selectors。你可以在同一个 item field 里面添加多个数值;Item Loader 将知道如何用合适的处理函数来“添加”这些数值。

下面是在 Spider 中典型的 Item Loader 的用法,使用 Items chapter声明的 Product item:

from scrapy.contrib.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()的方法,把从两个不同的 XPath 位置提取的数据收集起来. 这是将在以后分配给 name 字段中的数据。

之后,类似的请求被用于 price 和 stock 字段 (后者使用 CSS selector 和 add_css() 方法), 最后使用不同的方法 add_value()last_update 填充文本值(today)。

最终,当所有数据被收集起来之后, 调用 ItemLoader.load_item() 方法,实际上填充并且返回了之前通过调用 add_xpath(),add_css(),and add_value()所提取和收集到的数据的 Item。

Input and Output processors

Item Loader 在每个(Item)字段中都包含了一个输入处理器和一个输出处理器。 输入处理器收到数据时立刻提取数据 (通过 add_xpath(),add_css()或者 add_value()方法)之后输入处理器的结果被收集起来并且保存在 ItemLoader 内. 收集到所有的数据后, 调用 ItemLoader.load_item()方法来填充,并得到填充后的 Item 对象。这是当输出处理器被和之前收集到的数据(和用输入处理器处理的)被调用.输出处理器的结果是被分配到 Item 的最终值。

让我们看一个例子来说明如何输入和输出处理器被一个特定的字段调用(同样适用于其他 field)::

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 字段。输入处理器的结果被收集和保存在 Item Loader 中(但尚未分配给该 Item)。
  2. 从 xpath2 提取出来的数据,传递给(1)中使用的相同的 输入处理器。输入处理器的结果被附加到在(1)中收集的数据(如果有的话)。
  3. 和之前相似,只不过这里的数据是通过 css CSS selector 抽取,之后传输到在(1)和(2)使用 的 input processor 中。最终输入处理器的结果被附加到在(1)和(2)中收集的数据之后 (如果存在数据的话)。
  4. 这里的处理方式也和之前相似,但是此处的值是通过 add_value 直接赋予的, 而不是利用 XPath 表达式或 CSS selector 获取。得到的值仍然是被传送到输入处理器。 在这里例程中,因为得到的值并非可迭代,所以在传输到输入处理器之前需要将其 转化为可迭代的单个元素,这才是它所接受的形式。
  5. 在之前步骤中所收集到的数据被传送到 output processor 的 name field 中。输出处理器的结果就是赋到 item 中 name field 的值。

需要注意的是,输入和输出处理器都是可调用对象,调用时传入需要被分析的数据, 处理后返回分析得到的值。因此你可以使用任意函数作为输入、输出处理器。唯一需注意的是它们必须接收一个(并且只是一个)迭代器性质的 positional 参数。