Scrapy爬虫返回空值异常:同结构数据部分获取失败求助
问题排查:Scrapy爬取数据时部分数值返回空格异常
问题背景
爬取目标URL:https://www.rugbypass.com/live/bristol-vs-leicester/stats/?g=938938,使用Scrapy Spider提取比赛统计数据时,出现同结构下部分数值返回空格的异常。
相关代码
Spider核心代码
match_summary_stats = response.css("#statsSummary_stats > div.stat") for stat in match_summary_stats: stat_name = stat.css("div.mid.smaller::text").get() home_value = stat.css("div:nth-child(2)::text").get() away_value = stat.css("div:nth-child(4)::text").get() home_team_row['stats'][stat_name] = home_value away_team_row['stats'][stat_name] = away_value
异常对应的HTML结构
<div class="stat"> <div class="line lh"></div> <div>2</div> <div class="mid smaller">Penalty Goals</div> <div>0</div> <div class="line "></div> </div> <div class="stat"> <div class="line lh"></div> <div>3</div> <div class="mid smaller">Tries</div> <div>2</div> <div class="line "></div> </div>
当前输出
{"game_id": "938938", "stats": {"Penalty Goals": "2", "Tries": " "}}
预期输出
{"game_id": "938938", "stats": {"Penalty Goals": "2", "Tries": "3"}}
Scrapy Item定义
class TeamRow(scrapy.Item): game_id = scrapy.Field( input_processor=MapCompose(remove_tags), output_processor=TakeFirst() ) team_name = scrapy.Field( input_processor=MapCompose(remove_tags), output_processor=TakeFirst() ) opposition_team_name = scrapy.Field( input_processor=MapCompose(remove_tags), output_processor=TakeFirst() ) team_score = scrapy.Field() opposition_team_score = scrapy.Field() win_or_lose = scrapy.Field() stats = scrapy.Field()
原因分析
问题出在div:nth-child(2)和div:nth-child(4)这类基于位置的CSS选择器上:
- 这类选择器依赖父元素下所有子元素的绝对位置,但页面中可能存在隐藏元素(如被CSS隐藏的
<div>)或子文本包含换行/空白字符,导致::text提取到空白而非目标数值。 - 位置选择器鲁棒性极差,页面结构微调(比如新增/删除无关子元素)就会直接导致定位错误。
解决方案
方案1:基于兄弟元素关系定位(推荐)
利用统计名称元素的兄弟关系定位数值,不受其他无关子元素影响:
for stat in match_summary_stats: stat_name = stat.css("div.mid.smaller::text").get() # 取统计名称的前一个兄弟div文本(主场数值) home_value = stat.css("div.mid.smaller").xpath("preceding-sibling::div[1]/text()").get() # 取统计名称的后一个兄弟div文本(客场数值) away_value = stat.css("div.mid.smaller").xpath("following-sibling::div[1]/text()").get() # 处理空白字符,避免空值或空格 home_value = home_value.strip() if home_value else None away_value = away_value.strip() if away_value else None home_team_row['stats'][stat_name] = home_value away_team_row['stats'][stat_name] = away_value
方案2:过滤无效div后取值
先过滤掉无有效文本的line类div,再按顺序提取数值:
for stat in match_summary_stats: stat_name = stat.css("div.mid.smaller::text").get() # 提取所有非line类div的文本,过滤空白内容 valid_values = [text.strip() for text in stat.css("div:not(.line)::text").getall() if text.strip()] if len(valid_values) >= 2: home_value, away_value = valid_values[0], valid_values[1] home_team_row['stats'][stat_name] = home_value away_team_row['stats'][stat_name] = away_value
方案3:使用精确CSS选择器
直接定位非line、非mid类的数值div:
for stat in match_summary_stats: stat_name = stat.css("div.mid.smaller::text").get() # 获取所有目标数值div的文本 raw_values = stat.css("div:not(.line):not(.mid)::text").getall() # 过滤空白后取值 filtered_values = [v.strip() for v in raw_values if v.strip()] if filtered_values: home_value = filtered_values[0] away_value = filtered_values[1] if len(filtered_values) > 1 else None home_team_row['stats'][stat_name] = home_value away_team_row['stats'][stat_name] = away_value
额外优化建议
- 始终对提取的文本做
strip()处理,避免空白字符干扰 - 尽量避免使用基于位置的选择器,改用元素关系或属性选择器,提升代码稳定性
- 添加基础异常处理,避免页面结构变化导致爬虫崩溃
内容的提问来源于stack exchange,提问作者Luke Bray
相关产品推荐
相关产品推荐

