一、前言
在上一篇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)
- 官方下载地址:sites.google.com/chromium.or…
- 替代下载地址(推荐,国内可访问):registry.npmmirror.com/binary.html…
- 找到与浏览器版本对应的驱动版本,下载对应系统(Windows/Mac/Linux)的压缩包。
步骤3:配置驱动(两种方式,二选一即可)
方式1:手动配置环境变量(永久生效,推荐)
- 解压下载的驱动压缩包,得到
chromedriver.exe(Windows)或chromedriver(Mac/Linux)可执行文件; - 新建一个文件夹(如
D:\PythonTools\ChromeDriver),将驱动文件放入该文件夹; - 将该文件夹路径添加到系统环境变量「Path」中(Windows:右键「此电脑」→「属性」→「高级系统设置」→「环境变量」→「系统变量」→「Path」→「编辑」→「新建」,粘贴文件夹路径;Mac/Linux:修改
~/.bash_profile或~/.zshrc,添加export PATH=$PATH:驱动文件所在文件夹路径,然后执行source ~/.bash_profile生效); - 验证:打开命令行,输入
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()
核心说明:
driver.find_element():返回第一个匹配的元素对象,无匹配结果抛出NoSuchElementException异常;driver.find_elements():返回所有匹配的元素对象列表,无匹配结果返回空列表(不抛异常);- 优先使用
ID定位(高效唯一),其次是XPath和CSS选择器(万能灵活),应对复杂页面。
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. 运行结果说明
运行代码后,浏览器会自动打开并滚动加载页面,最终在控制台输出所有爬取到的文章信息,包括动态加载的内容。该案例的核心是「模拟滚动+等待加载」,确保所有动态数据都被渲染并获取。
五、常见问题与解决办法
- NoSuchElementException(未找到元素):① 检查元素定位表达式(ID、XPath等)是否正确;② 页面未加载完成,添加显式等待;③ 元素在iframe中(需先切换iframe:
driver.switch_to.frame(iframe_element));④ 元素是动态生成的,需模拟滚动或点击操作后再定位。 - 驱动与浏览器版本不匹配:下载与浏览器大版本一致的驱动,避免使用过新或过旧的驱动版本。
- 无界面模式运行报错:添加
--disable-gpu参数,或更新驱动与浏览器版本。 - 中文输入乱码:确保浏览器编码为UTF-8,或使用
send_keys()输入时直接传入中文(Selenium 4.x已良好支持中文)。 - 页面加载超时:延长显式等待时间,或检查网络连接,避免爬取高负载网站。
六、注意事项与爬虫伦理
- Selenium请求频率较慢:由于模拟真实浏览器操作,Selenium的爬取效率远低于
requests,仅用于爬取动态网页,静态网页优先使用requests+BeautifulSoup。 - 控制操作频率:避免频繁滚动、点击,可添加适当延时,防止被网站判定为恶意爬虫,导致IP被封禁。
- 遵守
robots.txt协议与网站规则:同基础爬虫一致,不爬取涉密、版权保护、个人隐私数据,不用于商业非法用途。 - 避免长期占用浏览器资源:使用
driver.quit()关闭浏览器(而非driver.close()),释放所有系统资源,防止内存泄漏。