如何在Scrapy中使用多个嵌套的跨距CSS选择器?

时间:2018-09-25 10:33:09

标签: python web-scraping scrapy css-selectors

我正在处理一个棘手的CSS选择器问题,该问题涉及多个嵌套的 span

(A)通常,HTML / CSS如下所示:

<div class="pricing">
    <strong>1 200 €</strong> 
</div>

(B),但也有一些部分看起来像这样:

<div class="pricing">
    <strong>
        <span class="promotion">
            <span class="promo-price">1 100 €</span>
        </span>
        <span class="strike">
            <span>1 200€</span>
        </span>
    </strong>
    <div class="new">New supplier</div>
</div>

(C),就像这样:

<div class="pricing">
    <strong>3 400 €</strong> 
    <span>/ best:  4500.00 €</span>
</div>

(D),就像这样:

<div class="pricing">
    <strong>4 900 €</strong> 
    <span class="netto">+ taxes</span> 
    <span>/ best:  4900.00 €</span>
</div>

使用类型为Scrapy的CSS选择器

response.css("div.pricing strong ::text").extract()
# ['2 500 €', '\n    ', '\n    ', '1 100 €', '\n    ', '\n    ', '1 200€', '3 999 €',...]

这表明上述CSS中有问题的<span ...>在选择器文本中添加了空格。因此,我尝试忽略{em> both 和strikepromotion类,使用:not()的各种变化如下:

response.css("div.pricing strong:not([class*='promotion']):not([class*='strike'])::text").extract()
# <same result as above>

我还可以通过以下方式获得promo-price

response.css("div.pricing  .promo-price::text").extract()
# ['1 100 €']

在这一点上,我对如何操作一无所知:

  • 获取所有(A)价格
  • 获取所有(B)promo-price(仅)
  • 没有引入空格的结果(如上所示)
  • (最好是)一个 CSS选择器或行中的所有上述条件

问:如何以最简单的方式做到这一点?


注意:我已经看到了类似的问题:

但是对于我来说,他们没有提供太多帮助。


更新

我无法按照@boltclock的指示完成任务,结果遭到了难看的骇客攻击,如下所示:

adPrice = aditem.css("div.pricing strong::text").extract_first().strip()
if adPrice == '':
    adPrice = aditem.css("div.pricing span.promo-price::text").extract_first()

所以,如果有人有更好或更优雅的解决方案...

1 个答案:

答案 0 :(得分:1)

嗯。

div.new仅出现在包含所有复杂性(B)的strong之后,而不出现在仅包含单个价格(A)的strong之后吗?

如果是这样:

  
      
  • 获取所有(A)价格
  •   
  • 没有引入空格的结果(如上所示)
  •   
response.css("div.pricing strong:only-child::text").extract()

请注意,省略了::text之前的空格,以确保您仅获得直接位于strong中的文本—有关用法指南,请参见我对this question的回答的结尾。 / p>

:only-child确保存在div.new时不匹配,如果缺少则表示(A),因此您不必担心(B)。

  
      
  • 获取所有(B)promo-price(仅)
  •   
response.css("div.pricing .promo-price::text").extract()
  
      
  • (最好是)一个 CSS选择器或行中的所有上述条件
  •   

在这一点上,将以上两个选择器进行分组应该很简单:

response.css("div.pricing strong:only-child::text, div.pricing .promo-price::text").extract()

如果div.new不相关,那么使用CSS选择器将很难做到这一点,因为没有其他方法可以区分(A)与(B)。另一方面,XPath的工作量很短:

response.xpath("//div[@class='pricing']/(strong[not(./span)]|descendant::span[@class='promo-price'])/text()").extract()