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

Selenium循环点击同class div报InvalidSelectorException问题

Blinkit商品批量采集动态XPath索引报错修复方案

问题场景

  • 实现目标:在指定Blinkit商品列表页完成批量信息采集,执行流程如下:
    1. 通过WebDriverWait显式等待定位class为product__wrapper的商品卡片,点击进入详情页后用BeautifulSoup解析页面,提取分类、子分类、商品名、品牌、保质期、产地、重量、有效期、售价、MRP等字段
    2. 执行返回操作回到原列表页
    3. 点击下一个商品卡片重复采集流程
  • 异常表现:直接定位索引为1的product__wrapper元素执行点击的逻辑,在for循环外可正常运行,但将该逻辑放入for循环、通过{i}拼接XPath动态索引时,会抛出InvalidSelectorException异常

根因说明

InvalidSelectorException属于选择器语法非法直接触发的前置报错,和元素不存在、点击拦截这类运行时异常不同,抛出该异常时优先排查拼接生成的XPath字符串本身的语法合规性,该场景下高频错误点:

  • 漏写属性前缀:XPath匹配属性必须加@前缀,将@class='product__wrapper'错写为class='product__wrapper'是最高发的语法错误,直接导致选择器非法
  • 索引作用域错误:未用括号包裹匹配到的商品节点集合,直接在属性判断规则后加[{i}],会导致XPath解析逻辑不符合预期,部分场景下会直接触发语法校验失败
  • 引号冲突:拼接XPath时内外层使用相同引号,会导致生成的字符串被截断,生成非法选择器
  • 类名匹配逻辑缺陷:直接用@class='product__wrapper'做精确匹配时,一旦元素带有其他动态拼接的类名(如高亮、曝光标记类),会出现匹配失败,该问题虽不会直接触发InvalidSelectorException,但会导致采集流程中断

修复方案

核心代码修正

优先用括号包裹匹配到的商品卡片节点集合后再加动态索引,class匹配用contains方法兼容多类名场景,每次循环重新查找元素避免DOM刷新导致的引用失效,参考实现如下:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time

# 按需配置采集参数
COLLECT_COUNT = 20
wait = WebDriverWait(driver, 15)

for i in range(1, COLLECT_COUNT + 1):
    try:
        # 正确动态XPath写法:括号包裹节点集合后再加索引,明确索引作用域
        product_xpath = f"(//div[contains(@class, 'product__wrapper')])[{i}]"
        # 显式等待元素可点击
        product_card = wait.until(EC.element_to_be_clickable((By.XPATH, product_xpath)))
        # 滚动到元素视口中间位置,避免浮层拦截点击
        driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", product_card)
        time.sleep(0.5)
        product_card.click()

        # 详情页解析逻辑
        wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
        soup = BeautifulSoup(driver.page_source, "html.parser")
        # 此处补充分类、子分类、商品名、品牌、保质期、产地、重量、有效期、售价、MRP等字段的提取逻辑

        # 返回列表页
        driver.back()
        # 等待列表页加载完成后再进入下一次循环
        wait.until(EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'product__wrapper')]")))
        time.sleep(1)
    except Exception as e:
        print(f"第{i}个商品采集异常: {str(e)}")
        # 异常场景强制返回列表页,避免流程卡死
        driver.back()
        wait.until(EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'product__wrapper')]")))
        time.sleep(1)
        continue

避坑说明

  • 禁止在循环外一次性获取所有商品元素列表再循环点击:从详情页返回列表后DOM会刷新,循环外获取的元素引用会触发stale element异常,每次循环必须重新从DOM查找对应索引的元素
  • 懒加载适配:Blinkit列表页为滚动懒加载,若采集数量较大,每次返回列表后可根据当前索引判断是否需要追加滚动操作,加载更多商品后再执行点击
  • 等待逻辑兜底:页面跳转、返回操作后必须加显式等待,确认目标页面加载完成后再执行后续操作,降低偶发异常概率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:36:20