scrapy如何将蜘蛛返回给另一只蜘蛛的价值

时间:2014-02-07 13:22:22

标签: python python-2.7 scrapy

我正在抓取的网站包含许多玩家,当我点击任何玩家时,我可以访问他的页面。

网站结构如下:

<main page>
<link to player 1>
<link to player 2>
<link to player 3>
..
..
..
<link to payer n>
</main page>

当我点击任何链接时,我会转到播放器的页面,如下所示:

<player name>
<player team>
<player age>
<player salary>
<player date>

我想废除那些年龄在20到25岁之间的所有球员。

我在做什么

  1. 使用第一个蜘蛛抓取主页

  2. 使用第一个蜘蛛获取链接。

  3. 使用第二个蜘蛛抓取每个链接

  4. 使用第二个蜘蛛获取播放器信息

  5. 使用管道将此信息保存在json文件中。

  6. 我的问题

    如何将date值从second spider返回到first spider

    我尝试了什么

    我构建自己的middelware并覆盖process_spider_output。它允许我打印请求但我不知道我还应该做什么才能将date值返回给我的第一个蜘蛛

    感谢任何帮助

    修改

    以下是一些代码:

    def parse(self, response):
            sel = Selector(response)
            Container = sel.css('div[MyDiv]')
            for player in Container:
                extract LINK and TITLE
                yield Request(LINK, meta={'Title': Title}, callback = self.parsePlayer)
    
    def parsePlayer(self,response):
        player = new PlayerItem();
        extract DATE
        return player
    

    我为您提供了一般代码,而不是非常具体的细节,以便于您

3 个答案:

答案 0 :(得分:4)

您想要丢弃日期范围之外的玩家

您只需检查date中的parsePlayer,然后只返回相关内容。

def parsePlayer(self,response):
    player = new PlayerItem();
    extract DATE
    if DATE == some_criteria:
        yield player

您希望按顺序废弃每个链接,并在达到某个日期时停止

例如,如果您遇到性能问题(您正在废弃太多的链接,并且在某些限制之后您不需要这些链接)。

鉴于Scrapy在非对称请求中工作,没有真正好的方法可以做到这一点。唯一的方法是尝试强制线性行为而不是默认的并行请求。

让我解释一下。当你有两个这样的回调时,在默认行为上,scrapy将首先解析第一页(主页)并在其队列中放入对玩家页面的所有请求。 无需等待第一页完成报废,它将开始处理这些播放器页面请求(不一定按照它们找到的顺序)。

因此,当您收到播放器页面p已过期的信息时,已发送p+1p+2的内部请求。 .. p+mm基本上是一个随机数)并且可能已开始处理其中一些请求。可能在 p+1之前p (没有固定的订单,请记住)。

因此,如果您保留此模式,则无法完全停在正确的页面,也无法与parse中的parsePlayer进行互动。

所做的是迫使它按顺序关注链接,以便您拥有完全控制权。缺点是会对性能造成很大的影响:如果scrapy一个接一个地跟随每个链接,则意味着它不能像往常那样同时处理它们,它会减慢速度。< / p>

代码可以是:

def parse(self, response):
    sel = Selector(response)
    self.container = sel.css('div[MyDiv]')
    return self.increment(0)

# Function that will yield the request for player n°index
def increment(index):
    player = self.container[index] # select current player
    extract LINK and TITLE
    yield Request(LINK, meta={'Title': Title, 'index': index}, callback=self.parsePlayer)

def parsePlayer(self,response):
    player = new PlayerItem();
    extract DATE
    yield player

    if DATE == some_criteria:
        index = response.meta['index'] + 1 
        self.increment(index)

这样scrapy将获得主页面,然后是第一个玩家,然后是主页面,然后是第二个玩家,然后是主要等等......直到找到不符合标准的日期。然后没有回调主函数,蜘蛛就停止了。

如果你还要增加主页面的索引(例如,如果有n个主页面),这会变得有点复杂,但这个想法保持不变。

答案 1 :(得分:2)

像(根据罗宾的回答):

class PlayerSpider(Spider):

    def __init__(self):
        self.player_urls = []
        self.done = False  # flag to know when a player with bday out of range found

    def extract_player_urls(self, response):
        sel = Selector(response)
        self.player_urls.extend(extracted player links)

    def parse(self, response):
        self.extract_player_urls(response)
        for i in xrange(10):
            yield Request(self.player_urls.pop(), parse=self.parse_player)

    def parse_player(self, response):
        if self.done:
            return
        ... extract player birth date
        if bd_date not in range:
            self.done = True
            ... somehow clear downloader queue
            return

        ... create and fill item
        yield item
        yield Request(self.player_urls.pop(), parse=self.parse_player)

答案 2 :(得分:2)

首先,我要感谢@ warwaruk,@ Robin帮助我解决这个问题。

最好的感谢我的老师@ pault

我找到了解决方案,这是算法:

  1. 在主页面开始抓取。
  2. 提取所有玩家的链接。
  3. 回复每个玩家的链接以提取他的信息。并且请求的元数据包括:当前主页面中的玩家数量以及我想要废弃的玩家的位置。
  4. 在每个玩家的回调中:

    4.1提取玩家的信息。

    4.2检查愤怒中的日期,如果不是:什么也不做,如果是:检查这是否是主播放器列表中的最后一次播放。如果是,则回调到第二个主页。

  5. 简单代码

    def parse(self, response):
        currentPlayer = 0
        for each player in Players:
            currentPlayer +=1
            yield Request(player.link, meta={'currentPlayer':currentPlayer, 'numberOfPlayers':len(Players),callback = self.parsePlayer)
    
    def parsePlayer(self,response):
        currentPlayer = meta['currentPlayer]
        numberOfPlayers = meta['numberOfPlayers']
        extract player's information
        if player[date] in range:
            if currentPlayer == numberOfPlayers:
                yield(linkToNextMainPage, callback = self.parse)
                yield playerInformatoin #in order to be written in JSON file
            else:
                yield playerInformaton
    

    完美无缺:)