我正在抓取的网站包含许多玩家,当我点击任何玩家时,我可以访问他的页面。
网站结构如下:
<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岁之间的所有球员。
使用第一个蜘蛛抓取主页。
使用第一个蜘蛛获取链接。
使用第二个蜘蛛抓取每个链接。
使用第二个蜘蛛获取播放器信息。
使用管道将此信息保存在json文件中。
如何将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
答案 0 :(得分:4)
您只需检查date
中的parsePlayer
,然后只返回相关内容。
def parsePlayer(self,response):
player = new PlayerItem();
extract DATE
if DATE == some_criteria:
yield player
例如,如果您遇到性能问题(您正在废弃太多的链接,并且在某些限制之后您不需要这些链接)。
鉴于Scrapy在非对称请求中工作,没有真正好的方法可以做到这一点。唯一的方法是尝试强制线性行为而不是默认的并行请求。
让我解释一下。当你有两个这样的回调时,在默认行为上,scrapy将首先解析第一页(主页)并在其队列中放入对玩家页面的所有请求。 无需等待第一页完成报废,它将开始处理这些播放器页面请求(不一定按照它们找到的顺序)。
因此,当您收到播放器页面p
已过期的信息时,已已发送p+1
,p+2
的内部请求。 .. p+m
(m
基本上是一个随机数)并且可能已开始处理其中一些请求。可能在 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
我找到了解决方案,这是算法:
在每个玩家的回调中:
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
完美无缺:)