Python Selenium 教程:从浏览器自动化到网页爬虫完整指南

📝 906 字 · ☕ 3 分钟阅读

前言:为什么需要 Selenium?

当你在浏览器中手动打开网页、点击按钮、填写表单、滚动页面时——这些操作如果每天重复几百次,你大概率会想:能不能让程序替我做?

Selenium 就是答案。它是一个浏览器自动化框架,支持 Chrome、Firefox、Edge 等主流浏览器,可以模拟真实用户在浏览器中的几乎所有操作。它的核心应用场景包括:

  • 网页端自动化测试:模拟用户行为验证功能是否正常
  • 数据采集(爬虫):处理 JavaScript 渲染的动态页面
  • 自动化办公:自动登录系统、批量下载文件、定时填报
  • 网页截图与监控:定时截取页面变化

本教程将从零开始,带你掌握 Selenium 的核心用法,并手写一个完整的实战项目。

1. 环境准备:安装 Selenium 与 WebDriver

1.1 安装 Selenium 库

pip install selenium

1.2 安装 ChromeDriver

Selenium 需要 WebDriver 来与浏览器通信。推荐使用 webdriver-manager 自动管理驱动版本:

pip install webdriver-manager

然后用代码自动处理:

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

# 自动下载并管理 ChromeDriver,无需手动配置
driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install())
)

⚠️ 注意:如果你使用 Firefox,替换为 webdriver.Firefox()GeckoDriverManager()

2. 核心操作:定位元素与交互

2.1 打开网页与基本操作

from selenium import webdriver
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://www.example.com")

# 获取页面标题
print(driver.title)

# 等待3秒,观察页面
time.sleep(3)

# 关闭浏览器
driver.quit()

2.2 八种元素定位方式

Selenium 提供了 8 种定位元素的策略,按推荐优先级排序:

from selenium.webdriver.common.by import By

# 1. ID 定位(最快,最推荐)
element = driver.find_element(By.ID, "search-input")

# 2. CSS 选择器(灵活,第二推荐)
element = driver.find_element(By.CSS_SELECTOR, "input#search-input")
element = driver.find_element(By.CSS_SELECTOR, ".btn-primary")
element = driver.find_element(By.CSS_SELECTOR, "div > a[href='/login']")

# 3. XPath(功能最强,但性能稍差)
element = driver.find_element(By.XPATH, "//input[@id='search-input']")
element = driver.find_element(By.XPATH, "//button[contains(text(), '登录')]")

# 4. Class Name
element = driver.find_element(By.CLASS_NAME, "btn-primary")

# 5. Tag Name
element = driver.find_element(By.TAG_NAME, "h1")

# 6. Link Text(精确匹配超链接文字)
element = driver.find_element(By.LINK_TEXT, "点击这里注册")

# 7. Partial Link Text(模糊匹配)
element = driver.find_element(By.PARTIAL_LINK_TEXT, "注册")

# 8. Name(针对表单元素)
element = driver.find_element(By.NAME, "username")

2.3 常用交互操作

# 点击按钮
driver.find_element(By.ID, "submit-btn").click()

# 输入文本
driver.find_element(By.ID, "username").send_keys("my_username")

# 清空输入框
driver.find_element(By.ID, "username").clear()

# 获取元素的文本
text = driver.find_element(By.CLASS_NAME, "result").text
print(text)

# 获取元素的属性(如 href、src、value)
href = driver.find_element(By.CSS_SELECTOR, "a.download-link").get_attribute("href")
print(f"下载链接: {href}")

# 获取整个页面的 HTML
html = driver.page_source

3. 等待机制:稳定自动化的关键

网页加载需要时间,直接操作尚未出现的元素会抛出 NoSuchElementException。Selenium 提供了两种等待方式。

3.1 隐式等待(Implicit Wait)

# 全局等待:在找任何元素时最多等10秒
driver.implicitly_wait(10)
# 只需设置一次,整个 driver 生命周期有效

3.2 显式等待(Explicit Wait)—— 推荐使用

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

wait = WebDriverWait(driver, 10)  # 最多等10秒

# 等待元素可见
element = wait.until(
    EC.visibility_of_element_located((By.ID, "result-panel"))
)

# 等待元素可点击
button = wait.until(
    EC.element_to_be_clickable((By.ID, "submit-btn"))
)

# 等待元素出现
element = wait.until(
    EC.presence_of_element_located((By.CLASS_NAME, "dynamic-content"))
)

# 等待元素消失(常用于加载动画)
wait.until(
    EC.invisibility_of_element_located((By.ID, "loading-spinner"))
)

# 等待页面标题包含特定文字
wait.until(EC.title_contains("搜索结果"))

显式等待 vs 隐式等待:显式等待针对性更强、更灵活。实际项目中两者可配合使用(隐式作为兜底,显式用于关键节点)。

4. 进阶技巧:处理复杂场景

4.1 处理下拉选择框

from selenium.webdriver.support.ui import Select

select_element = driver.find_element(By.ID, "city-select")
select = Select(select_element)

# 按可见文本选择
select.select_by_visible_text("北京")

# 按 value 属性选择
select.select_by_value("beijing")

# 按索引选择(从0开始)
select.select_by_index(0)

# 获取所有选项
options = select.options
for opt in options:
    print(f"{opt.get_attribute('value')}: {opt.text}")

4.2 处理弹窗(Alert)

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

# 等待弹窗出现
alert = WebDriverWait(driver, 5).until(EC.alert_is_present())

# 获取弹窗文本
print(alert.text)

# 确认弹窗(点击"确定")
alert.accept()

# 取消弹窗(点击"取消")
alert.dismiss()

# 输入文本(适用于 prompt 弹窗)
alert.send_keys("我的输入")
alert.accept()

4.3 切换窗口与标签页

# 获取当前窗口句柄
main_window = driver.current_window_handle

# 点击在新标签页打开的链接
driver.find_element(By.ID, "new-tab-link").click()

# 等待新窗口出现
WebDriverWait(driver, 10).until(
    EC.new_window_is_opened
)

# 获取所有窗口句柄
all_windows = driver.window_handles

# 切换到新窗口
driver.switch_to.window(all_windows[-1])
print(f"新窗口标题: {driver.title}")

# 关闭当前窗口
driver.close()

# 切回主窗口
driver.switch_to.window(main_window)

4.4 处理 iframe 内嵌页面

# 通过索引切换(第一个 iframe)
driver.switch_to.frame(0)

# 通过 name 或 id 切换
driver.switch_to.frame("content-iframe")

# 通过 WebElement 切换
iframe = driver.find_element(By.CSS_SELECTOR, "iframe[src*='form']")
driver.switch_to.frame(iframe)

# 在 iframe 内操作
driver.find_element(By.ID, "name").send_keys("张三")

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

4.5 滚动页面

from selenium.webdriver.common.action_chains import ActionChains

# 方法1:滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

# 方法2:滚动到指定元素可见
element = driver.find_element(By.ID, "footer")
driver.execute_script("arguments[0].scrollIntoView();", element)

# 方法3:使用 ActionChains 滚动
actions = ActionChains(driver)
actions.move_to_element(element).perform()

5. 实战项目:自动采集豆瓣电影 Top250

下面我们用 Selenium 写一个完整的爬虫,抓取豆瓣电影 Top250 的片名、评分和简介。豆瓣页面大量使用 JavaScript 渲染,恰好是 Selenium 大显身手的场景。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import csv
import time

# 初始化
driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install())
)
driver.implicitly_wait(5)

wait = WebDriverWait(driver, 10)
movies = []

BASE_URL = "https://movie.douban.com/top250"

for page in range(10):  # 10页,每页25部
    driver.get(f"{BASE_URL}?start={page * 25}")
    
    # 等待列表加载
    wait.until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ".grid_view"))
    )

    # 解析每部电影
    items = driver.find_elements(By.CSS_SELECTOR, ".item")
    
    for item in items:
        title = item.find_element(By.CSS_SELECTOR, ".title").text
        rating = item.find_element(By.CSS_SELECTOR, ".rating_num").text
        try:
            quote = item.find_element(By.CSS_SELECTOR, ".inq").text
        except:
            quote = ""
        
        movies.append({
            "title": title,
            "rating": rating,
            "quote": quote
        })
    
    print(f"第 {page + 1}/10 页完成,已采集 {len(movies)} 部")
    time.sleep(1)  # 礼貌停顿,避免被封

# 保存到 CSV
with open("douban_top250.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=["title", "rating", "quote"])
    writer.writeheader()
    writer.writerows(movies)

print(f"✅ 采集完成!共 {len(movies)} 部电影,已保存到 douban_top250.csv")

driver.quit()

6. Headless 模式:无界面运行

在服务器或 CI/CD 环境中,你没有显示器。此时需要无头模式(Headless),浏览器在后台运行,不弹出窗口。

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

options = Options()
options.add_argument("--headless=new")  # Chrome 112+ 新无头模式
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")

# 可选:模拟移动端
options.add_argument(
    "--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
)

driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install()),
    options=options
)

driver.get("https://www.example.com")
print(driver.title)

# 截图保存(无头模式下也可以截图)
driver.save_screenshot("screenshot.png")

driver.quit()

7. 常见报错与解决方案

❌ 报错:selenium.common.exceptions.NoSuchElementException

原因:元素还没加载出来就去找它。

解决:使用显式等待 WebDriverWait + expected_conditions

❌ 报错:selenium.common.exceptions.TimeoutException

原因:元素在规定时间内没有出现。

解决:检查选择器是否正确;可能需要增加等待时间或检查是否有 iframe。

❌ 报错:Message: session not created: Chrome version mismatch

原因:ChromeDriver 版本与 Chrome 浏览器版本不一致。

解决:使用 webdriver-manager 自动匹配,或手动下载对应版本的 ChromeDriver。

❌ 报错:Message: invalid argument: ‘name’ is not a valid value of ‘target’

原因switch_to.frame() 参数错误。

解决:确认 frame 的 id、name 或 WebElement 对象正确。

总结

Selenium 是浏览器自动化的利器,掌握了它,你就能够:

  • 自动化测试 Web 应用的完整流程
  • 采集任何 JavaScript 渲染的动态页面数据
  • 编写机器人替代重复的浏览器操作
  • 对网站进行定时监控和截图

下一步你可以学习 Selenium Grid(分布式执行)、Pytest + Selenium(自动化测试框架)、以及结合 Playwright 做横向对比。

FAQ

Q1:Selenium 和 Requests + BeautifulSoup 有什么区别?

Selenium 会完整运行浏览器渲染 JavaScript,适合处理动态页面。Requests + BeautifulSoup 只获取原始 HTML,速度快但无法解析 JS 渲染的内容。如果你的目标网站是 SPA(单页应用)或有大量异步加载,请使用 Selenium。

Q2:Selenium 采集数据会被封 IP 吗?怎么做反爬?

会的,尤其当你请求频率过高。反爬技巧:1)使用 time.sleep() 加随机延迟;2)设置不同的 User-Agent;3)使用代理 IP 轮换;4)添加无头模式的窗口尺寸参数防止特征检测;5)使用 undetected-chromedriver 库对抗反爬检测。

Q3:Selenium 4 和 Selenium 3 有什么主要变化?

Selenium 4(2021年发布)新增了:相对定位器(Relative Locators)、改进的窗口/标签页管理 API、原生 Chrome DevTools Protocol 支持(可用于模拟网络条件、捕获性能日志等)、以及更完善的 W3C WebDriver 标准支持。如果你还在用 Selenium 3,建议尽快迁移。

📤 分享这篇文章