【问题标题】:correct way to nest Item data in scrapy在scrapy中嵌套Item数据的正确方法
【发布时间】:2014-09-25 13:01:36
【问题描述】:

嵌套Item数据的正确方法是什么?

例如,我想要一个产品的输出:

{
'price': price,
'title': title,
'meta': {
    'url': url,
    'added_on': added_on
}

我有scrapy.Item of:

class ProductItem(scrapy.Item):
    url = scrapy.Field(output_processor=TakeFirst())
    price = scrapy.Field(output_processor=TakeFirst())
    title = scrapy.Field(output_processor=TakeFirst())
    url = scrapy.Field(output_processor=TakeFirst())
    added_on = scrapy.Field(output_processor=TakeFirst())

现在,我的做法是根据新项目模板重新格式化管道中的整个项目:

class FormatedItem(scrapy.Item):
    title = scrapy.Field()
    price = scrapy.Field()
    meta = scrapy.Field()

正在筹备中:

def process_item(self, item, spider):
    formated_item = FormatedItem()
    formated_item['title'] = item['title']
    formated_item['price'] = item['price']
    formated_item['meta'] = {
        'url': item['url'],
        'added_on': item['added_on']
    }
    return formated_item

这是解决这个问题的正确方法,还是有更直接的方法来解决这个问题而不破坏框架的理念?

【问题讨论】:

    标签: scrapy


    【解决方案1】:

    UPDATE 来自 cmets:看起来 nested loaders 是更新的方法。另一种评论表明这种方法会在序列化过程中导致错误。

    解决此问题的最佳方法是创建 mainmeta 项目类/加载器。

    from scrapy.item import Item, Field
    from scrapy.contrib.loader import ItemLoader
    from scrapy.contrib.loader.processor import TakeFirst
    
    
    class MetaItem(Item):
        url = Field()
        added_on = Field()
    
    
    class MainItem(Item):
        price = Field()
        title = Field()
        meta = Field(serializer=MetaItem)
    
    
    class MainItemLoader(ItemLoader):
        default_item_class = MainItem
        default_output_processor = TakeFirst()
    
    
    class MetaItemLoader(ItemLoader):
        default_item_class = MetaItem
        default_output_processor = TakeFirst()
    

    示例用法:

    from scrapy.spider import Spider
    from qwerty.items import  MainItemLoader, MetaItemLoader
    from scrapy.selector import Selector
    
    
    class DmozSpider(Spider):
        name = "dmoz"
        allowed_domains = ["example.com"]
        start_urls = ["http://example.com"]
    
        def parse(self, response):
            mainloader = MainItemLoader(selector=Selector(response))
            mainloader.add_value('title', 'test')
            mainloader.add_value('price', 'price')
            mainloader.add_value('meta', self.get_meta(response))
            return mainloader.load_item()
    
        def get_meta(self, response):
            metaloader = MetaItemLoader(selector=Selector(response))
            metaloader.add_value('url', response.url)
            metaloader.add_value('added_on', 'now')
            return metaloader.load_item()
    

    之后,您可以通过创建更多“子项”来轻松扩展您的项。

    【讨论】:

    • 哦,这是我最初的想法,但我对返回类型有点困惑,现在一切都清楚了。谢谢!
    • 我已经尝试过了,它似乎无法返回有效结果。似乎当调用 get_meta 时,解析似乎只接受 metaloader 的键,所以结果是 meta{'url':} 这真的很奇怪。
    • 您的意思是 item['meta'] 的计算结果为 {'url':<2nd field, i.e. 'added_on'>}
    • 此方法不再有效,并在序列化项目时产生错误。我发现将字段定义为scrapy.Field() 并在使用子项实例填充它之前将其初始化为列表效果很好。
    • 看起来嵌套加载器是更新的方法。 doc.scrapy.org/en/latest/topics/loaders.html#nested-loaders
    【解决方案2】:

    我认为在蜘蛛中构建字典会更直接。这里有两种不同的方法,都可以达到相同的结果。这里唯一可能的破坏因素是处理器应用于 item['meta'] 字段,而不是 item['meta']['add_on'] 和 item['meta']['url'] 字段。

    def parse(self, response):
        item = MyItem()
        item['meta'] = {'added_on': response.css("a::text").extract()[0]}
        item['meta']['url'] = response.xpath("//a/@href").extract()[0]
        return item
    

    您是否有特定原因要以这种方式构造它而不是解压缩元字段?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-14
      • 1970-01-01
      • 2017-01-29
      • 1970-01-01
      • 2021-09-03
      • 2022-07-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多