繁体   English   中英

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

[英]scrapy how spider returns value to another spider

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

网站结构如下:

<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文件

我的问题

如何将second spiderdate值返回到第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

我给了你一般代码,而不是非常具体的细节,以便让你轻松

你想丢弃一系列日期之外的玩家

您需要做的就是检查parsePlayerdate ,并仅返回相关的date

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

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

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

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

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

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

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

可以做的是强制它按顺序跟踪链接,以便您完全控制。 缺点是它会对性能造成很大影响 :如果scrapy一个接一个地跟随每个链接,则意味着它不能像往常那样同时处理它们并且它会减慢速度。

代码可能是这样的:

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个主页面),这会变得有点复杂,但这个想法保持不变。

像(根据罗宾的答案):

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)

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

最好的感谢我的老师@ pault

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

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

    4.1提取玩家的信息。

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

简单的代码

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

它完美地工作:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM