关于Scrapy ItemLoader、MapCompose、Compose、input_processor与output_processor的一些理解

本文以一个当当网图书出版社信息举例,说明Scrapy中,ItemLoader、MapCompose、Compose、input_processor与output_processor的一些使用事项。

先给出spider与item的代码实例:

spider:

def parse_item(self, response):
    for r in response.css(".bang_list li"):
        loader = DangdangItemLoader(DangdangItem(), selector=r)
        loader.add_css("publisher", ".publisher_info a::text")
        item = loader.load_item()
        yield item

item:

class DangdangItemLoader(ItemLoader):
    default_output_processor = TakeFirst()
class DangdangItem(scrapy.Item):
    publisher = scrapy.Field(
        input_processor=Compose(
            lambda x: x[-1]
        )
    )

1. 关于Item Loader与input_processor/output_processor

Scrapy中Item Loader为每个Item Field单独提供了一个Input processor和一个Output processor,spider中的 add_xpath()add_css()add_value()方法接收到的数据都会先进入input_processor,执行以后所得到清洗后的数据将仍然保存在 ItemLoader 实例中;当数据收集完成以后,ItemLoader 通过 load_item()方法来进行填充并返回已填充的 Item 实例。

我曾未清晰理解上述概念,而遇到了MapCompose不起作用,且程序不进入input_processor方法内部的问题。直到看了源码后才明白:

class ItemLoader(object):
    default_input_processor = Identity()
    default_output_processor = Identity()
    default_selector_class = Selector
    ...
    
    def get_input_processor(self, field_name):
        proc = getattr(self, '%s_in' % field_name, None)
        if not proc:
            proc = self._get_item_field_attr(field_name, 'input_processor', \
                self.default_input_processor)
        return proc
	def get_output_processor(self, field_name):
        proc = getattr(self, '%s_out' % field_name, None)
        if not proc:
            proc = self._get_item_field_attr(field_name, 'output_processor', \
                self.default_output_processor)
        return proc

input_processor与output_processor两个函数都必须在item_loader实例中!这里是一个错误案例:Why my input_processor in Scrapy not work?

我们一般定义一个ItemLoader的形式:

loader = DangdangItemLoader(item=DangdangItem(), response=response)

这仅是我们需要对response进行数据处理的情况。在我开头给出的例子中,实例化item_loader的第二个参数是一个Selector,这是因为要处理的数据已不再来自于response,而来自于scrapy.selector.unified.Selector对象。


2.关于input_processor与output_processor作用顺序

在我开头给出的例子中,Item类有一个input_processor:

	publisher = scrapy.Field(
        input_processor=Compose(
            lambda x: x[-1]
        )
    )

这里的input_processor是当函数parse_item通过add_css方法获取到原始数据:: ['卡勒德·胡赛尼', 'Khaled', 'Hosseini', '李继宏', '上海人民出版社']后开始执行的。如果这里再声明一个output_processor,则output_processor中的内容会当函数parse_item执行到item_loader方法时再执行。

在我给出的例子中,ItemLoader类有一个output_processor:

default_output_processor = TakeFirst()

因为是output_processor,所以是当函数parse_item执行到item_loader方法时,才会执行TakeFirst()。TakeFirst()的作用就不多解释了,就是将item中的全部列表变为值。


3. 关于MapCompose与Compose

在这个例子中,原始的publisher数据是:: ['卡勒德·胡赛尼', 'Khaled', 'Hosseini', '李继宏', '上海人民出版社']

	publisher = scrapy.Field(
        input_processor=Compose(
            lambda x: x[-1]
        )
    )

Compose方法会对原始publisher数据整体去执行下面的lambda,即保留最后一个元素"上海人民出版社"。

而如果是MapCompose,则会对原始publisher数据的每一个元素执行一遍下面的lambda,即仍会返回一个list:[‘尼’,'d','i','宏','社']。如果再经过上述的load_item中的TakeFirst(),最终的结果会是'尼'

你可能感兴趣的:(爬虫,scrapy)