Scrapy爬虫解析仅获取每个表格首个元素的问题咨询
问题原因
你现在的代码逻辑有两层核心问题:
- 循环层级错误:外层你只遍历了每个
table.acta-table表格节点,但进入单个表格后,直接调用css('a::text').get(),Scrapy中get()方法只会返回匹配规则的第一个元素,你没有继续遍历表格内部的每一行(<tr>标签),所以每个表格自然只能抓到第一条a标签的内容,剩下的行数据全被漏掉了。 - 未做表格类型区分:页面上多个
acta-table分别对应主队首发、客队替补、红黄牌、进球记录等不同板块,你现在的写法没有区分表格归属,根本没法生成预期的嵌套结构JSON。
修复思路
- 先提取固定字段:优先处理不需要循环的单对象内容,也就是比赛基本信息(
DadesPartit)、场馆信息(Estadi),直接从页面对应节点取值即可。 - 做表格映射关联:通过表格的专属class、表格前置标题文本定位不同功能板块的表格,和你预期JSON里的字段一一对应。
- 逐行提取列表数据:对每个存列表的表格,先跳过表头行,遍历剩下的每一个
<tr>行节点,在单行的作用域里提取姓名、链接、卡牌类型、进球分钟这些对应字段,每行生成一个字典存入对应板块的列表。 - 不要用裸
except:这种写法会吞掉所有真实报错,调试时根本定位不到问题,只需要捕获你预期的特定异常(比如属性不存在的KeyError)即可。
参考修改代码
import scrapy class ActaSpider(scrapy.Spider): name = 'acta_spider' start_urls = ['https://www.fcf.cat/acta/2022/futbol-11/cadet-primera-divisio/grup-2/1c/la-salle-bonanova-ce-a/1c/lhospitalet-centre-esports-b'] def parse(self, response): result = {} # 提取比赛基本信息,选择器需要根据页面实际DOM调整 match_info = response.css('.match-data-block') result['DadesPartit'] = { 'Temporada': match_info.css('.season::text').get('').strip(), 'Categoria': match_info.css('.category::text').get('').strip(), 'Divisio': match_info.css('.division::text').get('').strip(), 'Grup': int(match_info.css('.group::text').get('0').strip()), 'Jornada': int(match_info.css('.matchday::text').get('0').strip()) } # 提取场馆信息 stadium_info = response.css('.stadium-block') result['Estadi'] = { 'Nom': stadium_info.css('.stadium-name::text').get('').strip(), 'Direccio': stadium_info.css('.stadium-address::text').get('').strip() } # 列表类表格和JSON字段的映射关系 table_config = [ ('TitularsCasa', 'table.acta-table.home-starters'), ('SuplentsCasa', 'table.acta-table.home-bench'), ('CosTecnicCasa', 'table.acta-table.home-staff'), ('TargetesCasa', 'table.acta-table.home-cards'), ('Arbitres', 'table.acta-table.referees'), ('Gols', 'table.acta-table.goals'), ('TitularsFora', 'table.acta-table.away-starters'), ('SuplentsFora', 'table.acta-table.away-bench'), ('CosTecnicFora', 'table.acta-table.away-staff'), ('TargetesFora', 'table.acta-table.away-cards'), ] # 逐表格逐行提取数据 for field_name, table_selector in table_config: table = response.css(table_selector) item_list = [] # 跳过表头行遍历数据行 for row in table.css('tr:not(:first-child)'): # 跳过空行 if not row.css('a::text, td::text').get('').strip(): continue item = {} # 提取通用姓名字段,根据页面实际标签结构调整 item['Nom'] = row.css('.player-firstname::text').get('').strip() item['Cognom'] = row.css('.player-lastname::text').get('').strip() # 提取球员链接,相对路径转绝对路径 player_link = row.css('a::attr(href)').get() if player_link and 'Link' in [k for k in item.keys()]: item['Link'] = response.urljoin(player_link) else: item['Link'] = 'Link Error' # 按表格类型提取专属字段 if 'Targetes' in field_name: item['Tipus'] = row.css('.card-type::text').get('').strip() item['Minut'] = int(row.css('.minute::text').get('0').strip()) item.pop('Link') if 'CosTecnic' in field_name: item['Llicencia'] = row.css('.license::text').get('').strip() item.pop('Link') if field_name == 'Arbitres': item['Delegacio'] = row.css('.delegation::text').get('').strip() item.pop('Link') if field_name == 'Gols': item['Tipus'] = row.css('.goal-type::text').get('').strip() item['Minut'] = int(row.css('.minute::text').get('0').strip()) item.pop('Link') item_list.append(item) result[field_name] = item_list yield result
*注意:代码里的CSS类名是通用示例,你需要打开浏览器开发者工具,对照目标页面的实际DOM结构调整选择器,核心逻辑不要改动——必须在表格内逐行遍历,不能直接在表格节点上调用get()取单个值。
内容的提问来源于stack exchange,提问作者futbolbase celh
相关产品推荐
相关产品推荐

