Python Selenium:滚动页面时点击所有表格行以抓取数据
解决Grainger表格行点击展开及数据抓取问题
问题根源分析
你的代码无法实现表格行点击展开的核心问题如下:
- 直接对
find_elements(By.TAG_NAME, 'tr')返回的元素列表调用click(),属于语法错误,必须遍历每个<tr>元素逐个执行点击操作。 - 点击行后未等待展开内容加载完成,导致后续无法抓取目标数据。
- 部分元素选择器不准确(比如SKU的选择器缺少类名前缀
.)。 - 滚动页面的方式无法确保所有行都处于可交互状态。
修改后的完整代码
import undetected_chromedriver as uc from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import StaleElementReferenceException import pandas as pd import time def scrape_page_data(): # 等待产品列表容器加载完成 container = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, 'pl')) ) # 获取所有表格行(排除表头行) rows = WebDriverWait(container, 10).until( EC.presence_of_all_elements_located((By.TAG_NAME, 'tr')) ) # 跳过表头,只处理数据行 data_rows = rows[1:] if len(rows) > 1 else rows prod_num = [] prod_price = [] prod_desc = [] for index, row in enumerate(data_rows): try: # 滚动到当前行,确保元素可点击 driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", row) time.sleep(0.5) # 等待行元素可点击并执行点击 WebDriverWait(driver, 10).until( EC.element_to_be_clickable(row) ).click() # 等待展开内容加载完成 time.sleep(1) # 显式等待展开区域的元素出现 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '.sku-value__value')) ) # 抓取当前行的厂商型号(MFR Model) sku = row.find_element(By.CSS_SELECTOR, '.sku-value__value').text prod_num.append(sku) # 抓取当前行的价格 price = row.find_element(By.CSS_SELECTOR, '.rbqU0E.lVwVq5').text prod_price.append(price) # 抓取当前行的描述 desc = row.find_element(By.CSS_SELECTOR, '.fUWsxn._3ihaM4').text prod_desc.append(desc) except StaleElementReferenceException: # 处理元素过时异常,重新获取当前行 data_rows = WebDriverWait(container, 10).until( EC.presence_of_all_elements_located((By.TAG_NAME, 'tr')) )[1:] row = data_rows[index] # 重新执行点击和抓取逻辑 driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", row) WebDriverWait(driver, 10).until(EC.element_to_be_clickable(row)).click() time.sleep(1) sku = row.find_element(By.CSS_SELECTOR, '.sku-value__value').text prod_num.append(sku) price = row.find_element(By.CSS_SELECTOR, '.rbqU0E.lVwVq5').text prod_price.append(price) desc = row.find_element(By.CSS_SELECTOR, '.fUWsxn._3ihaM4').text prod_desc.append(desc) except Exception as e: # 处理其他异常,避免程序中断 print(f"处理第{index+1}行时出错: {str(e)}") prod_num.append('') prod_price.append('') prod_desc.append('') return prod_num, prod_price, prod_desc def pagination(url, pages=1): prod_num = [] prod_price = [] prod_desc = [] page_num = 0 for i in range(1, pages + 1): driver.get(f"{url}?offset={page_num}") print(f"正在抓取第{i}页...") current_skus, current_prices, current_descs = scrape_page_data() prod_num.extend(current_skus) prod_price.extend(current_prices) prod_desc.extend(current_descs) print(f"第{i}页抓取完成,共{len(current_skus)}条数据") print(f"累计数据量: 型号{len(prod_num)}条,价格{len(prod_price)}条,描述{len(prod_desc)}条") page_num += 24 time.sleep(2) return prod_num, prod_price, prod_desc # 初始化浏览器 website = 'https://www.grainger.com/category/adhesives-sealants-and-tape/patching-repairing-compounds/wall-repair-patching?categoryIndex=2' options = Options() driver = uc.Chrome(options=options) # 执行抓取 prod_num, prod_price, prod_desc = pagination(website, pages=1) # 保存数据到CSV df = pd.DataFrame({ '厂商型号(MFR Model)': prod_num, '价格': prod_price, '描述': prod_desc }) df.to_csv('grainger_wall_repair.csv', index=False, encoding='utf-8-sig') print("数据已保存到grainger_wall_repair.csv") print(df.head()) # 关闭浏览器 driver.quit()
关键修改说明
- 遍历表格行:循环处理每个数据行,跳过表头行,确保每一行都被点击展开。
- 元素交互保障:使用
scrollIntoView将当前行滚动到可视区域,避免元素不可点击;通过element_to_be_clickable等待元素处于可交互状态。 - 异常处理:捕获
StaleElementReferenceException(元素过时异常),重新获取元素后继续执行,避免程序崩溃。 - 数据抓取优化:点击后等待展开内容加载,确保目标元素存在后再抓取,避免空值;同时直接从当前行定位子元素,确保数据对应关系正确。
- 选择器修正:将SKU的选择器修正为
.sku-value__value(添加类名前缀.),确保定位准确。
内容的提问来源于stack exchange,提问作者ryan houghton
相关产品推荐
相关产品推荐

