【问题标题】:Pipeline.py to drop Value rather than FieldPipeline.py 删除值而不是字段
【发布时间】:2015-03-30 09:40:49
【问题描述】:

我目前正在编写一个 Scrapy 脚本来从亚马逊页面提取产品信息。我遇到的问题是异常处理,它只删除错误字段而不是输出中的整个项目/行。

当前蜘蛛:

from scrapy.spider import Spider
from scrapy.selector import Selector

from dirbot.items import Website


class DmozSpider(Spider):
    name = "dmoz"
    allowed_domains = ["amazon.co.uk"]
    start_urls = [
        "http://www.amazon.co.uk/dp/B004YVOU9S",
        "http://www.amazon.co.uk/dp/B009NFE2QQ"
    ]

    def parse(self, response):

        sel = Selector(response)
        sites = sel.xpath('//div[contains(@class, "a-container")]')
        items = []

            for site in sites:
                item = Website()
                item['asin'] = response.url.split('/')[-1]
                item['title'] = site.xpath('div[@id="centerCol"]/div[@id="title_feature_div"]/div[@id="titleSection"]/h1[@id="title"]/span[@id="productTitle"]/text()').extract()
                item['description'] = site.xpath('//*[@id="productDescription"]/div/div[1]/text()').extract()[0].strip()
                item['price'] = site.xpath('//*[@id="priceblock_ourprice"]/text()').extract()
                item['image'] = site.xpath('//*[@id="landingImage"]/@data-a-dynamic-image').extract()
                item['brand'] = site.xpath('//*[@id="brand"]/text()').extract()
                item['bullets'] = site.xpath('//*[@id="feature-bullets"]/span/ul').extract()[0].strip()
                item['category'] = site.xpath('//*[@id="wayfinding-breadcrumbs_feature_div"]/ul').extract()[0].strip()
                item['details'] = site.xpath('//*[@id="prodDetails"]/div/div[1]/div/div/div[2]/div/div/table').extract()[0].strip()
                items.append(item)

            return items

当抓取结果缺少任何字段时,我目前收到错误:

exceptions.IndexError: list index out of range

为了解决这个问题,我以 IgnoreRequest 的形式添加了一些异常处理。

from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.exceptions import IgnoreRequest

from dirbot.items import Website


class DmozSpider(Spider):
    name = "dmoz"
    allowed_domains = ["amazon.co.uk"]
    start_urls = [
        "http://www.amazon.co.uk/dp/B004YVOU9S",
        "http://www.amazon.co.uk/dp/B009NFE2QQ"
    ]

    def parse(self, response):

        sel = Selector(response)
        sites = sel.xpath('//div[contains(@class, "a-container")]')
        items = []

        try:
            for site in sites:
                item = Website()
                item['asin'] = response.url.split('/')[-1]
                item['title'] = site.xpath('div[@id="centerCol"]/div[@id="title_feature_div"]/div[@id="titleSection"]/h1[@id="title"]/span[@id="productTitle"]/text()').extract()
                item['description'] = site.xpath('//*[@id="productDescription"]/div/div[1]/text()').extract()[0].strip()
                item['price'] = site.xpath('//*[@id="priceblock_ourprice"]/text()').extract()
                item['image'] = site.xpath('//*[@id="landingImage"]/@data-a-dynamic-image').extract()
                item['brand'] = site.xpath('//*[@id="brand"]/text()').extract()
                item['bullets'] = site.xpath('//*[@id="feature-bullets"]/span/ul').extract()[0].strip()
                item['category'] = site.xpath('//*[@id="wayfinding-breadcrumbs_feature_div"]/ul').extract()[0].strip()
                item['details'] = site.xpath('//*[@id="prodDetails"]/div/div[1]/div/div/div[2]/div/div/table').extract()[0].strip()
                items.append(item)

            return items

        except IndexError:
                raise IgnoreRequest("Data type not found.")

我想做的是以继续输出蜘蛛结果的其余部分的方式处理此错误,只删除没有值的字段,而不是忽略整个项目。

任何帮助将不胜感激。

【问题讨论】:

    标签: python error-handling exception-handling scrapy


    【解决方案1】:

    你可以做不同的解决方案,如果你想去尝试,只捕获和删除单个字段,那么你必须对所有字段都这样做,

        try:
            //extract field
        except IndexError:
            raise IgnoreRequest("Data type not found.") 
    

    如果你想要一个空值而不是删除,那么你必须检查值是否存在,你可以定义一个单独的提取方法

        def get_value_from_node(self, node):
            value = node.extract()
            return value[0] if value else ''
    

    并为所有字段调用此方法

        item['title'] = self.get_value_from_node(site.xpath('div[@id="centerCol"]/div[@id="title_feature_div"]/div[@id="titleSection"]/h1[@id="title"]/span[@id="productTitle"]/text()'))
    

    它将返回值或空字符串。并且不需要异常处理。

    【讨论】:

    • 看起来这允许我针对我的项目列表提出单独的忽略请求,但结果仍然删除了整行。我刚刚尝试了几次不同的运行,但每次它都会自动跳到 item['bullets'],引发异常并停止。
    • 你想要任何空值吗?
    • 是的,我的预期结果是:asin, title, image 123, 123, 123 123, , 123 而不是跳过整个第二行,它只是跳过缺失的值。
    • @SamClarke 我已经更新了答案,如果 xpath 有效并且网站上存在值,它将返回值,否则它将返回 ''
    • @SamClarke 仅供参考,这是在重新发明轮子。项目加载器 + 处理器是 Scrapy 方式来做你所要求的。
    【解决方案2】:

    Item Loaders 带有 输入或输出处理器 是您需要的。

    TakeFirst processor 定义ItemLoader

    从接收到的值中返回第一个非空/非空值, 因此它通常用作单值字段的输出处理器。 它不接收任何构造函数参数,也不接受 Loader 上下文。

    from scrapy.contrib.loader import ItemLoader
    from scrapy.contrib.loader.processor import TakeFirst
    
    class ProductLoader(ItemLoader):
    
        default_output_processor = TakeFirst()
    
        # specific field loaders
    

    然后,使用加载器加载项目:

    for site in sites:
        l = ProductLoader(Website(), site)
        l.add_value('asin', response.url.split('/')[-1]) # (4)
        l.add_xpath('title', 'div[@id="centerCol"]/div[@id="title_feature_div"]/div[@id="titleSection"]/h1[@id="title"]/span[@id="productTitle"]/text()')
        # ...
    
        yield l.load_item()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 2020-01-22
      • 1970-01-01
      • 2014-02-16
      • 1970-01-01
      相关资源
      最近更新 更多