使用ItemLoader在多个响应中加载Item档案

时间:2019-01-09 02:29:30

标签: python scrapy

这是对问题Scrapy: populate items with item loaders over multiple pages的公认答案的后续问题。我想使用ItemLoader来收集来自多个请求到单个Item的值。接受的答案表明,已加载的Item.load_item()应该通过meta中的request字段传递给下一个请求。

但是,在爬网结束时返回加载的对象时,我想将output_processors应用于单个字段的所有收集值。

问题

  1. 实现它的最佳方法是什么?
  2. 我可以在不加载的情况下将ItemLoader上的meta实例传递给下一个request,然后仅替换{{中的selectorresponse元素1}}在添加下一个响应的值或xpath时?

示例:

ItemLoader

忽略实际网站的上下文。

1 个答案:

答案 0 :(得分:2)

是的,您可以仅传递ItemLoader实例。

如果很久以前我从irc或github聊天中正确地记得了这一点,那么这样做可能会存在一些潜在的问题,例如内存使用量增加或引用处理泄漏,因为您随身携带了对象引用通过将这些Itemloader实例绑定到那些请求,可以并可能需要很长时间(取决于下载队列的顺序)。 因此请记住这一点,也许要提防在大型爬网中使用此样式,或者进行一些内存调试以确保确定性。

但是,我过去曾广泛使用此方法(并且在使用ItemLoaders时仍会使用此方法),而我自己尚未发现该方法有任何问题。

这是我的操作方式:

import scrapy
from myproject.loader import ItemLoader

class TheLoader(ItemLoader):
    pass

class SomeSpider(scrapy.Spider):
    [...]

    def parse(self, response):
        loader = TheLoader(item=TestItems(), response=response)
        loader.add_xpath('title1', '//*[@id="firstHeading"]/text()')
        request = Request("https://en.wikipedia.org/wiki/2016_Rugby_Championship",
            callback=self.parsePage1,
            dont_filter=True
        )
        request.meta['loader'] = loader
        yield request

    def parsePage1(self, response):
        loader = response.meta['loader']
        # rebind ItemLoader to new Selector instance
        #loader.reset(selector=response.selector, response=response)
        # skipping the selector will default to response.selector, like ItemLoader
        loader.reset(response=response)
        loader.add_xpath('title1', '//*[@id="firstHeading"]/text()')
        return loader.load_item()

这需要使用自定义的ItemLoader类,可以在my scrapy scrapyard中找到该类, 但课程的相关部分在这里:

from scrapy.loader import ItemLoader as ScrapyItemLoader

class ItemLoader(ScrapyItemLoader):
    """ Extended Loader
        for Selector resetting.
        """

    def reset(self, selector=None, response=None):
        if response is not None:
            if selector is None:
                selector = self.default_selector_class(response)
            self.selector = selector
            self.context.update(selector=selector, response=response)
        elif selector is not None:
            self.selector = selector
            self.context.update(selector=selector)