You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Scrapy爬取多页面数据实现单赛事嵌套字典输出的问题

解决方案

完全可以实现你要的嵌套输出效果,核心思路是把已解析到的赛事对象通过请求的参数传递给 roster 页面的回调函数,等所有队伍的roster数据都采集完成后,再一次性yield完整的赛事对象。

核心实现步骤

  • 第一步:解析赛事主页面时,先把赛事名称、场地、日期这些基础字段存到一个赛事item里,同时解析出所有队伍的队名、排名,以及对应的roster页面链接,把这些队伍基础信息暂存到一个列表里,同时统计需要请求的roster页面总数。
  • 第二步:遍历所有队伍的roster链接,发起请求时通过cb_kwargs(Scrapy 2.0+ 推荐)或者meta参数把当前的赛事item、当前请求的队伍索引、待完成的请求计数这些信息传递给回调函数。
  • 第三步:在roster页面的回调函数里,解析出当前队伍的球员名单,填充到对应队伍对象的roster字段里,每完成一个请求就把待完成计数减1。
  • 第四步:当待完成计数归0时,说明所有队伍的roster都采集完成,把填充完所有队伍数据的rank数组赋值给赛事item的rank字段,再yield这个完整的赛事item即可。

示例代码片段

import scrapy
from yourproject.items import TournamentItem, TeamItem

class TournamentSpider(scrapy.Spider):
    name = "tournamentspider"
    
    def start_requests(self):
        yield scrapy.Request(url=self.start_url, callback=self.parse_tournament)
        
    def parse_tournament(self, response):
        # 先解析赛事基础信息
        tournament = TournamentItem()
        tournament['name'] = response.xpath('//h1/text()').get()
        tournament['venue'] = response.xpath('//div[@class="venue"]/text()').get()
        tournament['date'] = response.xpath('//div[@class="date"]/text()').get()
        tournament['rank'] = []
        
        # 解析排名表的所有队伍
        team_rows = response.xpath('//table[@class="rank"]//tr')[1:]
        # 暂存所有队伍的基础信息和roster链接
        team_tasks = []
        for row in team_rows:
            team = TeamItem()
            team['name'] = row.xpath('./td[2]/a/text()').get()
            team['rank'] = row.xpath('./td[1]/text()').get()
            roster_url = row.xpath('./td[2]/a/@href').get()
            team_tasks.append({
                'team': team,
                'roster_url': response.urljoin(roster_url)
            })
        
        # 总共有多少个队伍需要采集roster
        total_teams = len(team_tasks)
        if total_teams == 0:
            # 没有队伍直接返回赛事item
            yield tournament
            return
            
        # 遍历发起roster请求,传递必要的上下文
        for idx, task in enumerate(team_tasks):
            yield scrapy.Request(
                url=task['roster_url'],
                callback=self.parse_roster,
                cb_kwargs={
                    'tournament': tournament,
                    'team': task['team'],
                    'total_teams': total_teams
                }
            )
            
    def parse_roster(self, response, tournament, team, total_teams):
        # 解析球员名单
        roster = response.xpath('//ul[@class="players"]/li/text()').getall()
        team['roster'] = roster
        # 把当前队伍加到赛事的rank列表里
        tournament['rank'].append(team)
        
        # 当所有队伍都处理完了再返回完整的赛事对象
        if len(tournament['rank']) == total_teams:
            yield tournament

注意事项

  • 如果要保证rank数组里的队伍顺序和页面上的排名顺序一致,可以预先给tournament['rank']初始化一个和总队伍数等长的空列表,填充的时候按索引赋值,避免因为异步请求返回顺序不一致导致的排名混乱。
  • 如果是旧版本的Scrapy不支持cb_kwargs,把参数放到meta={'tournament': tournament, ...}里,回调函数里通过response.meta取值即可。
  • 不需要额外做数据关联,所有数据都在请求的上下文里传递,最终输出的就是你要的嵌套结构的单个赛事JSON对象。

内容的提问来源于stack exchange,提问作者kiril

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 02:54:02