杰瑞科技汇

Python Selenium 循环如何高效处理元素遍历?

  1. 批量操作元素:在页面上有多个结构相同的元素(如商品列表、搜索结果、菜单项),需要对每个元素执行相同的操作(如点击、获取文本、验证信息)。
  2. 分页处理:当数据需要多页展示时,通过循环点击“下一页”按钮,直到最后一页。
  3. 重试机制:当某个操作可能因为网络延迟或元素未加载而失败时,通过循环进行重试,直到成功或达到最大重试次数。
  4. 数据驱动测试:从一个数据源(如列表、CSV文件、Excel表格)中循环读取数据,并为每组数据执行测试用例。

下面我们通过具体的代码示例来讲解最常见的几种循环用法。

Python Selenium 循环如何高效处理元素遍历?-图1
(图片来源网络,侵删)

for 循环:最常见的用法

for 循环用于遍历一个可迭代对象(如列表、元组、字典,或者 Selenium 中的元素列表)。

示例1:遍历并操作一组元素

假设我们要在一个电商网站上搜索 "手机",然后验证搜索结果列表中的每个商品名称是否包含 "手机"。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
# 1. 设置 WebDriver
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.maximize_window()
driver.implicitly_wait(10)  # 隐式等待,全局有效
# 2. 打开目标网站
driver.get("https://www.example-ecommerce.com") # 这里用示例网站代替
try:
    # 3. 模拟搜索 "手机"
    search_box = driver.find_element(By.ID, "search-input")
    search_box.send_keys("手机")
    search_button = driver.find_element(By.CSS_SELECTOR, "button.search-btn")
    search_button.click()
    # 4. 等待搜索结果加载
    time.sleep(2) # 为了演示,使用固定等待,实际项目中建议用显式等待
    # 5. 找到所有商品名称的元素
    # 假设每个商品名称的 class 都是 "product-title"
    product_titles = driver.find_elements(By.CSS_SELECTOR, ".product-title")
    print(f"找到 {len(product_titles)} 个商品。")
    # 6. 使用 for 循环遍历每个商品元素
    for index, title_element in enumerate(product_titles):
        # 获取元素的文本内容
        product_name = title_element.text
        print(f"商品 {index + 1}: {product_name}")
        # 验证商品名称是否包含 "手机"
        assert "手机" in product_name, f"验证失败!商品 '{product_name}' 不包含 '手机'。"
        print(f"  -> 验证通过!")
    print("\n所有商品验证成功!")
except Exception as e:
    print(f"测试过程中发生错误: {e}")
finally:
    # 7. 关闭浏览器
    driver.quit()

代码解析

  • driver.find_elements(...):注意是 Elements (复数),它会返回一个包含所有匹配元素的列表,如果找不到任何元素,它会返回一个空列表 [],而不是报错。
  • for title_element in product_titles::这个循环会遍历 product_titles 列表中的每一个元素,在每次循环中,title_element 代表当前正在处理的那个商品标题的 WebElement 对象。
  • enumerate(product_titles):这是一个非常有用的函数,它可以在遍历列表的同时,获取每个元素的索引(从0开始)。index 就是索引,title_element 是元素本身。

while 循环:适用于条件判断和重试

while 循环在某个条件为 True 时会一直执行,非常适合用于处理分页或重试逻辑。

Python Selenium 循环如何高效处理元素遍历?-图2
(图片来源网络,侵删)

示例2:处理分页数据

假设我们要抓取一个网站所有页面的文章标题,直到没有“下一页”按钮为止。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.maximize_window()
driver.implicitly_wait(5)
driver.get("https://www.example-blog.com/articles") # 示例博客页面
all_article_titles = []
# 使用 while 循环处理分页
while True:
    try:
        # 1. 获取当前页面的所有文章标题
        article_elements = driver.find_elements(By.CSS_SELECTOR, "article h2 a")
        for article_element in article_elements:
            all_article_titles.append(article_element.text)
            print(f"抓取到文章: {article_element.text}")
        # 2. 查找 "下一页" 按钮
        next_button = driver.find_element(By.CSS_SELECTOR, "a.next-page") # 假设下一页按钮的 class 是 "next-page"
        # 3. 判断 "下一页" 按钮是否可点击(即是否是最后一页)
        # 一个常见的判断方法是检查按钮的 'disabled' 属性是否存在,或者其 'href' 是否为 '#'
        if 'disabled' in next_button.get_attribute('class'):
            print("已到达最后一页。")
            break # 如果是最后一页,跳出循环
        # 4. 点击 "下一页" 按钮
        next_button.click()
        time.sleep(2) # 等待页面加载
    except Exception as e:
        # 如果找不到 "下一页" 按钮(例如在最后一页时),或者发生其他错误,则退出循环
        print(f"找不到 '下一页' 按钮或发生错误: {e}")
        break
print(f"\n总共抓取到 {len(all_article_titles)} 篇文章。")
driver.quit()

代码解析

  • while True::创建一个无限循环,我们需要在循环内部通过 break 语句来手动退出。
  • try...except:在循环中处理异常非常重要,因为当到达最后一页时,“下一页”按钮可能不存在,find_element 会抛出 NoSuchElementException 异常,except 块会捕获这个异常并执行 break 来退出循环。
  • 条件判断:我们通过检查按钮的 class 属性中是否包含 disabled 来判断它是否被禁用,这是一种常见的分页处理逻辑。

示例3:重试机制

当点击一个按钮可能因为元素未准备好而失败时,我们可以用 while 循环来重试。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://example.com/some-page")
max_retries = 3
retry_count = 0
# 使用 while 循环进行重试
while retry_count < max_retries:
    try:
        # 尝试查找并点击一个可能加载缓慢的按钮
        submit_button = driver.find_element(By.ID, "submit-button")
        submit_button.click()
        print("按钮点击成功!")
        break # 成功则跳出循环
    except Exception as e:
        retry_count += 1
        print(f"点击失败,正在进行第 {retry_count} 次重试... 错误: {e}")
        if retry_count >= max_retries:
            print("已达到最大重试次数,测试失败。")
            # 可以在这里添加失败后的处理逻辑,比如截图
            driver.save_screenshot("failure.png")
        else:
            time.sleep(2) # 等待一段时间再重试
driver.quit()

for 循环结合 breakcontinue

  • break:立即终止整个循环。
  • continue:立即终止当前这次循环,并跳到下一次循环的开始。

示例4:使用 breakcontinue

假设我们有一个商品列表,我们想找到第一个价格为 "¥999" 的商品并点击它,如果找到价格低于 "¥100" 的商品则跳过它。

Python Selenium 循环如何高效处理元素遍历?-图3
(图片来源网络,侵删)
# ... (driver 初始化和页面打开代码省略) ...
# 假设我们已经获取了所有商品价格的元素列表
price_elements = driver.find_elements(By.CSS_SELECTOR, ".product-price")
for price_element in price_elements:
    price_text = price_element.text
    # 使用 continue 跳过不符合条件的商品
    if "¥100" in price_text: # 假设价格格式是 "¥99"
        print(f"跳过低价商品: {price_text}")
        continue # 跳过本次循环的剩余部分,进入下一个商品
    # 使用 break 找到目标后立即结束循环
    if "¥999" in price_text:
        print(f"找到目标商品: {price_text},正在点击...")
        # 找到这个商品对应的“购买”按钮并点击
        # 假设按钮在价格元素的父级 div 下
        buy_button = price_element.find_element(By.XPATH, "./../button")
        buy_button.click()
        print("点击成功!")
        break # 点击成功后,不再检查其他商品,直接退出循环
print("循环结束。")

最佳实践与注意事项

  1. find_elements vs find_element

    • 当你需要操作一组元素时,使用 find_elements (复数),它返回一个列表。
    • 当你需要操作一个特定元素时,使用 find_element (单数),如果找不到它会抛出 NoSuchElementException 异常。
  2. 等待策略

    • 避免 time.sleep():虽然 time.sleep() 简单易用,但它会固定等待,无论元素是否已经准备好,这会大大拖慢脚本的执行速度。
    • 优先使用隐式等待 driver.implicitly_wait(10):它会全局设置一个等待时间,在查找元素时,如果元素没找到,会等待最多指定的时间,但它在元素可点击、可交互等方面不够精确。
    • 复杂场景使用显式等待 WebDriverWait:这是最推荐的方式,可以精确地等待某个条件(如元素可见、可点击、包含特定文本)满足后再执行操作。

    显式等待示例

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    # 等待最多10秒,直到id为 "submit-button" 的元素变得可点击
    submit_button = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.ID, "submit-button"))
    )
    submit_button.click()
  3. 性能优化

    • 在循环内部应尽量避免重复的、耗时的操作,如果页面的页眉和页脚在每一页都一样,就不要在循环的每一次迭代中都去重新获取它们。
    • 尽量将 find_element 操作放在循环外部,只在循环内部操作已经找到的元素。
循环类型 适用场景 关键点
for 循环 遍历一组元素(列表、元组等),如商品列表、菜单项。 使用 find_elements 获取元素列表,enumerate 可同时获取索引。
while 循环 基于条件重复执行,如分页爬取、重试机制。 需要设置明确的退出条件(break),并用 try...except 处理异常。
break 在循环中提前终止整个循环。 通常在找到目标元素或达到某个条件后使用。
continue 在循环中跳过当前迭代,直接进入下一次。 通常在遇到不符合条件的元素时使用。

掌握好循环的使用,是编写高效、健壮的 Selenium 自动化脚本的关键一步,希望这些示例和解释能帮助你更好地理解!

分享:
扫描分享到社交APP
上一篇
下一篇