现代 Selenium 爬虫教程 (2024)

1. 简介

Selenium 是一个强大的浏览器自动化工具,广泛应用于 Web 测试、数据采集和自动化任务。随着 Web 技术的快速发展,Selenium 仍然是处理 JavaScript 渲染页面和复杂交互场景的首选工具。

本教程将介绍 Selenium 4.x 的最新特性和最佳实践,涵盖从基础到高级的各个方面。

2. 环境准备

2.1 安装浏览器驱动

# 安装 Chrome 浏览器
# Windows/macOS: 从官网下载安装
# Linux (Debian/Ubuntu):
sudo apt-get install google-chrome-stable

# 安装 ChromeDriver
# 方法1: 使用浏览器驱动管理器
pip install webdriver-manager

# 方法2: 手动下载对应版本
# https://chromedriver.chromium.org/downloads

2.2 安装 Python 包

pip install selenium==4.15.0 webdriver-manager

3. 基础用法

3.1 初始化浏览器

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# 现代初始化方式 (Selenium 4+)
service = Service(ChromeDriverManager().install())
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=service, options=options)

# 访问页面
driver.get("https://www.baidu.com")
print(driver.title)
driver.quit()

3.2 元素定位 (Selenium 4 新方式)

from selenium.webdriver.common.by import By

# 推荐使用新的定位方式
search_box = driver.find_element(By.ID, "kw")
search_button = driver.find_element(By.CSS_SELECTOR, "#su")

# 输入和点击
search_box.send_keys("Python")
search_button.click()

4. 高级特性

4.1 显式等待最佳实践

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 等待元素可点击
element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "some-button"))
)

# 等待元素包含特定文本
element = WebDriverWait(driver, 10).until(
    EC.text_to_be_present_in_element((By.CLASS_NAME, "status"), "完成")
)

4.2 动作链和高级交互

from selenium.webdriver.common.action_chains import ActionChains

# 复杂鼠标操作
actions = ActionChains(driver)
actions.move_to_element(menu).pause(1)
actions.click(hidden_submenu).perform()

# 拖放操作
source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "droppable")
ActionChains(driver).drag_and_drop(source, target).perform()

4.3 处理 iframe 和窗口

# 切换到 iframe
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)

# 返回主文档
driver.switch_to.default_content()

# 处理多窗口
main_window = driver.current_window_handle
for handle in driver.window_handles:
    if handle != main_window:
        driver.switch_to.window(handle)
        break

5. 反检测技术

5.1 现代反检测方案

options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)

# 使用 CDP 命令
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
    Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined
    })
    """
})

5.2 高级伪装技巧

# 修改用户代理
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")

# 禁用自动化特征
options.add_argument("--disable-infobars")
options.add_argument("--disable-extensions")
options.add_argument("--disable-gpu")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--no-sandbox")

6. 无头模式和性能优化

6.1 现代无头模式配置

options.add_argument("--headless=new")  # Chrome 112+ 的新无头模式
options.add_argument("--window-size=1920,1080")
options.add_argument("--blink-settings=imagesEnabled=false")  # 禁用图片加载
options.add_argument("--disable-javascript")  # 可选: 禁用JS

# 性能优化参数
prefs = {"profile.managed_default_content_settings.images": 2}
options.add_experimental_option("prefs", prefs)

6.2 资源控制

# 限制网络请求
driver.execute_cdp_cmd("Network.enable", {})
driver.execute_cdp_cmd("Network.setBlockedURLs", {
    "urls": ["*.png", "*.jpg", "*.gif"]
})

# 设置网络条件
driver.execute_cdp_cmd("Network.emulateNetworkConditions", {
    "offline": False,
    "downloadThroughput": 500 * 1024,  # 500KB/s
    "uploadThroughput": 500 * 1024,
    "latency": 50
})

7. 最佳实践

7.1 页面加载策略

# 设置页面加载策略
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

caps = DesiredCapabilities.CHROME.copy()
caps["pageLoadStrategy"] = "eager"  # 可选: normal, eager, none
driver = webdriver.Chrome(desired_capabilities=caps)

7.2 异常处理和重试机制

from selenium.common.exceptions import WebDriverException
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
def safe_click(element):
    try:
        element.click()
    except WebDriverException as e:
        if "element click intercepted" in str(e).lower():
            driver.execute_script("arguments[0].click();", element)
        else:
            raise

7.3 分布式爬虫集成

# 使用 Selenium Grid
from selenium.webdriver.remote.webdriver import WebDriver

def create_remote_driver(hub_url):
    options = webdriver.ChromeOptions()
    return WebDriver(
        command_executor=hub_url,
        options=options
    )

# 或者使用 Docker 容器
def create_docker_driver():
    options = webdriver.ChromeOptions()
    return webdriver.Remote(
        command_executor="http://localhost:4444/wd/hub",
        options=options
    )

8. 现代替代方案

8.1 Playwright 集成

# Playwright 是现代化的浏览器自动化工具
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

8.2 混合使用 Requests 和 Selenium

# 获取 cookies 用于 requests
def get_cookies(driver):
    return {c["name"]: c["value"] for c in driver.get_cookies()}

# 使用 requests 保持会话
import requests
session = requests.Session()
session.cookies.update(get_cookies(driver))
response = session.get("https://example.com/api/data")

9. 总结

Selenium 仍然是处理复杂 Web 交互和 JavaScript 渲染页面的强大工具。2024 年的最佳实践包括:

  1. 使用 Selenium 4+ 的新 API
  2. 采用更可靠的反检测技术
  3. 优化无头模式的性能和稳定性
  4. 结合现代工具如 Playwright 或 CDP 命令
  5. 实现健壮的异常处理和重试机制

通过本教程,你应该能够构建高效、稳定的现代化 Selenium 爬虫系统。