Selenium循环点击同class div报InvalidSelectorException问题
Blinkit商品批量采集动态XPath索引报错修复方案
问题场景
- 实现目标:在指定Blinkit商品列表页完成批量信息采集,执行流程如下:
- 通过
WebDriverWait显式等待定位class为product__wrapper的商品卡片,点击进入详情页后用BeautifulSoup解析页面,提取分类、子分类、商品名、品牌、保质期、产地、重量、有效期、售价、MRP等字段 - 执行返回操作回到原列表页
- 点击下一个商品卡片重复采集流程
- 通过
- 异常表现:直接定位索引为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
相关产品推荐
相关产品推荐

