Python爬虫进阶,Selenium 完整入门教程(静态网页搞不定?用它就对了)

123 阅读13分钟

一、前言

在上一篇requests+BeautifulSoup的教程中,我们掌握了静态网页的爬取技巧,但实际场景中,很多网站的数据是动态加载的(如滚动加载列表、点击按钮加载内容、AJAX异步渲染),这类网站用requests获取源码时,只能得到空壳HTML,无法获取到动态渲染的有效数据。

而Selenium就是解决这个问题的“利器”——它是一款自动化测试工具,能够模拟真实浏览器的操作(打开页面、点击、输入、滚动等),等待页面完全加载渲染完成后,再获取完整的网页源码,完美解决动态网页的爬取难题。本文将从环境搭建到实战操作,全面讲解Selenium的核心使用,适合有基础爬虫经验的学习者进阶。

二、前期环境准备

1. 安装Selenium库

首先通过pip安装Selenium第三方库,推荐使用国内镜像源加速下载,命令如下:

# 豆瓣镜像源
pip install selenium -i https://pypi.doubanio.com/simple/

# 若区分Python2/Python3,使用pip3
pip3 install selenium -i https://pypi.doubanio.com/simple/

注意:Selenium 4.x版本相比旧版本有部分API变更,本文基于最新的Selenium 4.x进行讲解,兼容性更好。

2. 配置浏览器驱动(关键步骤)

Selenium本身无法直接操作浏览器,需要依赖对应浏览器的驱动程序,建立与浏览器的通信。常用浏览器为Chrome(推荐)和Edge,下面分别讲解配置方法(以Chrome为例,Edge操作类似)。

步骤1:查看浏览器版本

打开Chrome浏览器,点击「设置」→「关于Chrome」,查看当前浏览器的版本号(如119.0.0.0),确保后续下载的驱动版本与浏览器版本匹配(无需完全一致,大版本相同即可,如119.x对应119.x驱动)。

步骤2:下载Chrome驱动(ChromeDriver)

  1. 官方下载地址:sites.google.com/chromium.or…
  2. 替代下载地址(推荐,国内可访问):registry.npmmirror.com/binary.html…
  3. 找到与浏览器版本对应的驱动版本,下载对应系统(Windows/Mac/Linux)的压缩包。

步骤3:配置驱动(两种方式,二选一即可)

方式1:手动配置环境变量(永久生效,推荐)
  1. 解压下载的驱动压缩包,得到chromedriver.exe(Windows)或chromedriver(Mac/Linux)可执行文件;
  2. 新建一个文件夹(如D:\PythonTools\ChromeDriver),将驱动文件放入该文件夹;
  3. 将该文件夹路径添加到系统环境变量「Path」中(Windows:右键「此电脑」→「属性」→「高级系统设置」→「环境变量」→「系统变量」→「Path」→「编辑」→「新建」,粘贴文件夹路径;Mac/Linux:修改~/.bash_profile~/.zshrc,添加export PATH=$PATH:驱动文件所在文件夹路径,然后执行source ~/.bash_profile生效);
  4. 验证:打开命令行,输入chromedriver --version,若显示驱动版本号,说明配置成功。
方式2:直接放置驱动文件(临时生效,简单快捷)

无需配置环境变量,直接将解压后的chromedriver.exe(Windows)或chromedriver(Mac/Linux)放入Python的安装目录(或当前项目的根目录),运行Selenium代码时会自动识别。

3. 验证环境是否搭建成功

运行以下简单代码,若能自动打开Chrome浏览器并访问百度首页,说明环境搭建成功:

# 导入Selenium的核心模块
from selenium import webdriver
from selenium.webdriver.common.by import By

# 1. 初始化Chrome浏览器驱动
driver = webdriver.Chrome()

# 2. 访问目标网页
driver.get("https://www.baidu.com")

# 3. 打印网页标题,验证是否加载成功
print("网页标题:", driver.title)

# 4. 关闭浏览器(可选,不关闭则浏览器会一直保持打开状态)
# driver.quit()

三、Selenium 核心操作详解

1. 初始化浏览器与基本配置

(1)基础浏览器初始化

除了Chrome,Selenium还支持Edge、Firefox等浏览器,初始化方式类似:

# Chrome浏览器(推荐)
from selenium import webdriver
driver = webdriver.Chrome()

# Edge浏览器(需下载EdgeDriver,配置方法同ChromeDriver)
# driver = webdriver.Edge()

# Firefox浏览器(需下载GeckoDriver)
# driver = webdriver.Firefox()

(2)常用配置:无界面模式(后台运行,不显示浏览器窗口)

很多场景下不需要可视化浏览器窗口,可配置无界面模式(无头模式),节省系统资源,适合服务器运行:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# 1. 构造Chrome配置对象
chrome_options = Options()
# 启用无界面模式
chrome_options.add_argument("--headless=new")  # Selenium 4.x 推荐写法,旧版本为 --headless
# 禁用GPU加速(避免部分环境报错)
chrome_options.add_argument("--disable-gpu")

# 2. 传入配置,初始化浏览器驱动
driver = webdriver.Chrome(options=chrome_options)

# 3. 访问网页,验证功能
driver.get("https://www.baidu.com")
print("无界面模式下网页标题:", driver.title)
driver.quit()

(3)其他实用配置(可选)

chrome_options = Options()
# 设置浏览器窗口大小(宽x高)
chrome_options.add_argument("--window-size=1920,1080")
# 禁用浏览器弹窗
chrome_options.add_argument("--disable-popup-blocking")
# 模拟手机端访问(可选,如模拟iPhone 12)
# chrome_options.add_argument("--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1")

2. 核心:访问网页与浏览器窗口操作

(1)访问指定URL

使用driver.get(url)方法,加载指定网页,该方法会等待页面初步加载完成后再执行后续代码:

from selenium import webdriver

driver = webdriver.Chrome()
# 访问目标网页
target_url = "https://www.runoob.com"
driver.get(target_url)
print(f"已访问:{target_url},网页标题:{driver.title}")

(2)浏览器窗口常用操作

# 1. 刷新当前页面
driver.refresh()

# 2. 前进到下一个页面(需先有后退操作)
# driver.forward()

# 3. 后退到上一个页面
# driver.back()

# 4. 最大化浏览器窗口
driver.maximize_window()

# 5. 最小化浏览器窗口
# driver.minimize_window()

# 6. 获取当前页面的URL
current_url = driver.current_url
print("当前页面URL:", current_url)

# 7. 关闭当前窗口(若只有一个窗口,等同于quit())
# driver.close()

# 8. 退出浏览器,释放所有资源(推荐程序结束时调用)
driver.quit()

3. 关键:定位页面元素(爬虫核心,对应BeautifulSoup的标签查找)

要提取页面数据或进行模拟操作(点击、输入),首先需要定位到目标元素。Selenium提供了多种元素定位方式,核心通过driver.find_element()(定位单个元素,返回第一个匹配结果)和driver.find_elements()(定位所有匹配元素,返回列表),配合By类指定定位策略。

常用定位方式(6种核心,优先掌握ID、XPath、CSS选择器)

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
time.sleep(1)  # 延时1秒,确保页面完全加载

# 方式1:通过ID定位(优先级最高,页面中ID唯一,精准高效)
# 定位百度搜索框(ID为"kw")
search_input_by_id = driver.find_element(By.ID, "kw")
print("ID定位:", search_input_by_id.tag_name)

# 方式2:通过Name定位(适合有唯一name属性的元素)
# 定位百度搜索框(Name为"wd")
search_input_by_name = driver.find_element(By.NAME, "wd")
print("Name定位:", search_input_by_name.tag_name)

# 方式3:通过XPath定位(万能定位,灵活强大,支持复杂场景)
# 绝对XPath(不推荐,页面结构变化易失效):/html/body/div[1]/div[1]/div[5]/div[1]/form/span[1]/input
# 相对XPath(推荐,基于元素属性定位,容错性强)
search_input_by_xpath = driver.find_element(By.XPATH, '//input[@id="kw"]')
print("XPath定位:", search_input_by_xpath.tag_name)

# 方式4:通过CSS选择器定位(高效灵活,与前端开发语法一致)
search_input_by_css = driver.find_element(By.CSS_SELECTOR, "#kw")  # #表示ID,.表示class
print("CSS选择器定位:", search_input_by_css.tag_name)

# 方式5:通过标签名定位(适合批量查找相同标签,如所有<a>标签)
a_tags_by_tag = driver.find_elements(By.TAG_NAME, "a")
print("标签名定位:找到", len(a_tags_by_tag), "个a标签")

# 方式6:通过链接文本定位(专门用于<a>标签,匹配完整文本)
# 定位"新闻"链接
news_link_by_text = driver.find_element(By.LINK_TEXT, "新闻")
print("链接文本定位:", news_link_by_text.text)

# 补充:通过部分链接文本定位(匹配包含指定文本的<a>标签,更灵活)
news_link_by_partial_text = driver.find_element(By.PARTIAL_LINK_TEXT, "新")
print("部分链接文本定位:", news_link_by_partial_text.text)

driver.quit()

核心说明:

  1. driver.find_element():返回第一个匹配的元素对象,无匹配结果抛出NoSuchElementException异常;
  2. driver.find_elements():返回所有匹配的元素对象列表,无匹配结果返回空列表(不抛异常);
  3. 优先使用ID定位(高效唯一),其次是XPathCSS选择器(万能灵活),应对复杂页面。

4. 模拟用户操作(点击、输入、滚动等)

Selenium能模拟真实用户的操作,这是它解决动态网页的核心优势之一,常用操作如下:

(1)输入与清空文本(如搜索框输入关键词)

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
driver.maximize_window()
time.sleep(1)

# 1. 定位搜索框,输入关键词
search_input = driver.find_element(By.ID, "kw")
search_input.send_keys("Python Selenium 教程")  # 输入文本
time.sleep(2)

# 2. 清空搜索框文本(可选)
# search_input.clear()
# time.sleep(1)
# 重新输入
# search_input.send_keys("Python 爬虫进阶")

# 3. 定位搜索按钮,点击提交搜索
search_button = driver.find_element(By.ID, "su")
search_button.click()  # 模拟点击操作

# 等待搜索结果加载
time.sleep(3)
print("搜索结果页面标题:", driver.title)

driver.quit()

(2)模拟滚动页面(应对滚动加载数据)

from selenium import webdriver
import time

driver = webdriver.Chrome()
driver.get("https://www.runoob.com/python/python-blog.html")
driver.maximize_window()
time.sleep(1)

# 方式1:通过JavaScript脚本滚动(万能,支持任意位置)
# 滚动到页面底部(document.body.scrollHeight = 页面总高度)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
print("已滚动到页面底部")
time.sleep(2)

# 滚动到页面顶部
driver.execute_script("window.scrollTo(0, 0);")
print("已滚动到页面顶部")
time.sleep(2)

# 滚动到指定位置(横向x=0,纵向y=800)
driver.execute_script("window.scrollTo(0, 800);")
print("已滚动到y=800位置")
time.sleep(2)

driver.quit()

(3)其他常用模拟操作

# 模拟鼠标右键点击元素
# element.context_click()

# 模拟鼠标双击元素
# element.double_click()

# 模拟按下回车键(需导入Keys类)
from selenium.webdriver.common.keys import Keys
# search_input.send_keys(Keys.ENTER)

# 模拟按下翻页键(PageDown)
# driver.find_element(By.TAG_NAME, "body").send_keys(Keys.PAGE_DOWN)

5. 获取页面数据与元素信息(对应BeautifulSoup的数据提取)

当页面完全加载后,可通过Selenium提取元素的文本、属性,或获取完整的网页源码,再结合BeautifulSoup解析(推荐,效率更高)。

(1)提取单个元素的文本与属性

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
time.sleep(1)

# 1. 提取元素文本(.text 属性,获取元素可见文本)
news_link = driver.find_element(By.LINK_TEXT, "新闻")
news_text = news_link.text
print("元素文本:", news_text)

# 2. 提取元素属性值(.get_attribute("属性名"),如href、src、class等)
news_href = news_link.get_attribute("href")
print("元素href属性:", news_href)

# 3. 提取元素的标签名
news_tag = news_link.tag_name
print("元素标签名:", news_tag)

driver.quit()

(2)获取完整网页源码,结合BeautifulSoup解析

from selenium import webdriver
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time

driver = webdriver.Chrome()
# 访问动态加载的博客页面
driver.get("https://www.runoob.com/python/python-blog.html")
driver.maximize_window()

# 滚动到页面底部,确保所有动态内容加载完成
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(3)  # 延时等待,确保动态数据渲染完成(关键)

# 1. 获取完整的网页源码
html_source = driver.page_source
print("网页源码长度:", len(html_source))

# 2. 结合BeautifulSoup解析,提取数据(复用之前的解析技巧)
soup = BeautifulSoup(html_source, "lxml")
blog_list = soup.find("ul", class_="blog-list").find_all("li")

print(f"\n共提取到{len(blog_list)}篇博客:")
for index, item in enumerate(blog_list, 1):
    title = item.find("a").get_text(strip=True)
    link = "https://www.runoob.com" + item.find("a")["href"]
    print(f"第{index}篇:{title} - {link}")

driver.quit()

6. 等待策略(解决页面加载延迟问题,关键优化)

动态网页的加载需要时间(网络延迟、JS渲染),直接执行后续操作可能会定位不到元素(抛出异常)。Selenium提供了3种等待策略,优先使用显式等待

(1)强制等待(最简单,不推荐,固定延时)

time.sleep(seconds),无论页面是否加载完成,都等待固定时间,优点是简单,缺点是效率低(等待时间过长浪费资源,过短可能加载未完成)。

(2)隐式等待(全局生效,中等推荐)

通过driver.implicitly_wait(seconds)设置全局等待时间,在指定时间内,Selenium会不断尝试定位元素,直到元素找到或超时,仅对元素定位操作生效,优点是无需多次写延时,缺点是灵活性差(无法针对单个元素设置不同等待时间)。

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
# 设置隐式等待10秒(全局生效,仅对find_element/find_elements生效)
driver.implicitly_wait(10)

driver.get("https://www.baidu.com")
# 若10秒内找到元素,立即执行;否则抛出异常
search_input = driver.find_element(By.ID, "kw")
search_input.send_keys("隐式等待测试")

driver.quit()

(3)显式等待(灵活高效,强烈推荐)

通过WebDriverWait配合expected_conditions,针对单个元素设置个性化等待条件(如元素可见、元素可点击、元素存在),超时后抛出异常,灵活性最高,适合复杂动态页面。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://www.baidu.com")

# 1. 构造显式等待对象(最长等待10秒,每0.5秒检查一次条件)
wait = WebDriverWait(driver, 10, poll_frequency=0.5)

# 2. 设置等待条件,等待元素可见并可点击
# expected_conditions 常用条件:
# EC.presence_of_element_located():元素存在于DOM中
# EC.visibility_of_element_located():元素可见(存在且不隐藏)
# EC.element_to_be_clickable():元素可点击
search_input = wait.until(
    EC.element_to_be_clickable((By.ID, "kw")),
    message="超时:未找到可点击的搜索框"
)

# 3. 元素加载完成后,执行操作
search_input.send_keys("显式等待测试")
print("输入成功")

driver.quit()

四、完整实战案例:爬取动态加载的文章列表

1. 实战目标

爬取某动态滚动加载的博客页面(以菜鸟教程Python博客为例,模拟滚动加载更多),提取文章标题、完整链接,解决静态爬取无法获取全部数据的问题。

2. 完整代码

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time

def crawl_dynamic_blog():
    # 1. 配置浏览器,初始化驱动
    driver = webdriver.Chrome()
    driver.maximize_window()
    # 设置隐式等待,兜底处理
    driver.implicitly_wait(8)
    target_url = "https://www.runoob.com/python/python-blog.html"
    
    try:
        # 2. 访问目标网页
        driver.get(target_url)
        print(f"已访问:{target_url}")
        
        # 3. 模拟滚动加载(滚动3次,每次滚动后等待加载)
        scroll_times = 3
        for i in range(scroll_times):
            # 滚动到页面底部
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            print(f"第{i+1}次滚动完成,等待数据加载...")
            # 等待动态数据加载(显式等待,等待新元素出现)
            WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.CLASS_NAME, "blog-list")),
                message="超时:博客列表未加载"
            )
            time.sleep(2)  # 额外延时,确保数据完全渲染
        
        # 4. 获取完整网页源码,解析数据
        html_source = driver.page_source
        soup = BeautifulSoup(html_source, "lxml")
        blog_items = soup.find("ul", class_="blog-list").find_all("li")
        
        # 5. 提取并打印结果
        print(f"\n=== 爬取完成,共获取{len(blog_items)}篇文章 ===")
        for index, item in enumerate(blog_items, 1):
            title_tag = item.find("a")
            if title_tag:
                blog_title = title_tag.get_text(strip=True)
                blog_link = "https://www.runoob.com" + title_tag["href"]
                print(f"第{index}篇:\n  标题:{blog_title}\n  链接:{blog_link}\n")
    
    except Exception as e:
        print(f"爬取过程中出现错误:{e}")
    
    finally:
        # 6. 关闭浏览器,释放资源
        driver.quit()
        print("浏览器已关闭")

if __name__ == "__main__":
    crawl_dynamic_blog()

3. 运行结果说明

运行代码后,浏览器会自动打开并滚动加载页面,最终在控制台输出所有爬取到的文章信息,包括动态加载的内容。该案例的核心是「模拟滚动+等待加载」,确保所有动态数据都被渲染并获取。

五、常见问题与解决办法

  1. NoSuchElementException(未找到元素):① 检查元素定位表达式(ID、XPath等)是否正确;② 页面未加载完成,添加显式等待;③ 元素在iframe中(需先切换iframe:driver.switch_to.frame(iframe_element));④ 元素是动态生成的,需模拟滚动或点击操作后再定位。
  2. 驱动与浏览器版本不匹配:下载与浏览器大版本一致的驱动,避免使用过新或过旧的驱动版本。
  3. 无界面模式运行报错:添加--disable-gpu参数,或更新驱动与浏览器版本。
  4. 中文输入乱码:确保浏览器编码为UTF-8,或使用send_keys()输入时直接传入中文(Selenium 4.x已良好支持中文)。
  5. 页面加载超时:延长显式等待时间,或检查网络连接,避免爬取高负载网站。

六、注意事项与爬虫伦理

  1. Selenium请求频率较慢:由于模拟真实浏览器操作,Selenium的爬取效率远低于requests,仅用于爬取动态网页,静态网页优先使用requests+BeautifulSoup
  2. 控制操作频率:避免频繁滚动、点击,可添加适当延时,防止被网站判定为恶意爬虫,导致IP被封禁。
  3. 遵守robots.txt协议与网站规则:同基础爬虫一致,不爬取涉密、版权保护、个人隐私数据,不用于商业非法用途。
  4. 避免长期占用浏览器资源:使用driver.quit()关闭浏览器(而非driver.close()),释放所有系统资源,防止内存泄漏。