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

使用Selenium+Python出现Timeout Exception,页面导航问题排查

爬虫问题诊断与修复方案

问题描述

爬虫逻辑为:访问指定商品搜索页,逐个点击商品链接进入详情页爬取数据,返回搜索页后继续处理下一个商品。实际运行时,常爬取1-2个商品(偶尔可爬5个),打印“exited”后抛出Timeout Exception,怀疑问题出在页面回退到循环开始的阶段。同时咨询当前自动滚动的实现是否正确。

核心问题诊断

  1. 页面回退后未等待加载完成:回退操作后直接操作页面元素,但页面尚未完全渲染,导致元素定位超时。
  2. Stale Element引用失效:回退后原有的商品列表元素引用已过期,未重新等待元素加载就复用,引发异常。
  3. implicitly_wait用法错误:该方法是设置全局隐式等待时间(单位秒),不是暂停程序,无法替代显式等待确保页面稳定。
  4. 尺码列表未清空:全局定义的sizes列表会累积所有商品的尺码,导致数据混乱。
  5. “查看更多”按钮逻辑缺陷:按钮定位放在循环内部,且未判断按钮是否可点击,点击后未重新获取新的商品列表。
  6. PDF重复写入:每次爬取商品后遍历整个历史数据写入PDF,导致重复输出。

修复方案

1. 优化页面回退与加载等待

用driver.back()替代window.history.go(-1),回退后显式等待商品列表重新加载完成:

driver.back()
# 显式等待搜索页商品列表重新渲染
WebDriverWait(driver, 20).until(
    EC.presence_of_all_elements_located((By.XPATH, "//div[@class = 'product-listing__grid']//div[@class = '_root_129ai_6 product-listing__grid-item']/a"))
)

2. 重置尺码列表

每次进入商品详情页前清空sizes,避免数据累积:

# 进入详情页前添加
sizes = []

3. 修正implicitly_wait用法

移除错误的driver.implicitly_wait(5000),改用显式等待覆盖所有元素定位场景,必要时可补充time.sleep()(仅用于等待页面动画等显式等待无法覆盖的场景)。

4. 优化“查看更多”按钮逻辑

将按钮定位移到循环外,点击后重新获取商品列表:

# 放在while循环末尾,for循环之外
try:
    botao_vermais = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.XPATH, "//div[@class = 'product-listing__view-more']/button"))
    )
    botao_vermais.click()
    # 等待新商品加载完成
    WebDriverWait(driver, 20).until(
        EC.staleness_of(product_list[0])  # 等待旧商品列表失效,确认新列表加载
    )
except TimeoutException:
    # 无更多商品,退出循环
    break

5. 修复PDF写入逻辑

仅写入当前爬取的商品数据,避免重复输出历史内容:

# 替换原有的PDF循环写入代码
current_data = pdf_data[-1]
for data in current_data.values():
    pdf.cell(1.6, 0.5, str(data))
pdf.ln()

6. 自动滚动实现的正确性与优化

当前的scrollIntoView写法是有效的,能将目标元素滚动到视口内。可优化为让元素居中显示,提升点击稳定性:

driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", product_list[index])

完整修复后的核心代码片段

def extract_prodirectsports_data():
    url_line = "https://www.prodirectsport.com/soccer/l/adults/departments-boots/activity-football/brand-adidas/silo-predator/"
    sheet_data = []
    pdf_data = []

    cotacao_libra()  # 假设该函数已定义
    driver.get(url_line)
    
    # 处理弹窗与Cookie
    WebDriverWait(driver, 100).until(EC.element_to_be_clickable((By.XPATH,"//button[@title='Accept all cookies']"))).click()
    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH,"//div[@id='zonos']")))
    close_button = driver.find_element(By.XPATH, "//a[@class = 'z-close']")
    driver.execute_script("arguments[0].click();", close_button)
    WebDriverWait(driver, 100).until(EC.element_to_be_clickable((By.XPATH,"//div[@class='global-popup']")))
    button = driver.find_element(By.XPATH,"//button[@aria-label='Close']")
    driver.execute_script("arguments[0].click();", button)
    
    df_sheet = pd.DataFrame(columns=SHEET_COLUMNS)
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font('Arial', 'B', 12)
    
    driver.maximize_window()

    while True:
        # 每次循环重新获取商品列表
        product_list = WebDriverWait(driver, 20).until(
            EC.presence_of_all_elements_located((By.XPATH, "//div[@class = 'product-listing__grid']//div[@class = '_root_129ai_6 product-listing__grid-item']/a"))
        )
        
        for index, product in enumerate(product_list):
            # 滚动到当前商品(居中显示)
            driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", product)
            
            # 精准定位商品图片
            image_element = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, ".//img[@class='product-listing__image']")))
            image = image_element.get_attribute("src")
            
            # 点击进入详情页
            driver.execute_script("arguments[0].click();", product)
            WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CLASS_NAME, "ml-meta__title")))
            
            # 爬取详情页数据
            product_name = driver.find_element(By.CLASS_NAME, "ml-meta__title").text
            price = driver.find_element(By.CLASS_NAME, "ml-prices__price").text
            product_price = float(re.sub("£", "", price))
            product_code = driver.current_url.split('-')[-1]
            
            # 爬取尺码(重置列表)
            sizes = []
            try:
                size_list = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@class = 'ml-size__sizes']")))
                driver.execute_script("arguments[0].scrollIntoView();", size_list)
                # 相对定位避免获取页面其他按钮
                size_buttons = size_list.find_elements(By.XPATH, ".//button[@class='ml-size__size qa-size-item']")
                for size_btn in size_buttons:
                    product_size = size_btn.text.strip()
                    sizes.append(product_size)
                    if "-" in product_size:
                        sizes.append(product_size.split('-')[0].strip())
                    elif "(" in product_size:
                        sizes.append(product_size.split('(')[0].strip())
            except TimeoutException:
                # 无尺码信息时跳过
                pass
            
            # 计算价格
            price_in_reais = calculate_price_in_reais(product_price)
            sale_price = calculate_sale_price(product_price)
            
            # 保存数据
            sheet_data.append({
                'Photo': image,
                'Code': product_code,
                'Description': product_name,
                'Purchase': price_in_reais,
                'Sale': sale_price,
                'Sizes': sizes
            })
            pdf_data.append({
                'Photo': image,
                'Code': product_code,
                'Description': product_name,
                'Price': product_price
            })
            
            # 写入PDF当前商品数据
            current_data = pdf_data[-1]
            for data in current_data.values():
                pdf.cell(1.6, 0.5, str(data))
            pdf.ln()
            
            # 回退到搜索页并等待加载
            driver.back()
            WebDriverWait(driver, 20).until(
                EC.presence_of_all_elements_located((By.XPATH, "//div[@class = 'product-listing__grid']//div[@class = '_root_129ai_6 product-listing__grid-item']/a"))
            )
            print("回到搜索页,继续处理下一个商品")
        
        # 处理查看更多按钮
        try:
            botao_vermais = WebDriverWait(driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, "//div[@class = 'product-listing__view-more']/button"))
            )
            botao_vermais.click()
            # 等待新商品加载完成
            WebDriverWait(driver, 20).until(
                EC.staleness_of(product_list[0])
            )
        except TimeoutException:
            break
    
    # 生成文件
    pdf.output(url_line + 'cátalogo.pdf')
    df_planilha = pd.DataFrame(sheet_data)
    df_planilha.to_excel(url_line + "catálogo.xlsx")    
    driver.close()

内容的提问来源于stack exchange,提问作者André

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:55:54