【问题标题】:Multiple pages per item in ScrapyScrapy中每个项目的多个页面
【发布时间】:2014-03-05 15:22:21
【问题描述】:

免责声明:我对 Scrapy 还很陌生。

直截了当地提出我的问题:如何从页面上的链接中检索 Item 属性并将结果返回到同一个 Item 中?

给定以下示例蜘蛛:

class SiteSpider(Spider):
    site_loader = SiteLoader
    ...
    def parse(self, response):
        item = Place()
        sel = Selector(response)
        bl = self.site_loader(item=item, selector=sel)
        bl.add_value('domain', self.parent_domain)
        bl.add_value('origin', response.url)
        for place_property in item.fields:
            parse_xpath = self.template.get(place_property)

            # parse_xpath will look like either:
            # '//path/to/property/text()'
            # or
            # {'url': '//a[@id="Location"]/@href', 
            #  'xpath': '//div[@class="directions"]/span[@class="address"]/text()'}
            if isinstance(parse_xpath, dict):  # place_property is at a URL
                url = sel.xpath(parse_xpath['url_elem']).extract()
                yield Request(url, callback=self.get_url_property,
                              meta={'loader': bl, 'parse_xpath': parse_xpath,
                                    'place_property': place_property})
            else:  # parse_xpath is just an xpath; process normally
                bl.add_xpath(place_property, parse_xpath)
        yield bl.load_item()

    def get_url_property(self, response):
        loader = response.meta['loader']
        parse_xpath = response.meta['parse_xpath']
        place_property = response.meta['place_property']
        sel = Selector(response)
        loader.add_value(place_property, sel.xpath(parse_xpath['xpath'])
        return loader

我正在针对多个站点运行这些蜘蛛,其中大多数都在一个页面上拥有我需要的数据,并且运行良好。但是,某些网站在子页面上具有某些属性(例如,“获取路线”链接中存在的“地址”数据)。

“yield Request”行确实是我遇到问题的地方。我看到这些项目通过管道移动,但它们缺少在其他 URL 中找到的那些属性(IOW,那些到达“yield Request”的属性)。 get_url_property 回调基本上只是在新的 response 变量中查找 xpath,并将其添加到项目加载器实例中。

有没有办法做我正在寻找的事情,或者有更好的方法吗?我想避免进行同步调用来获取我需要的数据(如果这甚至可能在这里),但如果这是最好的方法,那么也许这就是正确的方法。谢谢。

【问题讨论】:

  • parse() 如果未完全填充,则不应生成项目。应该将部分填充的项目传递给 get_url_property 并且应该从那里返回/生成。另见:stackoverflow.com/questions/9334522/…
  • 我明白这一点,但我怎样才能得到一个 Request 对象来解析和处理回调而不从 parse() 产生?我也不能保证每个项目都会涉及请求其他 URL。他们中的大多数不会。
  • 也许你应该看看 CrawlSpider。然后,您可以通过添加回调函数来设置蜘蛛应如何处理不同链接的一些规则。
  • 我试过 CrawlSpider,但也适用类似的规则。项目仍然是分开处理的,在某些情况下,我必须通过多个 URL 将单个项目传递到链中。无论哪种方式,我都阅读了@JanWrobel 在其他地方发布的评论,这给了我一个想法。这是希望......

标签: python scrapy


【解决方案1】:

如果我理解正确的话,你有(至少)两种不同的情况:

  1. 抓取的页面链接到包含数据的另一个页面(需要 1+ 进一步请求)
  2. 抓取的页面包含数据(无需进一步请求)

在您当前的代码中,您在这两种情况下都调用yield bl.load_item(),但在parse 回调中。请注意,您产生的请求会在稍后的某个时间点执行,因此该项目是不完整的,这就是您在第一种情况下缺少该项目的 place_property 键的原因。

可能的解决方案

一个可能的解决方案(如果我理解正确的话)是利用 Scrapy 的异步行为。只涉及对代码的微小更改。

对于第一种情况,您将项目加载器传递给另一个请求,然后该请求将产生它。这就是您在isinstance if 子句中所做的事情。您需要更改 get_url_property 回调的返回值以实际生成加载的项目。

第二种情况,可以直接退货, 因此只需在 else 子句 in 中产生项目。

以下代码包含对您的示例所做的更改。 这能解决您的问题吗?

def parse(self, response):

    # ...

    if isinstance(parse_xpath, dict):  # place_property is at a URL
        url = sel.xpath(parse_xpath['url_elem']).extract()
        yield Request(url, callback=self.get_url_property,
                      meta={'loader': bl, 'parse_xpath': parse_xpath,
                            'place_property': place_property})
    else:  # parse_xpath is just an xpath; process normally
        bl.add_xpath(place_property, parse_xpath)
        yield bl.load_item()

def get_url_property(self, response):

    loader = response.meta['loader']
    # ...
    loader.add_value(place_property, sel.xpath(parse_xpath['xpath'])
    yield loader.load_item()

与该问题相关的是question of chaining requests,我已经注意到了类似的解决方案。

【讨论】:

  • 是的,我的解决方案最终与此有些相似。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多