2017-02-20 259 views
0

我认为我的问题是由一些对某些scrapy概念的误解造成的。所以我会很感激,如果有人能够向我解释这一点。ItemLoader为什么从一个元素创建一个列表

让我们想象一下,我有以下蜘蛛:

# -*- coding: utf-8 -*- 
import scrapy 
from scrapy.loader import ItemLoader 
from reddit.items import RedditItem 
from scrapy.loader.processors import TakeFirst 


class RedditspiderSpider(scrapy.Spider): 
    name = "redditspider" 
    allowed_domains = ["wwww.reddit.com"] 
    start_urls = [ 
     'https://www.reddit.com/r/starcraft/comments/5t8v7i/community_feedback_update_widow_mines_carriers/?st=iz7ba37h&sh=b7e9bd35'] 

    def parse(self, response): 
     l = ItemLoader(item=RedditItem(), response=response) 

     comments = response.xpath(
      '//div[contains(@class,"usertext-body may-blank-within md-container ")]/div') 
     comments = comments[1:] 
     for comment_it in comments: 
      comment = comment_it.extract() 
      l.add_value('comment', comment) 
      yield l.load_item() 

RedditItem通过以下方式定义:

from scrapy.item import Item, Field 
from w3lib.html import remove_tags 
from scrapy.loader.processors import TakeFirst 


def _remove_tags(input): 
    return input 


class RedditItem(Item): 
    comment = Field(input_processor=_remove_tags, output_processor=TakeFirst()) 

所以一切都很简单明了。现在,我的问题。在第一个代码示例l加载程序有一个字段comments。据我所知,当我做l.add_value('comment', comment),这个字段的输入处理器被触发。 这是正确的吗?

据我所知,这是正确的。在l.add_value('comment', comment)comment是一个字符串,而不是一个列表。但是,当我在_remove_tags中设置断点时,我发现input实际上是长度为1的列表。所以我的主要问题是为什么会发生?为什么我传递一个字符串并在那里看到一个列表?

我看着scrapy源代码,发现这是_add_value(线90):value = arg_to_iter(value)。这让事情变得非常清楚,这似乎是我看到长度为1的列表的原因。

这条线背后的设计推理是什么?这是因为在scrapy我可以从不同的xpath/css请求填充项目中的相同字段?如果是这样,那对我来说很有意义。那么问题是:我该如何解决这个问题?我只是在输入处理器中应用map(_remove_tags, input)?这会是一个推荐的解决方案吗?或者我错了?

感谢

+0

你确定'comment = comment_it.extract()'是一个字符串吗? – eLRuLL

+0

@eLRuLL是的。在add_value类型(注释)---> Out [2]:unicode附近设置一个断点。似乎是Unicode字符串 –

回答

1

是,scrapy装载机设计用于处理在同一领域,你可以测试多次插入:现在

from scrapy.loader.processors import Identity 

class RedditItem(Item): 
    comment = Field(input_processor=Identity(), output_processor=Identity()) 

l = ItemLoader(item=RedditItem(), response=response) 
l.add_value('comment', 'first comment') 
l.add_value('comment', 'second comment') 

print l.output_value('comment') # ['first comment', 'second comment'] 

,正因为如此,你可以检查here什么推荐的处理器用于scrapy程序员使用的输入和输出(在default_output_processordefault_input_processor之内)。

正如你所看到的,他们有一个称为MapCompose的处理器,它接收当前值的每个条目并应用每个定义为参数的方法。

+0

因此,一个很好的解决方法是在input_processor中执行map(_remove_tags,input),然后使用TakeFirst作为输出处理器?我正确地得到这个吗? –

+0

'MapCompose(_remove_tags)' – eLRuLL

+0

@sof_dff是的,你可以看到一个[示例](https://doc.scrapy.org/en/latest/topics/loaders.html?highlight=remove_tags#declaring-input-and-输出处理器),只是在文档中做到这一点,但请注意下划线。 – guival

相关问题