Scrapy中每个项目有多个页面

时间:2014-03-05 15:22:22

标签: python scrapy

免责声明:我对Scrapy很新。

明白地提出我的问题:如何从页面上的链接检索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

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

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

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

1 个答案:

答案 0 :(得分:5)

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

  1. 已抓取的网页链接到包含数据的另一个网页(需要进一步请求1个以上)
  2. 已抓取的网页包含数据(无需进一步请求)
  3. 在您当前的代码中,您在两种情况下都会调用yield bl.load_item(),但在parse回调中。请注意,您收到的请求会在稍后的某个时间点执行,因此该项目不完整,这就是为什么您在第一个案例的项目中缺少 place_property 键的原因。

    可能的解决方案

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

    对于第一种情况,您将项目加载器传递给另一个请求,然后将其生成。这是您在isinstance if子句中执行的操作。您需要更改get_url_property回调的返回值以实际生成已加载的项目。

    对于第二种情况,您可以直接退回该项目, 因此,只需在中生成项目

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

    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,我已经注意到了类似的解决方案。

相关问题