当前位置: 首页 > news >正文

全面了解selenium

引言:为什么选择 Selenium?

在当今的互联网时代,Web 应用已经成为企业服务用户的主要载体。无论是电商平台、社交媒体还是企业内部系统,都需要保证其功能稳定性和用户体验。这就催生了对 Web 自动化工具的强烈需求 —— 开发者需要高效地测试 Web 应用,数据分析师需要批量获取网页信息,而普通用户也希望通过自动化脚本简化重复操作。

Selenium作为目前最流行的 Web 自动化工具之一,自 2004 年诞生以来,已经成为行业标准。它支持多种编程语言(Python、Java、C# 等)、多种浏览器(Chrome、Firefox、Edge 等)和多种操作系统,能够模拟真实用户的操作行为,实现从简单的表单提交到复杂的单页应用交互的全流程自动化。

本文将从 Selenium 的核心概念出发,通过丰富的代码示例,全面覆盖以下内容:

  • Selenium 的架构与核心组件
  • 环境搭建与基础配置
  • 元素定位与交互操作
  • 高级功能(等待机制、窗口切换、文件处理等)
  • 自动化测试框架集成
  • 网页数据爬取实战
  • 性能优化与最佳实践

无论你是测试工程师、数据分析师还是开发人员,掌握 Selenium 都将显著提升你的工作效率,让你从繁琐的重复劳动中解放出来。

一、Selenium 核心概念与架构

1.1 Selenium 是什么?

Selenium 是一个用于 Web 应用测试的开源工具集,它允许开发者通过编程方式控制浏览器执行各种操作,如点击按钮、填写表单、页面导航等。与其他自动化工具相比,Selenium 的核心优势在于:

  • 真实浏览器环境:直接驱动真实浏览器,模拟用户真实操作,避免了与 JavaScript 渲染相关的兼容性问题
  • 跨平台与跨浏览器:支持 Windows、macOS、Linux,以及 Chrome、Firefox、Safari 等主流浏览器
  • 多语言支持:提供 Python、Java、C#、Ruby 等多种编程语言的 API
  • 强大的社区支持:作为最流行的 Web 自动化工具,拥有丰富的文档和解决方案

1.2 Selenium 的核心组件

Selenium 生态系统包含多个组件,各自承担不同的功能:

  1. Selenium WebDriver:核心组件,提供编程接口用于控制浏览器,支持多种编程语言,是本文的重点内容
  2. Selenium IDE:浏览器插件,用于录制和回放操作,适合快速创建简单的自动化脚本
  3. Selenium Grid:用于分布式测试,允许在不同的机器和浏览器上并行执行测试用例,提高测试效率

1.3 Selenium 的工作原理

Selenium WebDriver 采用客户端 - 服务器架构:

  • 客户端:开发者编写的自动化脚本(如 Python 代码)
  • 浏览器驱动:特定浏览器的驱动程序(如 ChromeDriver),作为客户端与浏览器之间的桥梁
  • 浏览器:实际执行操作的浏览器(如 Chrome)

其工作流程如下:

  1. 客户端脚本通过 WebDriver API 发送指令(如 "打开网页"、"点击按钮")
  2. 浏览器驱动接收指令并将其转换为浏览器能理解的格式
  3. 浏览器执行相应操作并将结果返回给驱动
  4. 驱动将结果反馈给客户端脚本

这种架构的优势在于:

  • 支持跨语言开发(只需实现对应的 WebDriver API)
  • 可以远程控制不同机器上的浏览器
  • 与浏览器的交互更加稳定可靠

二、环境搭建与基础配置

2.1 安装 Python

Selenium 支持多种编程语言,其中 Python 因其简洁易学的特点成为最受欢迎的选择之一。首先需要安装 Python 环境:

  1. 访问 Python 官网(Welcome to Python.org)下载对应操作系统的安装包
  2. 安装时勾选 "Add Python to PATH",方便在命令行中直接使用 Python
  3. 验证安装:打开命令行,输入python --versionpython3 --version,显示版本号即安装成功

2.2 安装 Selenium 库

使用 pip(Python 包管理工具)安装 Selenium:

bash

pip install selenium
# 或使用pip3(区分Python2和Python3时)
pip3 install selenium

验证安装:

bash

python -c "import selenium; print(selenium.__version__)"

若输出 Selenium 版本号(如 4.15.0),则安装成功。

2.3 安装浏览器驱动

Selenium 需要特定的浏览器驱动才能控制浏览器,不同浏览器需要对应版本的驱动:

浏览器驱动下载地址
Chromehttps://sites.google.com/chromium.org/driver/
Firefoxhttps://github.com/mozilla/geckodriver/releases
EdgeMicrosoft Edge WebDriver | Microsoft Edge Developer
Safari内置(需在 Safari 设置中启用 "开发" 菜单下的 "允许远程自动化")

安装步骤(以 Chrome 为例):

  1. 查看 Chrome 版本:打开 Chrome,在地址栏输入chrome://version/,记录版本号(如 118.0.5993.88)
  2. 下载对应版本的 ChromeDriver:在下载页面找到与 Chrome 版本匹配的驱动(主版本号需一致,如 118.x.x.x)
  3. 配置驱动:
    • Windows:将下载的 chromedriver.exe 放入 Python 安装目录(或添加到系统 PATH 环境变量)
    • macOS/Linux:将驱动文件放入 /usr/local/bin/ 目录,或添加到 PATH

验证驱动配置:

bash

# Windows
chromedriver --version# macOS/Linux
chromedriver --version

若输出驱动版本号,则配置成功。

2.4 第一个 Selenium 程序

让我们编写一个简单的程序验证环境是否配置正确:

python

运行

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager# 初始化Chrome浏览器
# 使用webdriver_manager自动管理驱动(无需手动下载)
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))# 打开百度首页
driver.get("https://www.baidu.com")# 打印页面标题
print("页面标题:", driver.title)# 等待3秒(让我们看清效果)
import time
time.sleep(3)# 关闭浏览器
driver.quit()

代码说明:

  • webdriver.Chrome():创建 Chrome 浏览器实例
  • driver.get(url):打开指定 URL
  • driver.title:获取当前页面标题
  • driver.quit():关闭浏览器并释放资源

运行程序后,会自动打开 Chrome 浏览器并访问百度首页,控制台输出页面标题,3 秒后关闭浏览器。

提示:webdriver_manager库可以自动下载和管理浏览器驱动,避免版本匹配问题,安装方法:pip install webdriver-manager

三、元素定位:Selenium 的核心技能

在 Web 自动化中,元素定位是最基础也是最重要的操作 —— 我们需要先找到页面上的元素(如按钮、输入框),才能进行后续的交互。Selenium 提供了多种元素定位方法,适用于不同场景。

3.1 常用元素定位方法

Selenium 提供了 8 种元素定位方法,定义在By类中:

方法说明适用场景
By.ID通过元素的 id 属性定位元素有唯一 id 时(推荐)
By.NAME通过元素的 name 属性定位元素有 name 属性且值唯一时
By.CLASS_NAME通过元素的 class 属性定位元素有 class 属性时(注意:class 可能包含多个值,需完整匹配)
By.TAG_NAME通过 HTML 标签名定位页面中该标签唯一时
By.LINK_TEXT通过链接的完整文本定位定位<a>标签,文本唯一时
By.PARTIAL_LINK_TEXT通过链接的部分文本定位定位<a>标签,只需匹配部分文本
By.XPATH通过 XPath 表达式定位复杂场景,几乎适用于所有情况
By.CSS_SELECTOR通过 CSS 选择器定位复杂场景,语法简洁,性能优于 XPath

3.2 单元素定位与多元素定位

Selenium 提供了两类定位方法:

  • 单元素定位:返回第一个匹配的元素,如find_element()
  • 多元素定位:返回所有匹配的元素列表,如find_elements()

代码示例:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager# 初始化浏览器
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")# 1. 通过ID定位(百度搜索框)
search_box = driver.find_element(By.ID, "kw")
print("ID定位结果:", search_box.tag_name)  # 输出:input# 2. 通过NAME定位
search_box = driver.find_element(By.NAME, "wd")
print("NAME定位结果:", search_box.get_attribute("id"))  # 输出:kw# 3. 通过CLASS_NAME定位(百度一下按钮)
search_button = driver.find_element(By.CLASS_NAME, "s_btn")
print("CLASS_NAME定位结果:", search_button.get_attribute("value"))  # 输出:百度一下# 4. 通过TAG_NAME定位(定位所有a标签)
links = driver.find_elements(By.TAG_NAME, "a")
print("页面中a标签数量:", len(links))  # 输出:页面中链接的数量# 5. 通过LINK_TEXT定位(百度首页的"新闻"链接)
news_link = driver.find_element(By.LINK_TEXT, "新闻")
print("LINK_TEXT定位结果:", news_link.get_attribute("href"))  # 输出:新闻页面URL# 6. 通过PARTIAL_LINK_TEXT定位(包含"地图"的链接)
map_link = driver.find_element(By.PARTIAL_LINK_TEXT, "地图")
print("PARTIAL_LINK_TEXT定位结果:", map_link.text)  # 输出:地图# 7. 通过XPATH定位(搜索框)
search_box = driver.find_element(By.XPATH, '//*[@id="kw"]')
print("XPATH定位结果:", search_box.tag_name)  # 输出:input# 8. 通过CSS_SELECTOR定位(搜索按钮)
search_button = driver.find_element(By.CSS_SELECTOR, "#su")
print("CSS_SELECTOR定位结果:", search_button.get_attribute("value"))  # 输出:百度一下# 关闭浏览器
driver.quit()

3.3 XPath 详解

XPath(XML Path Language)是一种在 XML 文档中定位节点的语言,也适用于 HTML 文档。XPath 功能强大,几乎可以定位到页面上的任何元素。

3.3.1 绝对路径与相对路径
  • 绝对路径:从根节点/html开始,逐级向下,如/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input

    • 缺点:页面结构稍有变化就会失效,不推荐使用
  • 相对路径:从任意节点开始,以//开头,如//input[@id="kw"]

    • 优点:更灵活,对页面结构变化的容忍度更高,推荐使用
3.3.2 常用 XPath 表达式
表达式说明示例
//标签名选择所有该标签的元素//input 选择所有 input 元素
//@属性名选择所有具有该属性的元素//@id 选择所有具有 id 属性的元素
//标签名[@属性名="值"]选择属性等于指定值的元素//input[@name="wd"]
//标签名[contains(@属性名,"部分值")]选择属性包含指定部分值的元素//a[contains(@href,"baidu.com")]
//标签名[text()="文本"]选择文本内容等于指定值的元素//a[text()="新闻"]
//标签名[contains(text(),"部分文本")]选择文本包含指定部分的元素//a[contains(text(),"地图")]
//标签名[position()=n]选择第 n 个该标签的元素//div[position()=1] 选择第一个 div
//父标签名/子标签名选择父标签下的子标签//form/input 选择 form 下的 input

XPath 代码示例:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManagerdriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")# 1. 定位id为kw的input元素
element = driver.find_element(By.XPATH, '//input[@id="kw"]')# 2. 定位name包含"wd"的元素
element = driver.find_element(By.XPATH, '//*[contains(@name,"wd")]')# 3. 定位文本为"百度一下"的元素
element = driver.find_element(By.XPATH, '//*[text()="百度一下"]')# 4. 定位class包含"bg"的div下的input元素
element = driver.find_element(By.XPATH, '//div[contains(@class,"bg")]/input')# 5. 定位第2个a标签
element = driver.find_element(By.XPATH, '//a[position()=2]')driver.quit()

3.4 CSS 选择器详解

CSS 选择器是另一种强大的元素定位方式,语法简洁,性能通常优于 XPath,是很多开发者的首选。

3.4.1 常用 CSS 选择器语法
选择器说明示例
#id通过 id 定位#kw 定位 id 为 kw 的元素
.class通过 class 定位.s_btn 定位 class 为 s_btn 的元素
标签名通过标签名定位input 定位所有 input 元素
标签名#id标签 + id 定位input#kw 定位 id 为 kw 的 input 元素
标签名.class标签 + class 定位input.s_ipt 定位 class 为 s_ipt 的 input 元素
[属性名]具有指定属性的元素[name] 定位所有有 name 属性的元素
[属性名="值"]属性等于指定值的元素[name="wd"] 定位 name 为 wd 的元素
[属性名^="前缀"]属性以指定前缀开头的元素[href^="http://"] 定位 href 以 http:// 开头的元素
[属性名$="后缀"]属性以指定后缀结尾的元素[src$=".png"] 定位 src 以.png 结尾的元素
[属性名*="包含"]属性包含指定值的元素[class*="btn"] 定位 class 包含 btn 的元素
父选择器 子选择器后代元素form input 定位 form 下的所有 input 后代
父选择器>子选择器直接子元素div>input 定位 div 的直接子元素 input
选择器1+选择器2相邻兄弟元素input+button 定位 input 后面的第一个 button
选择器:nth-child(n)第 n 个子元素li:nth-child(2) 定位第 2 个 li 元素

CSS 选择器代码示例:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManagerdriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")# 1. 通过id定位
element = driver.find_element(By.CSS_SELECTOR, '#kw')# 2. 通过class定位
element = driver.find_element(By.CSS_SELECTOR, '.s_btn')# 3. 标签+属性定位
element = driver.find_element(By.CSS_SELECTOR, 'input[name="wd"]')# 4. 属性包含定位
element = driver.find_element(By.CSS_SELECTOR, '[class*="bg"]')# 5. 后代元素定位
element = driver.find_element(By.CSS_SELECTOR, 'form #kw')# 6. 第n个子元素定位
element = driver.find_element(By.CSS_SELECTOR, 'div:nth-child(1)')driver.quit()

3.5 定位技巧与最佳实践

  1. 优先使用 ID:ID 在页面中通常是唯一的,定位速度快且稳定
  2. 其次考虑 NAME 或 CLASS:如果没有 ID,可使用这两种方法
  3. 复杂场景用 XPath 或 CSS:对于动态生成的元素或复杂结构,使用这两种方法
  4. 避免使用绝对路径:维护性差,容易受页面结构变化影响
  5. 使用相对定位:提高脚本的稳定性和可维护性
  6. 结合多种属性:当单一属性不足以定位时,可组合多个属性
  7. 利用开发者工具:浏览器的开发者工具(F12)可以帮助快速定位元素并生成选择器

开发者工具使用技巧

  • 在页面右键点击元素,选择 "检查" 打开开发者工具
  • 在 Elements 面板中右键点击元素,选择 "Copy" -> "Copy XPath" 或 "Copy selector" 获取定位表达式
  • 注意:自动生成的表达式可能不够简洁,需要手动优化

四、元素交互:模拟用户操作

定位到元素后,下一步就是与元素进行交互,模拟用户的各种操作。Selenium 提供了丰富的方法来实现点击、输入、选择等操作。

4.1 常用元素交互方法

方法说明
click()点击元素(适用于按钮、链接等)
send_keys(*value)向输入框发送文本
clear()清空输入框内容
submit()提交表单(适用于 form 元素)
get_attribute(name)获取元素的属性值
text获取元素的文本内容
is_displayed()判断元素是否可见
is_enabled()判断元素是否可用
is_selected()判断复选框或单选按钮是否被选中

代码示例:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")# 定位搜索框
search_box = driver.find_element(By.ID, "kw")# 1. 输入文本
search_box.send_keys("Selenium自动化")
time.sleep(2)  # 等待2秒,观察效果# 2. 获取属性值
print("搜索框类型:", search_box.get_attribute("type"))  # 输出:text
print("搜索框名称:", search_box.get_attribute("name"))  # 输出:wd# 3. 清空内容
search_box.clear()
time.sleep(2)# 4. 重新输入并提交
search_box.send_keys("Python")
search_box.submit()  # 提交表单,相当于点击搜索按钮
time.sleep(3)# 5. 定位搜索结果标题并获取文本
result_title = driver.find_element(By.XPATH, '//*[@id="1"]/h3/a')
print("第一个搜索结果标题:", result_title.text)# 6. 判断元素状态
print("结果标题是否可见:", result_title.is_displayed())  # 输出:True# 7. 点击搜索结果
result_title.click()
time.sleep(5)# 关闭浏览器
driver.quit()

4.2 处理表单元素

表单是 Web 应用中最常见的交互元素,包括文本框、密码框、复选框、单选按钮、下拉菜单等。

4.2.1 处理复选框和单选按钮

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import time# 访问一个包含表单的测试页面
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.w3schools.com/html/html_forms.asp")# 切换到包含表单的iframe(示例页面的表单在iframe中)
driver.switch_to.frame("iframeResult")# 1. 处理复选框
checkbox1 = driver.find_element(By.XPATH, '//input[@value="vehicle1"]')
checkbox2 = driver.find_element(By.XPATH, '//input[@value="vehicle2"]')# 勾选复选框(如果未勾选)
if not checkbox1.is_selected():checkbox1.click()
time.sleep(2)if not checkbox2.is_selected():checkbox2.click()
time.sleep(2)# 取消勾选第一个复选框
if checkbox1.is_selected():checkbox1.click()
time.sleep(2)# 2. 处理单选按钮
radio1 = driver.find_element(By.XPATH, '//input[@value="male"]')
radio2 = driver.find_element(By.XPATH, '//input[@value="female"]')# 选择第一个单选按钮
if not radio1.is_selected():radio1.click()
time.sleep(2)# 选择第二个单选按钮
if not radio2.is_selected():radio2.click()
time.sleep(2)# 退出iframe
driver.switch_to.default_content()driver.quit()
4.2.2 处理下拉菜单

下拉菜单(<select>元素)是表单中的特殊元素,Selenium 提供了Select类专门处理:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select")# 切换到iframe
driver.switch_to.frame("iframeResult")# 定位select元素并创建Select对象
select_element = driver.find_element(By.TAG_NAME, "select")
select = Select(select_element)# 1. 通过索引选择(索引从0开始)
select.select_by_index(1)  # 选择第二个选项
time.sleep(2)# 2. 通过可见文本选择
select.select_by_visible_text("Saab")
time.sleep(2)# 3. 通过value属性选择
select.select_by_value("opel")
time.sleep(2)# 获取所有选项
options = select.options
print("所有选项:")
for option in options:print(option.text)# 获取当前选中的选项
selected_option = select.first_selected_option
print("当前选中的选项:", selected_option.text)# 退出iframe
driver.switch_to.default_content()driver.quit()

4.3 处理文件上传和下载

文件上传和下载是 Web 应用中常见的功能,Selenium 提供了相应的处理方法。

4.3.1 文件上传

文件上传通常通过<input type="file">元素实现,Selenium 可以直接向该元素发送文件路径:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import time
import osdriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_input_type_file")# 切换到iframe
driver.switch_to.frame("iframeResult")# 定位文件上传元素
file_input = driver.find_element(By.ID, "myFile")# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 要上传的文件路径(替换为实际文件路径)
file_path = os.path.join(current_dir, "test.txt")# 发送文件路径到文件上传元素
file_input.send_keys(file_path)
time.sleep(3)# 退出iframe
driver.switch_to.default_content()driver.quit()

注意:确保test.txt文件存在于脚本所在目录,或使用绝对路径。

4.3.2 文件下载

文件下载需要配置浏览器的下载路径,不同浏览器的配置方式略有不同:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
import time
import os# 配置Chrome浏览器的下载设置
chrome_options = Options()
# 设置下载路径(替换为实际路径)
download_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "downloads")
# 创建下载目录(如果不存在)
os.makedirs(download_dir, exist_ok=True)# 配置下载选项
prefs = {"download.default_directory": download_dir,  # 下载路径"download.prompt_for_download": False,       # 不提示下载"download.directory_upgrade": True,"safebrowsing.enabled": True                 # 启用安全浏览
}
chrome_options.add_experimental_option("prefs", prefs)# 初始化浏览器
driver = webdriver.Chrome(ChromeDriverManager().install(),options=chrome_options
)# 访问一个提供下载的页面(示例)
driver.get("https://www.w3schools.com/python/python_intro.asp")# 定位并点击下载链接(示例)
# 注意:实际页面的下载链接可能不同,需要根据实际情况修改
try:download_link = driver.find_element(By.LINK_TEXT, "Download Python")download_link.click()print("开始下载...")time.sleep(10)  # 等待下载完成(根据文件大小调整)print(f"文件已下载到:{download_dir}")
except Exception as e:print("未找到下载链接:", e)driver.quit()

五、浏览器控制与导航

除了元素交互,Selenium 还可以控制浏览器本身,如调整窗口大小、前进后退、刷新页面等。

5.1 浏览器窗口控制

python

运行

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")
time.sleep(2)# 1. 设置窗口大小
driver.set_window_size(1024, 768)  # 宽1024,高768
time.sleep(2)# 2. 最大化窗口
driver.maximize_window()
time.sleep(2)# 3. 最小化窗口
driver.minimize_window()
time.sleep(2)# 4. 全屏显示
driver.fullscreen_window()
time.sleep(2)# 5. 获取窗口大小
window_size = driver.get_window_size()
print(f"窗口大小:宽{window_size['width']},高{window_size['height']}")# 6. 设置窗口位置(左上角坐标)
driver.set_window_position(100, 100)
time.sleep(2)# 7. 获取窗口位置
window_position = driver.get_window_position()
print(f"窗口位置:x={window_position['x']},y={window_position['y']}")driver.quit()

5.2 页面导航

python

运行

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())# 1. 打开网页
driver.get("https://www.baidu.com")
print("当前URL:", driver.current_url)
print("页面标题:", driver.title)
time.sleep(2)# 2. 打开另一个网页
driver.get("https://www.bing.com")
print("当前URL:", driver.current_url)
print("页面标题:", driver.title)
time.sleep(2)# 3. 后退
driver.back()
print("后退后URL:", driver.current_url)
time.sleep(2)# 4. 前进
driver.forward()
print("前进后URL:", driver.current_url)
time.sleep(2)# 5. 刷新页面
driver.refresh()
time.sleep(2)# 6. 获取页面源码
page_source = driver.page_source
print("页面源码长度:", len(page_source))driver.quit()

5.3 多窗口与 iframe 切换

现代 Web 应用经常使用多窗口或 iframe(内嵌框架),Selenium 需要切换到相应的上下文才能操作其中的元素。

5.3.1 多窗口切换

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")
time.sleep(2)# 获取当前窗口句柄(唯一标识)
original_window = driver.current_window_handle
print("原始窗口句柄:", original_window)# 打开新窗口(通过JavaScript)
driver.execute_script("window.open('https://www.bing.com');")
time.sleep(2)# 获取所有窗口句柄
all_windows = driver.window_handles
print("所有窗口句柄:", all_windows)# 切换到新窗口
for window in all_windows:if window != original_window:driver.switch_to.window(window)breakprint("切换后窗口URL:", driver.current_url)
time.sleep(2)# 在新窗口中执行操作
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("Selenium")
search_box.submit()
time.sleep(3)# 切换回原始窗口
driver.switch_to.window(original_window)
print("切换回原始窗口URL:", driver.current_url)
time.sleep(2)# 关闭当前窗口(新窗口)
driver.switch_to.window(all_windows[1])
driver.close()
time.sleep(2)# 切换回原始窗口并继续操作
driver.switch_to.window(original_window)
print("最终窗口URL:", driver.current_url)driver.quit()
5.3.2 iframe 切换

iframe 是内嵌在页面中的另一个 HTML 文档,需要先切换到 iframe 才能操作其中的元素:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.w3schools.com/html/tryit.asp?filename=tryhtml_iframe")
time.sleep(2)# 1. 通过id或name切换iframe
driver.switch_to.frame("iframeResult")
time.sleep(2)# 在iframe中操作元素
heading = driver.find_element(By.TAG_NAME, "h1")
print("iframe中的标题:", heading.text)
time.sleep(2)# 2. 切换到嵌套的iframe(iframe中的iframe)
nested_iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(nested_iframe)
time.sleep(2)# 在嵌套iframe中操作
nested_heading = driver.find_element(By.TAG_NAME, "h1")
print("嵌套iframe中的标题:", nested_heading.text)
time.sleep(2)# 3. 切换回父级iframe
driver.switch_to.parent_frame()
time.sleep(2)# 4. 切换回主文档
driver.switch_to.default_content()
time.sleep(2)# 在主文档中操作
menu_item = driver.find_element(By.LINK_TEXT, "HTML Tutorial")
print("主文档中的菜单:", menu_item.text)driver.quit()

5.4 处理 JavaScript 弹窗

Web 页面中常见的弹窗有三种:alert(警告框)、confirm(确认框)、prompt(提示框),Selenium 提供了相应的处理方法:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import timedriver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.w3schools.com/js/tryit.asp?filename=tryjs_alert")
time.sleep(2)# 切换到iframe
driver.switch_to.frame("iframeResult")# 1. 处理alert弹窗
alert_button = driver.find_element(By.XPATH, '//button[text()="Try it"]')
alert_button.click()
time.sleep(2)# 切换到alert
alert = driver.switch_to.alert
print("Alert文本:", alert.text)# 接受alert(点击确定)
alert.accept()
time.sleep(2)# 2. 处理confirm弹窗
driver.get("https://www.w3schools.com/js/tryit.asp?filename=tryjs_confirm")
driver.switch_to.frame("iframeResult")
confirm_button = driver.find_element(By.XPATH, '//button[text()="Try it"]')
confirm_button.click()
time.sleep(2)confirm = driver.switch_to.alert
print("Confirm文本:", confirm.text)# 取消confirm(点击取消)
confirm.dismiss()
time.sleep(2)# 3. 处理prompt弹窗
driver.get("https://www.w3schools.com/js/tryit.asp?filename=tryjs_prompt")
driver.switch_to.frame("iframeResult")
prompt_button = driver.find_element(By.XPATH, '//button[text()="Try it"]')
prompt_button.click()
time.sleep(2)prompt = driver.switch_to.alert
print("Prompt文本:", prompt.text)# 向prompt输入文本并确认
prompt.send_keys("Selenium")
prompt.accept()
time.sleep(2)# 退出iframe
driver.switch_to.default_content()driver.quit()

六、等待机制:处理页面加载与动态内容

在 Web 自动化中,页面元素的加载往往不是瞬间完成的,特别是使用 AJAX 或 JavaScript 动态生成的内容。如果脚本执行速度快于元素加载速度,就会导致 "元素未找到" 的错误。Selenium 提供了三种等待机制来解决这个问题。

6.1 强制等待(sleep)

强制等待是最简单的等待方式,使用time.sleep(seconds)让脚本暂停指定的秒数:

python

运行

import time
from selenium import webdriver
from selenium.webdriver.common.by import Bydriver = webdriver.Chrome()
driver.get("https://www.baidu.com")# 强制等待2秒
time.sleep(2)search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("Selenium")
search_box.submit()# 强制等待3秒,等待搜索结果加载
time.sleep(3)result = driver.find_element(By.XPATH, '//*[@id="1"]/h3/a')
print(result.text)driver.quit()

缺点

  • 无论元素是否已经加载,都会等待固定时间,降低脚本执行效率
  • 难以确定合适的等待时间(设置太短可能仍会出错,设置太长则浪费时间)

6.2 隐性等待(Implicit Wait)

隐性等待设置一个全局的等待时间,在这个时间内 Selenium 会不断尝试查找元素,直到元素被找到或超时:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import Bydriver = webdriver.Chrome()
# 设置隐性等待时间为10秒
driver.implicitly_wait(10)driver.get("https://www.baidu.com")search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("Selenium")
search_box.submit()# 最多等待10秒,直到元素出现
result = driver.find_element(By.XPATH, '//*[@id="1"]/h3/a')
print(result.text)driver.quit()

特点

  • 只需设置一次,对整个 driver 生命周期有效
  • 等待元素被找到,但不等待元素可交互
  • 超时后会抛出NoSuchElementException

6.3 显性等待(Explicit Wait)

显性等待是最灵活、最推荐的等待方式,它允许我们等待某个条件满足(如元素可见、可点击等),并可以设置超时时间和轮询间隔:

python

运行

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 ECdriver = webdriver.Chrome()
driver.get("https://www.baidu.com")search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("Selenium")
search_box.submit()# 创建WebDriverWait对象,设置超时时间为10秒
wait = WebDriverWait(driver, 10)# 等待元素可见(最多10秒)
result = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/h3/a'))
)
print(result.text)# 其他常用条件
# 等待元素可点击
# button = wait.until(EC.element_to_be_clickable((By.ID, "button_id")))# 等待元素存在于DOM中
# element = wait.until(EC.presence_of_element_located((By.ID, "element_id")))# 等待标题包含指定文本
# wait.until(EC.title_contains("Selenium"))driver.quit()
6.3.1 常用的 Expected Conditions

expected_conditions模块提供了多种预定义的条件,常用的有:

条件说明
title_is(title)页面标题等于指定标题
title_contains(substring)页面标题包含指定子串
presence_of_element_located(locator)元素存在于 DOM 中
visibility_of_element_located(locator)元素可见(存在且可见)
visibility_of(element)已知元素可见
element_to_be_clickable(locator)元素可点击
element_located_to_be_selected(locator)元素被选中
text_to_be_present_in_element(locator, text)元素包含指定文本
text_to_be_present_in_element_value(locator, text)元素的 value 属性包含指定文本
frame_to_be_available_and_switch_to_it(locator)iframe 可用并切换到该 iframe
invisibility_of_element_located(locator)元素不可见
alert_is_present()存在 alert 弹窗
6.3.2 自定义等待条件

如果预定义的条件不能满足需求,我们可以定义自己的等待条件:

python

运行

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWaitdriver = webdriver.Chrome()
driver.get("https://www.baidu.com")search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("Selenium")
search_box.submit()# 自定义条件:等待元素文本长度大于10
def text_length_greater_than_10(driver):element = driver.find_element(By.XPATH, '//*[@id="1"]/h3/a')return len(element.text) > 10# 应用自定义条件
wait = WebDriverWait(driver, 10)
result = wait.until(text_length_greater_than_10)
print(f"满足条件的元素文本:{result.text},长度:{len(result.text)}")driver.quit()

6.4 等待机制最佳实践

  1. 优先使用显性等待:灵活性高,只在需要等待的地方应用,不影响其他操作
  2. 合理设置超时时间:根据页面加载速度设置,一般 3-10 秒
  3. 避免混合使用隐性等待和显性等待:可能导致不可预测的等待时间
  4. 针对不同操作选择合适的条件
    • 读取文本:使用visibility_of_element_located(确保元素可见)
    • 点击按钮:使用element_to_be_clickable(确保元素可点击)
    • 判断元素存在:使用presence_of_element_located(只需存在于 DOM 中)
  5. 结合使用强制等待:在极少数情况下,如页面跳转、文件下载等,可配合使用

七、Selenium 自动化测试框架集成

Selenium 不仅可以用于网页爬取,更是自动化测试的利器。将 Selenium 与 Python 的单元测试框架(如 unittest 或 pytest)结合,可以构建强大的自动化测试套件。

7.1 与 unittest 集成

unittest 是 Python 内置的单元测试框架,提供了测试用例、测试套件、断言等功能:

python

运行

import unittest
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 webdriver_manager.chrome import ChromeDriverManagerclass TestBaiduSearch(unittest.TestCase):# 在所有测试方法执行前运行def setUp(self):self.driver = webdriver.Chrome(ChromeDriverManager().install())self.driver.maximize_window()self.driver.get("https://www.baidu.com")self.wait = WebDriverWait(self.driver, 10)# 在所有测试方法执行后运行def tearDown(self):self.driver.quit()# 测试方法必须以test开头def test_search_selenium(self):# 定位搜索框并输入内容search_box = self.wait.until(EC.element_to_be_clickable((By.ID, "kw")))search_box.clear()search_box.send_keys("Selenium")# 点击搜索按钮search_button = self.driver.find_element(By.ID, "su")search_button.click()# 验证搜索结果result = self.wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/h3/a')))self.assertIn("Selenium", result.text, "搜索结果不包含预期文本")def test_search_python(self):# 定位搜索框并输入内容search_box = self.wait.until(EC.element_to_be_clickable((By.ID, "kw")))search_box.clear()search_box.send_keys("Python")# 点击搜索按钮search_button = self.driver.find_element(By.ID, "su")search_button.click()# 验证搜索结果result = self.wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/h3/a')))self.assertIn("Python", result.text, "搜索结果不包含预期文本")if __name__ == "__main__":# 运行所有测试unittest.main()

7.2 与 pytest 集成

pytest 是比 unittest 更灵活的测试框架,支持更简洁的语法和丰富的插件:

首先安装 pytest:

bash

pip install pytest

测试代码:

python

运行

import pytest
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 webdriver_manager.chrome import ChromeDriverManager# 测试前的准备工作
@pytest.fixture(scope="module")
def driver():driver = webdriver.Chrome(ChromeDriverManager().install())driver.maximize_window()driver.get("https://www.baidu.com")yield driver  # 测试完成后执行后续代码driver.quit()# 测试搜索功能
def test_search_selenium(driver):wait = WebDriverWait(driver, 10)# 输入搜索内容search_box = wait.until(EC.element_to_be_clickable((By.ID, "kw")))search_box.clear()search_box.send_keys("Selenium")# 点击搜索driver.find_element(By.ID, "su").click()# 验证结果result = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/h3/a')))assert "Selenium" in result.text, "搜索结果不包含预期文本"# 参数化测试:测试多个搜索关键词
@pytest.mark.parametrize("keyword", ["Python", "Java", "JavaScript"])
def test_search_parametrized(driver, keyword):wait = WebDriverWait(driver, 10)# 输入搜索内容search_box = wait.until(EC.element_to_be_clickable((By.ID, "kw")))search_box.clear()search_box.send_keys(keyword)# 点击搜索driver.find_element(By.ID, "su").click()# 验证结果result = wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="1"]/h3/a')))assert keyword in result.text, f"搜索{keyword}的结果不包含预期文本"

运行测试:

bash

pytest test_baidu.py -v  # -v 显示详细信息

7.3 生成测试报告

使用 pytest-html 插件可以生成美观的 HTML 测试报告:

安装插件:

bash

pip install pytest-html

运行测试并生成报告:

bash

pytest test_baidu.py -v --html=report.html

运行完成后,会生成 report.html 文件,包含测试结果、执行时间、错误信息等详细内容。

八、网页数据爬取实战

Selenium 不仅用于自动化测试,也是网页爬取的重要工具,特别适合处理 JavaScript 动态渲染的页面。下面通过几个实战案例展示 Selenium 在数据爬取中的应用。

8.1 爬取静态网页数据

以爬取豆瓣电影 Top250 为例:

python

运行

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 webdriver_manager.chrome import ChromeDriverManager
import time
import csvdef crawl_douban_top250():# 初始化浏览器driver = webdriver.Chrome(ChromeDriverManager().install())driver.maximize_window()wait = WebDriverWait(driver, 10)# 存储结果movies = []try:# 爬取10页数据for page in range(10):url = f"https://movie.douban.com/top250?start={page*25}&filter="driver.get(url)print(f"正在爬取第{page+1}页:{url}")# 等待页面加载完成wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".grid_view li")))# 获取当前页的电影列表movie_list = driver.find_elements(By.CSS_SELECTOR, ".grid_view li")for movie in movie_list:# 提取电影信息title = movie.find_element(By.CSS_SELECTOR, ".title").textrating = movie.find_element(By.CSS_SELECTOR, ".rating_num").textinfo = movie.find_element(By.CSS_SELECTOR, ".bd p:first-child").textquote = movie.find_element(By.CSS_SELECTOR, ".inq").text if movie.find_elements(By.CSS_SELECTOR, ".inq") else ""movies.append({"排名": len(movies) + 1,"标题": title,"评分": rating,"信息": info,"引言": quote})time.sleep(2)  # 避免爬取过快被反爬# 保存数据到CSV文件with open("douban_top250.csv", "w", encoding="utf-8-sig", newline="") as f:fieldnames = ["排名", "标题", "评分", "信息", "引言"]writer = csv.DictWriter(f, fieldnames=fieldnames)writer.writeheader()writer.writerows(movies)print(f"爬取完成,共{len(movies)}部电影,已保存到douban_top250.csv")except Exception as e:print(f"爬取过程中出错:{e}")finally:driver.quit()if __name__ == "__main__":crawl_douban_top250()

8.2 爬取动态加载数据

很多网站采用滚动加载或点击加载更多的方式加载数据,如 Twitter、知乎等。下面以爬取滚动加载的页面为例:

python

运行

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 webdriver_manager.chrome import ChromeDriverManager
import time
import csv
import randomdef crawl_scroll_page():# 初始化浏览器driver = webdriver.Chrome(ChromeDriverManager().install())driver.maximize_window()wait = WebDriverWait(driver, 10)# 访问目标页面(示例:知乎话题下的回答)url = "https://www.zhihu.com/topic/19552832/top-answers"driver.get(url)print(f"访问页面:{url}")# 存储结果answers = []try:# 等待页面加载wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".List-item")))# 滚动次数(根据需要调整)scroll_count = 10last_height = driver.execute_script("return document.body.scrollHeight")for i in range(scroll_count):print(f"第{i+1}次滚动")# 滚动到页面底部driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")# 随机等待2-5秒,模拟人类行为sleep_time = random.uniform(2, 5)print(f"等待{sleep_time:.2f}秒")time.sleep(sleep_time)# 计算新的页面高度并与上一次比较new_height = driver.execute_script("return document.body.scrollHeight")if new_height == last_height:print("页面已滚动到底部,没有更多内容")breaklast_height = new_height# 提取所有回答answer_elements = driver.find_elements(By.CSS_SELECTOR, ".List-item")print(f"共找到{len(answer_elements)}个回答")for idx, element in enumerate(answer_elements):try:# 提取回答者author = element.find_element(By.CSS_SELECTOR, ".UserLink-link").text# 提取回答内容(简化版)content = element.find_element(By.CSS_SELECTOR, ".RichText").text[:200]  # 只取前200字# 提取点赞数vote = element.find_element(By.CSS_SELECTOR, ".VoteButton-count").textanswers.append({"序号": idx + 1,"作者": author,"点赞数": vote,"内容": content})except Exception as e:print(f"提取第{idx+1}个回答时出错:{e}")continue# 保存数据with open("zhihu_answers.csv", "w", encoding="utf-8-sig", newline="") as f:fieldnames = ["序号", "作者", "点赞数", "内容"]writer = csv.DictWriter(f, fieldnames=fieldnames)writer.writeheader()writer.writerows(answers)print(f"爬取完成,共{len(answers)}个回答,已保存到zhihu_answers.csv")except Exception as e:print(f"爬取过程中出错:{e}")finally:driver.quit()if __name__ == "__main__":crawl_scroll_page()

8.3 处理登录验证

很多网站需要登录才能访问完整内容,Selenium 可以模拟登录过程:

python

运行

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 webdriver_manager.chrome import ChromeDriverManager
import time
import getpass  # 用于安全输入密码def simulate_login():# 初始化浏览器driver = webdriver.Chrome(ChromeDriverManager().install())driver.maximize_window()wait = WebDriverWait(driver, 10)try:# 访问登录页面(示例:GitHub登录)driver.get("https://github.com/login")print("已打开GitHub登录页面")# 输入用户名username_input = wait.until(EC.element_to_be_clickable((By.ID, "login_field")))username = input("请输入GitHub用户名:")username_input.send_keys(username)# 输入密码password_input = driver.find_element(By.ID, "password")password = getpass.getpass("请输入GitHub密码:")  # 密码不显示在终端password_input.send_keys(password)# 点击登录按钮login_button = driver.find_element(By.NAME, "commit")login_button.click()time.sleep(3)# 验证登录是否成功(检查是否跳转到首页)try:wait.until(EC.url_contains("github.com"))print("登录成功!")# 登录后的操作(例如访问个人主页)driver.get(f"https://github.com/{username}")wait.until(EC.title_contains(username))print(f"已进入{username}的个人主页")time.sleep(5)except Exception as e:print("登录失败:", e)except Exception as e:print(f"登录过程中出错:{e}")finally:driver.quit()if __name__ == "__main__":simulate_login()

注意:自动化登录可能违反某些网站的用户协议,且可能触发验证码等反爬机制。在实际应用中,请遵守网站的 robots 协议和使用条款。

九、Selenium 性能优化与最佳实践

9.1 性能优化技巧

  1. 使用无头浏览器:在不需要可视化界面的场景(如服务器环境),使用无头模式可以显著提高性能

    python

    运行

    from selenium import webdriver
    from selenium.webdriver.chrome.options import Optionschrome_options = Options()
    chrome_options.add_argument("--headless=new")  # 无头模式
    chrome_options.add_argument("--disable-gpu")  # 禁用GPU加速
    driver = webdriver.Chrome(options=chrome_options)
    
  2. 禁用不必要的功能:关闭图片加载、JavaScript 等非必要功能(根据需求)

    python

    运行

    chrome_options = Options()
    # 禁用图片加载
    prefs = {"profile.managed_default_content_settings.images": 2}
    chrome_options.add_experimental_option("prefs", prefs)
    # 禁用JavaScript
    chrome_options.add_argument("--disable-javascript")
    
  3. 减少等待时间:合理设置等待时间,避免不必要的等待

  4. 复用浏览器实例:在多个测试用例或爬取任务中复用同一个浏览器实例,减少启动开销

  5. 使用更快的选择器:优先使用 ID、NAME 等快速定位方式,CSS 选择器性能通常优于 XPath

  6. 批量操作:使用 JavaScript 执行批量操作,减少与浏览器的交互次数

    python

    运行

    # 批量设置多个元素的文本
    js_code = """
    var elements = document.getElementsByClassName('item');
    for(var i=0; i<elements.length; i++){elements[i].textContent = 'new text';
    }
    """
    driver.execute_script(js_code)
    

9.2 最佳实践

  1. 元素定位策略

    • 优先使用 ID,其次是 NAME、CLASS
    • 复杂场景使用相对 XPath 或 CSS 选择器
    • 避免使用绝对路径和动态生成的属性
    • 为测试环境的元素添加稳定的测试 ID
  2. 代码组织

    • 使用 Page Object 模式封装页面操作,提高代码复用性和可维护性
    • 将配置信息(如 URL、等待时间)与业务逻辑分离
    • 异常处理:捕获并处理可能的异常(如元素未找到、超时等)
  3. 反爬与合规

    • 遵守网站的 robots 协议和使用条款
    • 模拟人类行为(合理的等待时间、随机操作间隔)
    • 避免频繁请求,设置请求间隔
    • 必要时使用代理 IP 和 User-Agent 池
  4. 测试实践

    • 保持测试用例的独立性,每个用例应能单独运行
    • 测试数据与测试代码分离
    • 定期维护测试用例,适应页面变化
    • 结合 CI/CD 工具(如 Jenkins)实现持续测试
  5. 调试技巧

    • 使用driver.save_screenshot()在关键步骤截图,便于调试
    • 利用print()或日志输出关键信息
    • 使用浏览器开发者工具分析页面结构和网络请求

十、总结与展望

Selenium 作为一款成熟的 Web 自动化工具,已经成为测试工程师和数据爬取爱好者的必备技能。它强大的浏览器控制能力、跨平台跨浏览器的特性以及丰富的 API,使其能够应对各种复杂的 Web 自动化场景。

本文从 Selenium 的基本概念出发,详细介绍了环境搭建、元素定位、交互操作、浏览器控制、等待机制等核心内容,并通过大量代码示例展示了 Selenium 在自动化测试和网页爬取中的应用。同时,我们也探讨了性能优化和最佳实践,帮助读者编写更高效、更稳定的自动化脚本。

随着 Web 技术的不断发展,Selenium 也在持续进化。未来,Selenium 将更好地支持现代 Web 框架(如 React、Vue)和新兴浏览器特性,同时在性能和易用性上不断提升。掌握 Selenium 不仅能够提高工作效率,更能为从事测试开发、数据采集等领域的工作打下坚实基础。

无论是自动化测试工程师、数据分析师还是开发人员,都可以通过 Selenium 打开 Web 自动化的大门,探索更多可能性。希望本文能够帮助你快速入门并精通 Selenium,让自动化技术为你的工作带来更多价值。

http://www.dtcms.com/a/321879.html

相关文章:

  • RSA非对称加密
  • 除了腾讯会议,私有化有哪些选择?
  • 安科瑞EMS3.0源网荷储一体化解决方案 全面助力零碳园区建设
  • FreeSWITCH parse-all-invite-headers
  • 记一次lombok链式调用引发EasyExcel兼容性的问题
  • 记录网站突然报错503
  • 第六章第四节 PWM驱动LED呼吸灯 PWM驱动舵机 PWM驱动直流电机
  • 计算机网络:到底什么是可变长子网掩码VLSM?
  • win11中Qt5.14.0+msvc2019+opencv4.9配置
  • 全方位无限随机地图实现指南
  • 模块 PCB 技术在未来通信领域的创新突破方向
  • Docker 创建镜像错误记录
  • Java技术栈/面试题合集(21)-Docker篇
  • 如何动态执行 JS 脚本
  • 揭秘Java synchronize:轻量级锁升级与偏向锁
  • Java-注解
  • 重新 mybatis plus 的 撒着OrUpdate 方法,实现根据自定义字段插入或者修改
  • P1044 [NOIP 2003 普及组] 栈
  • B4263 [GESP202503 四级] 荒地开垦 题解
  • 【工作笔记】Docker Desktop一直转圈加载不出来然后报错
  • 提升LLM服务效率的秘密武器——vLLM!
  • Docker 安装 Redis
  • 机柜中不同类型板卡的操作系统配置情况一览
  • 解决苍穹外卖项目中 MyBatis - Plus 版本冲突问题
  • 【Linux运维】深入理解Cookie与Session机制:安全性与性能的平衡艺术
  • SAP接口日志查询
  • 多级缓存架构:新品咖啡上线引发的数据库压力风暴与高并发实战化解方案
  • 数据返回后需要刷新才会展示的解决方法
  • Vue3 组合式API
  • 飞算JavaAI深度解析:专为Java生态而生的智能引擎