回调之间的项目加载器故障排除

时间:2016-10-17 16:20:52

标签: python csv scrapy web-crawler

为了理解"天真的方法" http://oliverguenther.de/2014/08/almost-asynchronous-requests-for-single-item-processing-in-scrapy/

中的示例

我正在尝试复制该代码。这个想法是填充一个单独的项目,其中每个字段来自不同的网站。

我试图理解为什么在运行它时从下面的代码中获得以下行为,并使用scrapy crawl compSpider -o prices.csv将结果导出到csv文件中。

代码实际上使用相关价格填充nic_price,但它对tester_price没有做同样的事情。

我相信它应该这样做,因为项目加载器对象在请求元字段中从第一个回调[firstRequest]传递到第二个回调[parseDescription1],其中项目加载器对象被创建对象最终被加载到一个项目中。

我测试过css选择器的工作原理。有人可以帮我理解为什么我会这样做?

项目声明

import scrapy
class ProductItem(scrapy.Item):       
    nic_price = scrapy.Field()
    tester_price = scrapy.Field()

蜘蛛代码

import scrapy
from scrapy.http import Request
from scrapy.loader import ItemLoader
from comparator.items import ProductItem


class Compspider(scrapy.Spider):
    name = "compSpider"


    #start_urls = (  'https://www.shop.niceic.com/',   )

    def start_requests(self):  

        yield Request(
            'https://www.shop.niceic.com/6559-megger-mft1711-multifunction-tester-1008-121',  callback=self.firstRequest)


    def firstRequest(self, response):
        l = ItemLoader(item=ProductItem(), response=response)
        l.add_css('nic_price', 'div.product-info p.product-price span[itemprop="price"]::text')

        yield Request('https://www.tester.co.uk/test-safe-pen-co-meter', meta={'loader' : l}, callback= self.parseDescription1)

    def parseDescription1(self, response):

        # Recover item(loader)
        l = response.meta['loader']

        # Use just as before
        l.add_css('tester_price', 'div.price-breakdown div.price-excluding-tax span.price::text')

        yield l.load_item()

2 个答案:

答案 0 :(得分:2)

最近,我遇到了同样的问题。尝试使用这两种方法,但每个方法都有很大的缺点:

  1. 传递项目加载器并使用add_value()引入了双重处理问题(通过输入和输出处理器),这使得设计那些使代码难以阅读和维护的处理器变得更加困难。 / p>

  2. 传递项目而不是项目加载器只会杀死ItemLoaders的整体想法,并在整个代码中引入不一致。

  3. 我发现通过scrapy的scrapy.loader.ItemLoader源代码(非常小而简单的代码,btw)是parent关键字参数,它允许您传递父ItemLoader并复制其所有值到新的ItemLoader。

    以下是修改后的parseDescription1

    def parseDescription1(self, response):
    
        # Recover item(loader)
        loader = response.meta['loader']
    
        if response.body:
            loader = ItemLoader(item=ProductItem(), response=response, parent=loader)
    
            # Use just as before
            tester_loader.add_css('tester_price', 'div.price-breakdown div.price-excluding-tax span.price::text')
    
        yield loader.load_item()
    

    scrapy crawl结果:

    2016-11-01 10:48:50 [scrapy] DEBUG: Crawled (200) <GET https://www.shop.niceic.com/robots.txt> (referer: None)
    2016-11-01 10:48:50 [scrapy] DEBUG: Crawled (200) <GET https://www.shop.niceic.com/6559-megger-mft1711-multifunction-tester-1008-121> (referer: None)
    2016-11-01 10:48:50 [scrapy] DEBUG: Crawled (200) <GET https://www.tester.co.uk/robots.txt> (referer: None)
    2016-11-01 10:48:51 [scrapy] DEBUG: Crawled (200) <GET https://www.tester.co.uk/test-safe-pen-co-meter> (referer: https://www.shop.niceic.com/6559-megger-mft1711-multifunction-tester-1008-121)
    2016-11-01 10:48:51 [scrapy] DEBUG: Scraped from <200 https://www.tester.co.uk/test-safe-pen-co-meter>
    {'nic_price': [u'\xa3509.00'],
     'tester_price': [u'\n\t\t            \xa349.00            \n\t\t        ']}
    

答案 1 :(得分:0)

css选择器绝对有效。问题在于你在meta dict中保存的itemloader变量 l ,它指的是来自 firstRequest 回调的响应,而不是 parseDescription1 回调,其中此css选择器可以工作

'div.price-breakdown div.price-excluded-tax span.price :: text'

要解决这个问题,只需创建一个新的项目加载器 parseDescription1 ,或者更好地加载项目本身而不是元​​数据中的加载器,如下所示......

    def firstRequest(self, response):

        l = ItemLoader(item=ProductItem(), response=response)

        l.add_css('nic_price', 'div.product-info p.product-price span[itemprop="price"]::text')

        yield Request('https://www.tester.co.uk/test-safe-pen-co-meter',
                  meta={'item': l.load_item()},
                  callback=self.parseDescription1)

    def parseDescription1(self, response):

        # Recover item
        item = response.meta['item']

        l = ItemLoader(item=ProductItem(), response=response)

        # Use just as before
        l.add_css('tester_price', 'div.price-breakdown div.price-excluding-tax span.price::text')

        l.add_value('nic_price', item['nic_price'])

        yield l.load_item()