目录:
-
开始前:环境与 "第一屏代码"(图片 2 的启动代码)
-
元素定位:cssSelector 与 xpath
-
操作测试对象:click、send_keys、clear、取文本
-
获取页面信息:title 与 URL
-
窗口:句柄、切换、大小、截图、关闭
-
等待:自动化测试的灵魂(强制 / 隐式 / 显式 / 混合实验)
-
浏览器导航:get 与 navigate
-
弹窗:Alert 接口
-
文件上传:send_keys 传路径
-
浏览器参数:无头模式与加载策略
-
本次实操踩坑实录(5 个真实坑)
1. 开始前:环境与"第一屏代码"
课件所有函数都挂在一个 driver 对象上,先看懂它,后面 everything 才有落脚点。
python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
# 1、打开浏览器
# 让 Selenium 自动匹配已安装的 Chrome 版本,并自动下载对应驱动,无需手动处理驱动
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
driver.maximize_window() # 最大化窗口
time.sleep(2)
# 2、输入百度网址
driver.get("https://www.baidu.com")
time.sleep(3) # 多等几秒,让页面完全加载
# 3、输入关键词"迪丽热巴"
# 新版首页可见的搜索框是 div#chat-input-area 容器里的 textarea(id=chat-textarea),
# 旧版隐藏的 #kw 已不可交互,这里直接对可见元素原生输入
print("正在输入关键词...")
box = driver.find_element(By.ID, "chat-textarea") # 等价 xpath://*[@id="chat-input-area"]//textarea
box.send_keys("迪丽热巴")
time.sleep(1)
# 4、点击"百度一下"按钮
# 新版首页可见按钮是 button#chat-submit-button,旧版隐藏的 #su 已废弃
print("正在点击搜索...")
btn = driver.find_element(By.ID, "chat-submit-button") # 等价 xpath://*[@id="chat-submit-button"]
btn.click()
time.sleep(20) # 留出时间查看采集结果
# 查看提交后落到哪个页面(搜索结果页 or 百度安全验证页)
print("跳转后网址 =", driver.current_url[:80])
print("跳转后标题 =", driver.title)
# 5、关闭浏览器
print("执行完毕,关闭浏览器...")
driver.quit()

2. 元素定位:cssSelector 与 xpath
课Web 自动化测试的操作核心是能够找到页面对应的元素,然后才能对元素进行具体的操作。定位方式必须唯一 。
web⾃动化测试的操作核⼼是能够找到⻚⾯对应的元素,然后才能对元素进⾏具体的操作。
常⻅的元素定位⽅式⾮常多,如id,classname,tagname,xpath,cssSelector
常⽤的主要由cssSelector和xpath
由于百度版本的更替可能会导致定位元素的名字发生了变化,但是底层逻辑不变
2.1 cssSelector(CSS 选择器)
两类:

2.2 xpath 六种语法

例说明索引从 1 开始**://div/ul/li[3] 取的是"第 3 个 li",不是程序员习惯的"下标 3(即第 4 个)"。这是 xpath 和 Python/Java 数组下标最大的区别,写错一位就定位到隔壁元素,是高频 bug。

2.3 课件的思考题:能直接右键 Copy selector,为什么还要学语法?
手动复制的表达式不一定唯一 。例如复制热搜标题得到的 #title-content,页面上每条热搜都有一个同 id 的节点,find_element 只会拿到第一个,find_elements 会拿到一堆------都不"唯一"。学语法是为了手动改表达式 ,把不唯一的定位改唯一(比如加上父级限定 #hotsearch-content-wrapper > li:nth-child(1) .title-content)。
我的补充见解 :复制来的 xpath 还经常是" fullPath 型"的(/html/body/div[2]/div/...),页面一改版就全废。手写"短 xpath"(//*[@id='kw'] 这类基于 id/文本的)抗改版能力强得多。
3. 操作测试对象:click、send_keys、clear、取文本
获取到了⻚⾯的元素之后,接下来就是要对元素进⾏操作了。常⻅的操作有点击、提交、输⼊、清除、获取⽂本。
3.1 点击/提交对象

python
#常见的自动化函数的使用
#1点击提交元素
import os # 读取环境变量控制后台运行
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 方式A:原生 click() 点击当前首页可见的"百度一下"按钮 ----------
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 等首屏渲染
box = driver.find_element(By.ID, "chat-textarea") # 定位当前首页可见搜索框(textarea)
box.send_keys("软件测试") # 先输入关键词,点击后才有意义
btn = driver.find_element(By.ID, "chat-submit-button") # 当前首页可见的"百度一下"按钮(button 标签)
assert btn.is_displayed(), "按钮不可见,click() 会报 element not interactable" # 点击前先断言可见
btn.click() # 原生点击:等价于课件 driver.findElement(...).click()
time.sleep(20) # 等待跳转完成
url_a = driver.current_url # 读取跳转后的网址
print("方式A 原生click 跳转后网址 =", url_a[:80])
assert url_a != "https://www.baidu.com/", "点击后网址未变化,说明点击未生效" # 断言点击确实触发了提交
# 说明:自动化会话可能落到百度安全验证页(wappass...),也属于"提交已触发",均为通过


3.2 模拟按键输⼊

python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 等首屏渲染
# ---------- 普通文本输入 ----------
box = driver.find_element(By.ID, "chat-textarea") # 当前首页可见搜索框(隐藏的 #kw 无法 send_keys)
box.send_keys("我爱学习") # send_keys:模拟键盘逐字输入
time.sleep(5)
val = box.get_attribute("value") # 读回输入框当前值用于断言
print("输入后 value =", val)
assert val == "我爱学习", "输入内容与预期不一致" # 断言输入成功

3.3 清除⽂本内容
输⼊⽂本后⼜想换⼀个新的关键词,这⾥就需要⽤到 clear()

python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 等首屏渲染
box = driver.find_element(By.ID, "chat-textarea") # 定位当前首页可见搜索框
# ---------- 4.1 先输入第一个关键词 ----------
box.send_keys("我爱游戏") # 课件原例第 1 步:输入旧关键词
time.sleep(2)
print("clear 前 value =", box.get_attribute("value"))
# ---------- 4.2 clear() 清空 ----------
box.clear() # clear:一键清空输入框全部文本
time.sleep(2)
val_empty = box.get_attribute("value") # 读回清空后的值
print("clear 后 value =", repr(val_empty))
assert val_empty == "", "clear() 未清空输入框" # 断言确实清空
# ---------- 4.3 再输入新关键词 ----------
box.send_keys("我爱学习") # 课件原例第 3 步:输入新关键词
time.sleep(2)
val_new = box.get_attribute("value") # 读回新值
print("重新输入后 value =", val_new)
assert val_new == "我爱学习", "重新输入失败" # 断言新关键词就位
driver.quit() # 关闭浏览器释放驱动



3.4 获取⽂本信息
如果判断获取到的元素对应的⽂本是否符合预期呢?获取元素对应的⽂本并打印⼀下~~

python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 等首屏渲染
# ---------- 5.1 获取节点文本 text(等价于课件 Java 的 getText()) ----------
title_span = driver.find_element(By.CSS_SELECTOR, "#hotsearch-content-wrapper > li:nth-child(4) span.title-content-title")
txt = title_span.text # .text:元素在页面上"看得见"的文字
print("第3条热搜文本 =", txt)
assert len(txt) > 0, "热搜文本为空" # 断言确实取到文本
# ---------- 5.2 获取属性值 get_attribute() ----------
su = driver.find_element(By.ID, "su") # 定位隐藏的"百度一下"按钮 #su
val = su.get_attribute("value") # get_attribute("value"):按钮文字在 value 属性里
print("#su 的 value 属性 =", val)
assert val == "百度一下", "属性值与预期不符" # 断言属性读取正确
# 注意:对 #su 用 .text 会得到空字符串------按钮文字是 value 属性而不是节点文本,这就是课件说的"文本和属性值不要混淆"
print("#su 的 .text =", repr(su.text))
kw = driver.find_element(By.ID, "kw") # 定位隐藏搜索框
print("#kw 的 name 属性 =", kw.get_attribute("name")) # 读取 name 属性(wd)
driver.quit() # 关闭浏览器释放驱动



- 获取页面信息:title 与 URL
4.1获取当前⻚⾯标题
getTitle()
python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 6a.1 读取百度首页的标题 ----------
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 等首屏渲染
t1 = driver.title # driver.title 等价课件 getTitle(),读 <title> 标签
print("首页标题 getTitle() =", t1)
assert t1 == "百度一下,你就知道", "首页标题不符" # 断言标题正确
# ---------- 6a.2 页面跳转后再读一次标题,观察变化 ----------
box = driver.find_element(By.ID, "chat-textarea") # 新版首页可见输入框
box.send_keys("软件测试")
btn = driver.find_element(By.ID, "chat-submit-button") # 新版首页可见按钮
btn.click() # 提交搜索触发页面跳转
time.sleep(20) # 等跳转完成
t2 = driver.title # 跳转后的标题
print("跳转后标题 getTitle() =", t2[:40])
assert t2 != t1, "跳转后标题毫无变化,getTitle() 未反映新页面" # 断言标题随页面变化
print("说明:getTitle() 读的是当前所在页面的标题,页面一换结果就跟着换")
driver.quit() # 关闭浏览器释放驱动

4.2获取当前⻚⾯URL
getCurrentUrl()
python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 6b.1 读取百度首页的 URL ----------
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 等首屏渲染
u1 = driver.current_url # driver.current_url 等价课件 getCurrentUrl()
print("首页 getCurrentUrl() =", u1)
assert u1.startswith("https://www.baidu.com"), "首页URL不符" # 断言 URL 正确
#---------- 6b.2 页面跳转后再读一次 URL,观察变化 ----------
box = driver.find_element(By.ID, "chat-textarea") # 新版首页可见输入框
box.send_keys("迪丽热巴")
btn = driver.find_element(By.ID, "chat-submit-button") # 新版首页可见按钮
btn.click() # 提交搜索触发页面跳转
time.sleep(20) # 等跳转完成
u2 = driver.current_url # 跳转后的 URL
print("跳转后 getCurrentUrl() =", u2[:80])
assert u2 != u1, "跳转后URL毫无变化,getCurrentUrl() 未反映新页面" # 断言 URL 随页面变化
print("说明:getCurrentUrl() 读的是当前所在页面的地址,跳到哪读哪")
print("(自动化会话提交后可能落到'百度安全验证'页,URL 已变化即说明读取正常)")
driver.quit() # 关闭浏览器释放驱动

5. 窗口:句柄、切换、大小、截图、关闭
为什么需要句柄
人能用眼睛看出"当前在哪个窗口",程序不能。每个窗口/标签页有一个唯一字符串标识,叫句柄(handle),程序靠它认窗口。
当我们⼿⼯测试的时候,我们可以通过眼睛来判断当前的窗⼝是什么,但对于程序来说它是不知道当
前最新的窗⼝应该是哪⼀个。对于程序来说它怎么来识别每⼀个窗⼝呢?每个浏览器窗⼝都有⼀个唯
⼀的属性句柄(handle)来表⽰,我们就可以通过句柄来切换

可以理解为多一个窗口就多一个句柄 窗口换句柄也会跟着更换
接下来的代码就可以很好的演示出来
python
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://www.baidu.com")
time.sleep(2)
# ---- 1) 获取当前页面句柄 getWindowHandle() ----
cur = driver.current_window_handle
print("== getWindowHandle() 获取当前页面句柄 ==")
print("当前句柄 =", cur)
# ---- 2) 获取所有页面句柄 getWindowHandles() ----
print("== getWindowHandles() 获取所有页面句柄 ==")
handles = driver.window_handles
print("句柄个数 =", len(handles), "列表 =", handles)
assert len(handles) == 1, "只开了一个标签页,句柄应只有 1 个"
print("PASS:只有一个标签页时,Handles 列表里就是当前这 1 个句柄")
# ---- 再开一个新标签页,观察句柄列表变化 ----
print("== 新开一个标签页后重新获取所有句柄 ==")
driver.switch_to.new_window("tab") # 开新标签页(句柄演示用)
handles2 = driver.window_handles
print("新标签页打开后句柄个数 =", len(handles2))
print("句柄列表 =", handles2)
print("当前句柄 =", driver.current_window_handle,
"(new_window 会自动切到新标签页,所以当前句柄变了)")
assert len(handles2) == 2, "两个标签页应对应 2 个句柄"
assert cur in handles2, "旧句柄仍然在列表里"
print("PASS:每多一个窗口就多一个唯一句柄,旧句柄不变")
print("示例07 运行结束:getWindowHandle / getWindowHandles 演示 PASS")
finally:
driver.quit()

5.1切换窗口
python
# 演示痛点
# a. 打开新页面后,不切换句柄,getTitle/getCurrentUrl 读到的还是旧页面;
# b. 去掉等待后,获取跳转后页面的元素会失败 -> 配上 WebDriverWait 才稳定。
import time
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
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://www.baidu.com")
time.sleep(2)
cur = driver.current_window_handle
print("== 停留页(旧窗口)==")
print("旧窗口句柄 =", cur)
print("旧窗口标题 =", driver.title)
# ---- 制造一个新窗口:点首页热搜链接(target=_blank 会开新标签页)----
opened = False
try:
link = driver.find_element(By.CSS_SELECTOR, "#hotsearch-content-wrapper li a")
driver.execute_script("arguments[0].click();", link) # JS 点击,避开遮挡
for _ in range(10):
if len(driver.window_handles) > 1:
opened = True
break
time.sleep(0.5)
except Exception as e:
print("热搜链接点击失败,改用 new_window 兜底:", type(e).__name__)
if not opened:
driver.switch_to.new_window("tab")
driver.get("https://example.com")
# 统一站回旧窗口,才能演示"不切换就读不到新页面"
driver.switch_to.window(cur)
time.sleep(1)
print("== 痛点a:新窗口已打开,但句柄没切换 ==")
print("当前句柄仍是 =", driver.current_window_handle)
print("此时 getTitle() 读到的还是旧页面:", driver.title)
print("句柄列表已有", len(driver.window_handles), "个:", driver.window_handles)
# ---- 课件同款循环:切换到"不是当前句柄"的那个窗口(即最新页面)----
print("== switchTo().window(w) 切换到最新页面 ==")
all_window = driver.window_handles
time.sleep(20)
for w in all_window:
if w != cur:
driver.switch_to.window(w)
print("切换后句柄 =", driver.current_window_handle)
print("切换后标题 =", driver.title[:50])
print("切换后URL =", driver.current_url[:70])
assert driver.current_window_handle != cur, "应已切到新窗口"
# ---- 痛点b:去掉等待取元素会失败,配 WebDriverWait 才稳定 ----
print("== 痛点b:等待 vs 不等待 取新页面元素 ==")
try:
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.TAG_NAME, "body")))
print("带 WebDriverWait 取 body 元素:成功(稳定写法)")
except Exception as e:
print("带等待仍失败:", type(e).__name__)
print("说明:课件提醒'去掉等待后获取跳转后的页面元素失败',"
"慢页面上不等就直接 find_element 会抛 NoSuchElementException")
# ---- 切回旧窗口,验证标题又变回去 ----
driver.switch_to.window(cur)
print("== 切回旧窗口 ==")
print("切回后标题 =", driver.title)
assert driver.title.startswith("百度"), "切回后应回到百度首页标题"
print("示例08 运行结束:switchTo().window() 切换窗口演示 PASS")
finally:
driver.quit()
5.2窗口设置大小
python
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://www.baidu.com")
time.sleep(2)
def show(tag):
size = driver.get_window_size()
print(f"{tag} -> 宽={size['width']} 高={size['height']}")
return size
print("== §3.2 窗口设置大小,逐个函数演示 ==")
show("初始窗口")
# 1) 窗口最大化
driver.maximize_window()
s_max = show("maximize() 最大化后")
time.sleep(2 )
# 2) 手动设置窗口大小(课件 setSize(new Dimension(1024, 768)))
driver.set_window_size(1024, 768)
time.sleep(2)
s_set = show("setSize(1024, 768) 后")
assert s_set["width"] == 1024 and s_set["height"] == 768, "setSize 应精确生效"
print("PASS:setSize 后 get_window_size 读回正是 1024x768")
# 3) 窗口最小化(无头环境下可能不改变尺寸,只演示调用不报错) 无法演示
try:
driver.minimize_window()
time.sleep(2)
show("minimize() 最小化后")
print("PASS:minimize() 调用成功")
except Exception as e:
print("minimize() 在当前环境不支持:", type(e).__name__)
# 4) 全屏窗口
try:
driver.fullscreen_window()
time.sleep(2)
s_full = show("fullscreen() 全屏后")
print("PASS:fullscreen() 调用成功,全屏尺寸 >= 最大化尺寸:",
s_full["width"] >= s_max["width"])
except Exception as e:
print("fullscreen() 在当前环境不支持:", type(e).__name__)
# 再设回一个小尺寸,肉眼/读回都能确认"手动设置"生效
driver.set_window_size(800, 600)
time.sleep(2)
show("再次 setSize(800, 600) 后")
print("示例09 运行结束:窗口设置大小四个函数演示 PASS")
finally:
driver.quit()





5.3 屏幕截图
我们的⾃动化脚本⼀般部署在机器上⾃动的去运⾏,如果出现了报错,我们是不知道的,可以通过抓拍来记录当时的错误场景
python
# Python 对应(一步到位,无需 commons-io):
# driver.get_screenshot_as_file(路径) -> 直接存成 png 文件
# driver.get_screenshot_as_png -> 返回 png 字节串(备选)
# 用途:脚本自动跑报错时,抓拍当时的错误场景留证。
# 运行方式:直接运行=有头;设环境变量 DEMO_HEADLESS=1=无头
# =====================================================================
import os
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://www.baidu.com")
time.sleep(2)
base = os.path.join(os.path.dirname(os.path.abspath(__file__)), "截图记录")
os.makedirs(base, exist_ok=True)
# ---- 简单版本:固定文件名 my.png ---- 用第一种方法截屏不能保存同名的文件 如果我们想保存另外的还需要手动
print("== 简单版本:getScreenshotAs(FILE) 存成 my.png ==")
p1 = os.path.join(base, "my.png2") #这里原来是"my.png"被我改成"my.png2"才能保存另外的一张
driver.get_screenshot_as_file(p1)
print("已保存:", p1)
print("文件大小 =", os.path.getsize(p1), "字节")
assert os.path.getsize(p1) > 0, "截图文件不应为空"
print("PASS:简单版本截图成功")
finally:
driver.quit()

5.4关闭窗口
python
import os
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://www.baidu.com")
time.sleep(2)
base = os.path.join(os.path.dirname(os.path.abspath(__file__)), "截图记录")
os.makedirs(base, exist_ok=True)
# ---- 高阶版本:日期文件夹 + 时间戳文件名(课件同款思路)----
print("== 高阶版本:autotest-日期/名字-时分秒.png ==")
day = time.strftime("%Y-%m-%d") # 对应课件 times.get(0)
hms = time.strftime("%H%M%S") # 对应课件 times.get(1)
folder = os.path.join(base, "autotest-" + day)
os.makedirs(folder, exist_ok=True)
p2 = os.path.join(folder, "baidu_homepage-" + hms + ".png")
driver.get_screenshot_as_file(p2)
print("已保存:", p2)
print("文件大小 =", os.path.getsize(p2), "字节")
assert os.path.getsize(p2) > 0, "截图文件不应为空"
print("PASS:高阶版本截图成功(每次运行文件名不重复,便于留证)")
# ---- 备选:拿 png 字节串,不落地文件 ----
png_bytes = driver.get_screenshot_as_png() # 注意带括号:是方法不是属性
print("== 备选:get_screenshot_as_png 返回字节数 =", len(png_bytes))
print("示例10 运行结束:屏幕截图演示 PASS,截图在", base)
finally:
driver.quit()
而这里的自动化截图以时间为依托 我就可以截取不同时间的信息了更高效和方便

python
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
try:
driver.get("https://www.baidu.com")
time.sleep(2)
cur = driver.current_window_handle
print("== 准备两个窗口 ==")
print("旧窗口句柄 =", cur, "标题 =", driver.title)
driver.switch_to.new_window("tab") # 开第二个标签页
new = driver.current_window_handle
driver.get("https://www.baidu.com")
time.sleep(1)
print("新窗口句柄 =", new)
print("当前句柄个数 =", len(driver.window_handles))
# ---- 1) close() 关闭"当前"窗口(新标签页)----
print("== close() 关闭当前窗口(新标签页)==")
driver.close()
time.sleep(10)
print("close 后剩余句柄 =", driver.window_handles)
assert len(driver.window_handles) == 1, "close 只关一个窗口,应剩 1 个"
print("PASS:close() 只关当前窗口,另一个窗口还在")
# ---- 2) 注意a:close 后先切到未被关闭的窗口 ----
try:
print("close 后不切换直接读标题:", driver.title)
except Exception as e:
print("close 后不切换就操作,报错:", type(e).__name__,
"(句柄还停在已关闭的窗口上)")
driver.switch_to.window(cur) # 注意:切到未被关闭的窗口
print("切回未关闭的旧窗口后,标题 =", driver.title)
print("PASS:切换后可正常继续操作")
# ---- 3) 注意b:关掉最后一个窗口,driver 失效需重新定义 ----
print("== 关闭最后一个窗口,验证 driver 失效 ==")
driver.close()
try:
driver.title
print("意外:会话居然还可用")
except Exception as e:
print("所有窗口关闭后 driver 已失效:", type(e).__name__)
print("按课件注意重新定义 driver ...")
driver = webdriver.Chrome(service=service) # 重新定义
driver.get("https://www.baidu.com")
time.sleep(1)
print("新 driver 标题 =", driver.title)
print("PASS:重新定义后 driver 恢复可用")
print("示例11 运行结束:close() 关闭窗口演示 PASS")
finally:
try:
driver.quit()
except Exception:
pass

6.等待
通常代码执⾏的速度⽐⻚⾯渲染的速度要快,如果避免因为渲染过慢出现的⾃动化误报的问题呢?可以使⽤selenium中提供的三种等待⽅法:
6.1 强制等待
Thread.sleep()
优点:使⽤简单,调试的时候⽐较有效
缺点:影响运⾏效率,浪费⼤量的时间
python
import time # 本示例主角:time.sleep 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.chrome.options import Options # Chrome 启动参数
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
t0 = time.time() # 记录开始时间,用于量化"浪费"
driver.get("https://www.baidu.com") # 打开百度首页
time.sleep(2) # 强制等待 2 秒:不管页面是否早已渲染好,都死等
box = driver.find_element(By.ID, "chat-textarea") # 定位可见搜索框
box.send_keys("强制等待演示") # 输入关键词
time.sleep(1) # 再死等 1 秒(调试时常用,生产脚本应改智能等待)
print("两段 sleep 总耗时 =", round(time.time() - t0, 2), "秒(含页面加载)")
assert box.get_attribute("value") == "强制等待演示", "输入失败" # 断言等待期间输入已生效
driver.quit() # 关闭浏览器释放驱动
print("示例11 运行结束:强制等待 sleep 演示 PASS")

6.2 隐式等待
隐式等待是⼀种智能等待,他可以规定在查找元素时,在指定时间内不断查找元素。如果找到则代码
继续执⾏,直到超时没找到元素才会报错。

隐式等待作⽤域是整个脚本的所有元素 。即只要driver对象没有被释放掉(driver.quit()),隐式等待就⼀直⽣效。
优点:智能等待,作⽤于全局
python
import time # 计时用
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.chrome.options import Options # Chrome 启动参数
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 12.1 设置隐式等待 5 秒(全局生效) ----------
driver.implicitly_wait(5) # 隐式等待:找不到元素时最多重试 5 秒再报错
driver.get("https://www.baidu.com") # 打开百度首页(不再需要 sleep)
box = driver.find_element(By.ID, "chat-textarea") # 隐式等待下:元素一出现立即返回,不必死等
print("隐式等待下定位搜索框成功,tag =", box.tag_name)
# ---------- 12.2 验证"找不到时等满超时才报错" ----------
t0 = time.time() # 开始计时
try:
driver.find_element(By.ID, "not-exist-element-xyz") # 故意找一个不存在的元素
except Exception as e: # 5 秒后抛 NoSuchElementException
cost = time.time() - t0 # 实际等待时长
print("不存在元素报错耗时 =", round(cost, 2), "秒,异常类型 =", type(e).__name__)
assert 4.5 <= cost <= 6.5, "隐式等待时长不符合 5 秒设定" # 断言确实等满了 5 秒
driver.quit() # 关闭浏览器释放驱动

6.3 显⽰等待
显⽰等待也是⼀种智能等待,在指定超时时间范围内只要满⾜操作的条件就会继续执⾏后续代码
new WebDriverWait(driver, Duration.ofSeconds(3)).until($express)
$press:涉及到selenium.support.ui.ExpectedConditions包下的ExpectedConditions类
返回值:boolean

优点:显⽰等待是智能等待,可以⾃定义显⽰等待的条件,操作灵活
缺点:写法复杂
python
import time # 计时用
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
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 # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 13.1 element_to_be_clickable:等到元素可见且可点击 ----------
driver.get("https://www.baidu.com") # 打开百度首页
wait = WebDriverWait(driver, 10) # 课件 WebDriverWait(driver, Duration.ofSeconds(10))
btn = wait.until(EC.element_to_be_clickable((By.ID, "chat-submit-button"))) # 等到按钮可点击才返回该元素
print("element_to_be_clickable 命中按钮 =", btn.tag_name, btn.get_attribute("id"))
# ---------- 13.2 presence_of_element_located:DOM 中存在即通过 ----------
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#hotsearch-content-wrapper > li"))) # 热搜出现
print("presence_of_element_located 通过:热搜列表已进入 DOM")
# ---------- 13.3 text_to_be_present_in_element:课件 textToBe 的 Python 等价 ----------
ok = wait.until(EC.text_to_be_present_in_element(
(By.CSS_SELECTOR, "#hotsearch-content-wrapper > li:nth-child(3) span.title-content-title"), "")) # 文本非空即满足包含关系
# 更贴近课件的用法:断言按钮文案
ok2 = wait.until(EC.text_to_be_present_in_element((By.ID, "chat-submit-button"), "百度一下"))
print("text_to_be_present_in_element 返回值 =", ok, ok2)
assert ok2, "按钮文本条件未满足"
# ---------- 13.4 url_to_be:等待跳转到指定 URL ----------
driver.execute_script("document.getElementById('kw').value='软件测试'") # JS 赋值隐藏搜索框
driver.execute_script("document.getElementById('su').click()") # JS 提交
try:
wait.until(EC.url_contains("baidu.com")) # 等待 URL 含 baidu.com(结果页或验证页都满足)
print("url 条件通过,当前 =", driver.current_url[:70])
except Exception as e: # 超时则打印而非崩溃
print("url 条件超时:", str(e)[:80])
# ---------- 13.5 条件不满足时 until 会抛 TimeoutException ----------
t0 = time.time() # 计时
try:
WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.ID, "no-such-node"))) # 3 秒内必失败
except Exception as e: # 捕获超时异常
print("3 秒显式等待超时耗时 =", round(time.time() - t0, 2), "秒,异常 =", type(e).__name__)
driver.quit() # 关闭浏览器释放驱动

那么这里如果显示和隐式等待一起使用呢?会出现什么情况吗?
python
import os
import time
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
driver_path = ChromeDriverManager().install()
driver = webdriver.Chrome(service=Service(driver_path))
# 本地练习页:一个元素 3 秒后才插入 DOM
html = os.path.join(os.path.dirname(os.path.abspath(__file__)), "练习页_等待.html")
with open(html, "w", encoding="utf-8") as f:
f.write("<html><head><meta charset='utf-8'><title>等待练习页</title></head><body>"
"<div id='now'>立即出现的元素</div>"
"<script>setTimeout(function(){var d=document.createElement('div');"
"d.id='late';d.innerText='延迟3秒才出现的元素';"
"document.body.appendChild(d);},3000);</script></body></html>")
try:
# ---------- 课件同款配置:隐式 5s + 显式 10s ----------
driver.implicitly_wait(5) # 隐式等待 5 秒(全局)
wait = WebDriverWait(driver, 10) # 显式等待 10 秒
# ---------- 实验1:元素延迟 3 秒出现 ----------
driver.get("file:///" + html.replace("\\", "/"))
t0 = time.time()
el = wait.until(EC.presence_of_element_located((By.ID, "late")))
cost1 = time.time() - t0
print("实验1 隐式5s+显式10s 等延迟3秒元素,耗时 = %.1fs" % cost1,
",元素文本 =", el.text)
assert cost1 < 10, "元素3秒就出现了,不该等到接近超时"
# ---------- 实验2:元素不存在,看超时耗时 ----------
t0 = time.time()
try:
wait.until(EC.presence_of_element_located((By.ID, "not-exist")))
except Exception as e:
cost2 = time.time() - t0
print("实验2 隐式5s+显式10s 等不存在元素,超时耗时 = %.1fs" % cost2,
",异常 =", type(e).__name__)
print("课件预期:耗时在 10s、11s 左右浮动,而不是 5+10=15s")
assert cost2 < 15, "混用下耗时应小于简单相加的 15 秒"
driver.implicitly_wait(0) # 复位,避免影响后续脚本习惯
print("结论:不要混合隐式和显式等待,可能会导致不可预测的等待时间。")
print("示例13b 运行结束:隐式+显式混用实验 PASS")
finally:
driver.quit()

结果:重试多次,最终打印的等待时间有10s、11s...
结论:不要混合隐式和显式等待,可能会导致不可预测的等待时间。
7.浏览器导航
7.1打开网址
python
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.chrome.options import Options # Chrome 启动参数
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 14.1 用简洁方法 get() 打开第一个网站 ----------
driver.get("https://www.baidu.com") # 课件简洁写法 driver.get(...)
time.sleep(2) # 等首屏渲染
print("get('https://www.baidu.com') 后:")
print(" title =", driver.title)
print(" url =", driver.current_url)
assert driver.title == "百度一下,你就知道", "第一个网站未打开" # 断言打开成功
# ---------- 14.2 再打开第二个网站(等价于课件 navigate().to(...)) ----------
print("Python 是否有 navigate() 对象:", hasattr(driver, "navigate"),
"(False 说明 get() 就是 to() 的等价写法)")
driver.get("https://example.com") # 等价课件 navigate().to(...)
time.sleep(2) # 等首屏渲染
print("get('https://example.com')(= navigate().to())后:")
print(" title =", driver.title)
print(" url =", driver.current_url)
assert "example" in driver.current_url, "第二个网站未打开" # 断言跳转成功
print("PASS:get() 与 navigate().to() 等价,打开谁 title/url 就跟到谁")
driver.quit() # 关闭浏览器释放驱动
print("示例14 运行结束:打开网站演示 PASS")

7.2浏览器的前进、后退、刷新

python
import os # 环境变量/路径处理
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
base = os.path.dirname(os.path.abspath(__file__)) # 脚本所在目录
pa = os.path.join(base, "练习页_导航A.html") # 练习页 A
pb = os.path.join(base, "练习页_导航B.html") # 练习页 B(带加载时间戳)
with open(pa, "w", encoding="utf-8") as f:
f.write("<html><head><meta charset='utf-8'><title>导航页A</title></head>"
"<body><h1>这是页面 A</h1></body></html>")
with open(pb, "w", encoding="utf-8") as f:
f.write("<html><head><meta charset='utf-8'><title>导航页B</title></head>"
"<body><h1>这是页面 B</h1><div id='ts'></div>"
"<script>document.getElementById('ts').innerText="
"'加载时间戳:' + Date.now();</script></body></html>")
def uri(p):
return "file:///" + p.replace("\\", "/")
try:
# ---------- 造浏览历史:A -> B ----------
driver.get(uri(pa)) # 历史记录第 1 条:页面 A
time.sleep(2)
print("打开页面A:title =", driver.title)
driver.get(uri(pb)) # 历史记录第 2 条:页面 B
time.sleep(2)
ts1 = driver.find_element(By.ID, "ts").text # B 页第一次加载的时间戳
print("打开页面B:title =", driver.title, "|", ts1)
# ---------- 1) 后退 back()(课件 navigate().back()) ----------
driver.back() # 后退到上一页
time.sleep(2)
print("back() 后退后:title =", driver.title, "| url 结尾 =",
driver.current_url.split("/")[-1])
assert driver.title == "导航页A", "back() 应回到页面 A" # 断言后退成功
# ---------- 2) 前进 forward()(课件 navigate().forward()) ----------
driver.forward() # 前进到下一页
time.sleep(2)
print("forward() 前进后:title =", driver.title, "| url 结尾 =",
driver.current_url.split("/")[-1])
assert driver.title == "导航页B", "forward() 应回到页面 B" # 断言前进成功
# ---------- 3) 刷新 refresh()(课件 navigate().refresh()) ----------
ts2 = driver.find_element(By.ID, "ts").text # 刷新前读一次时间戳
driver.refresh() # 重新加载当前页
time.sleep(2)
ts3 = driver.find_element(By.ID, "ts").text # 刷新后读时间戳
print("refresh() 刷新前:", ts2)
print("refresh() 刷新后:", ts3)
assert driver.title == "导航页B", "刷新后应仍是页面 B" # 断言页面没跑偏
assert ts3 != ts2, "时间戳没变,说明 refresh 没有真正重新加载" # 断言确实重新加载
print("PASS:时间戳变化证明 refresh() 重新加载了页面")
print("示例14b 运行结束:back / forward / refresh 演示 PASS")
finally:
driver.quit() # 关闭浏览器释放驱动

8.弹窗
弹窗是在⻚⾯是找不到任何元素的,这种情况怎么处理?使⽤selenium提供的Alert接⼝

8.1警告弹窗+确认弹窗
python
import os # 环境变量 + 写本地演示页
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 本地演示页:警告按钮 + 确认按钮 + 结果区 ----------
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "练习页_弹窗61.html")
with open(html_path, "w", encoding="utf-8") as f:
f.write("<html><head><meta charset='utf-8'><title>弹窗61练习页</title></head><body>"
"<button id='b1' onclick=\"alert('此网页显示 hello!')\">警告弹窗</button>"
"<button id='b2' onclick=\"document.getElementById('r').innerText = "
"confirm('此网页显示 Press a button') ? '点了确定' : '点了取消'\">确认弹窗</button>"
"<div id='r'></div></body></html>")
driver.get("file:///" + html_path.replace("\\", "/")) # 打开本地演示页
time.sleep(1) # 等渲染
# ---------- 15.1 警告弹窗:switchTo().alert() + accept() ----------
driver.find_element(By.ID, "b1").click() # 点击触发警告弹窗
alert = driver.switch_to.alert # 课件:switchTo().alert() 切到弹窗对象
time.sleep(5) # 等页面回调写结果
print("== 警告弹窗(只有确定按钮)==")
print("alert.text 读到弹窗文字 =", alert.text) # 弹窗文字也能读
assert alert.text == "此网页显示 hello!", "警告弹窗文本不符" # 断言弹窗确实弹出
alert.accept() # 课件:alert.accept() 点"确定"
print("accept() 后弹窗已关闭,页面恢复正常操作")
# ---------- 15.2 确认弹窗:dismiss() 走取消分支 ----------
print("== 确认弹窗(确定/取消两个按钮)==")
driver.find_element(By.ID, "b2").click() # 触发确认弹窗
alert = driver.switch_to.alert # 切到弹窗对象
time.sleep(5)
alert.dismiss() # 课件:alert.dismiss() 点"取消"
time.sleep(5) # 等页面回调写结果
r = driver.find_element(By.ID, "r").text
print("dismiss() 点取消后,页面结果 =", r)
assert r == "点了取消", "dismiss 未走取消分支" # 断言取消分支生效
# ---------- 15.3 确认弹窗:accept() 走确定分支 ----------
driver.find_element(By.ID, "b2").click() # 再次触发确认弹窗
alert = driver.switch_to.alert # 切到弹窗对象
alert.accept() # 这次点"确定"
time.sleep(5) # 等页面回调写结果
r = driver.find_element(By.ID, "r").text
print("accept() 点确定后,页面结果 =", r)
assert r == "点了确定", "accept 未走确定分支" # 断言确定分支生效
print("PASS:同一个 confirm 弹窗,dismiss/accept 分别走到两条分支")
driver.quit() # 关闭浏览器释放驱动
print("示例15 运行结束:警告弹窗+确认弹窗(switchTo().alert/accept/dismiss)PASS")



8.2提示弹窗

python
import os # 环境变量 + 写本地演示页
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 本地演示页:提示按钮 + 结果区 ----------
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "练习页_弹窗62.html")
with open(html_path, "w", encoding="utf-8") as f:
f.write("<html><head><meta charset='utf-8'><title>弹窗62练习页</title></head><body>"
"<button id='b3' onclick=\"var v=prompt('Please enter yourname','');"
"document.getElementById('r').innerText=(v===null)?'取消了':"
"'输入:' + v\">提示弹窗</button>"
"<div id='r'></div></body></html>")
driver.get("file:///" + html_path.replace("\\", "/")) # 打开本地演示页
time.sleep(1) # 等渲染
# ---------- 15b.1 提示弹窗:send_keys 输入 + accept 提交 ----------
print("== 提示弹窗第一轮:sendKeys('hello') + accept() ==")
driver.find_element(By.ID, "b3").click() # 触发提示弹窗(带输入框)
alert = driver.switch_to.alert # 课件:switchTo().alert()
alert.send_keys("hello") # 课件:alert.sendKeys("hello") 往弹窗输入框打字h
time.sleep(5)
alert.accept() # 点"确定",把输入值提交给页面
time.sleep(0.5) # 等页面回调写结果
r = driver.find_element(By.ID, "r").text
print("accept 后页面结果 =", r)
assert r == "输入:hello", "send_keys 的内容未被提交" # 断言输入值被页面接收
# ---------- 15b.2 提示弹窗:send_keys 输入 + dismiss 放弃 ----------
print("== 提示弹窗第二轮:sendKeys('world') + dismiss() ==")
driver.find_element(By.ID, "b3").click() # 再次触发提示弹窗
alert = driver.switch_to.alert # 切到弹窗对象
alert.send_keys("world") # 先打字
time.sleep(5)
alert.dismiss() # 课件:alert.dismiss() 点"取消"放弃输入
time.sleep(0.5) # 等页面回调写结果
r = driver.find_element(By.ID, "r").text
print("dismiss 后页面结果 =", r)
assert r == "取消了", "dismiss 后不应提交输入值" # 断言取消分支生效
print("PASS:send_keys 输入的值,accept 才提交、dismiss 就丢弃")
driver.quit() # 关闭浏览器释放驱动
print("示例15b 运行结束:提示弹窗(sendKeys/accept/dismiss)PASS")


9 ⽂件上传
点击⽂件上传的场景下会弹窗系统窗⼝,进⾏⽂件的选择。
selenium⽆法识别⾮web的控件,上传⽂件窗⼝为系统⾃带,⽆法识别窗⼝元素
但是可以使⽤sendkeys来上传指定路径的⽂件,达到的效果是⼀样的
python
import os # 生成演示页与待上传样本
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path)) # 启动浏览器
# ---------- 16.0 准备演示页(结构照课件:body > div > div > input[type=file]) ----------
base = os.path.dirname(os.path.abspath(__file__)) # 示例目录
html_path = os.path.join(base, "练习页_上传.html") # 演示页路径
with open(html_path, "w", encoding="utf-8") as f: # 写页面:div 套 div 再套 file 输入框
f.write("<html><head><meta charset='utf-8'><title>上传练习页</title></head><body>"
"<div><div><input type='file' id='fu'></div></div>"
"<div id='r'>未选择文件</div>"
"<script>document.getElementById('fu').onchange=function(){"
"document.getElementById('r').innerText='页面已接收文件:'"
"+this.files[0].name};</script></body></html>")
sample = os.path.join(base, "upload_sample.txt") # 待上传的样本文件(绝对路径)
with open(sample, "w", encoding="utf-8") as f: # 写样本内容
f.write("selenium 文件上传测试样本")
driver.get("file:///" + html_path.replace("\\", "/")) # 打开本地演示页
time.sleep(1) # 等渲染
# ---------- 16.1 课件第 1 行:cssSelector 定位 file 输入框 ----------
ele = driver.find_element(By.CSS_SELECTOR,
"body > div > div > input[type=file]") # 课件原选择器,逐层路径
print("定位到元素:tag =", ele.tag_name,
"| type =", ele.get_attribute("type"))
# ---------- 16.2 课件第 2 行:sendKeys 文件绝对路径完成上传 ----------
print("待上传文件路径 =", sample)
ele.send_keys(sample) # 课件核心:sendKeys(路径)= 代替系统弹窗选文件
time.sleep(1) # 等 onchange 回调
value = ele.get_attribute("value") # input.value 回读
echo = driver.find_element(By.ID, "r").text # 页面 JS 回显
print("send_keys 后 input.value =", value)
print("页面 onchange 回显 =", echo)
assert "upload_sample.txt" in value, "文件路径未写入 input" # 断言路径已写入
assert echo == "页面已接收文件:upload_sample.txt", "浏览器未接收文件" # 断言浏览器真的收到了文件
print("PASS:没碰系统弹窗,send_keys(路径) 就达到了手动选文件一样的效果")
print("(input.value 显示 C:\\fakepath\\... 是浏览器安全策略,属正常现象)")
driver.quit() # 关闭浏览器释放驱动
print("示例16 运行结束:文件上传 send_keys 演示 PASS")


10.浏览器参数设置
10.1 设置无头模式
python
import os # 路径处理
import time # 强制等待
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.chrome.options import Options # ChromeOptions:本例主角
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
# ---------- 课件第 1、2 行:new ChromeOptions() + addArguments("-headless") ----------
options = Options() # 课件 new ChromeOptions()
options.add_argument("--headless") # 课件 addArguments("-headless"):无头开关
options.add_argument("--window-size=1920,1080") # 无头下给一个虚拟视口,截图/布局才正常
# ---------- 课件第 3 行:new ChromeDriver(options) ----------
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
driver = webdriver.Chrome(service=Service(driver_path), options=options) # 带无头参数启动
print("浏览器已在后台启动:此刻你桌面上应该看不到任何 Chrome 窗口")
# ---------- 课件第 4 行:driver.get(...),并证明无头下功能正常 ----------
driver.get("https://www.baidu.com") # 课件 driver.get("https://www.baidu.com")
time.sleep(2) # 等后台渲染
print("无头模式读标题 title =", driver.title) # 功能证明1:标题读得到
assert driver.title == "百度一下,你就知道", "无头模式页面异常" # 断言页面正常打开
box = driver.find_elements(By.ID, "chat-textarea") # 功能证明2:元素找得到
print("无头模式找搜索框:命中", len(box), "个")
assert box, "无头模式下找不到搜索框"
shot = os.path.join(os.path.dirname(os.path.abspath(__file__)), "截图_无头模式.png")
driver.get_screenshot_as_file(shot) # 功能证明3:后台也能截图
print("无头模式截图已保存 =", shot, "(", os.path.getsize(shot), "字节)")
# ---------- 课件第 5 行:driver.quit() ----------
driver.quit() # 课件 driver.quit()
print("示例17 运行结束:全程无窗口,无头模式演示 PASS")


10.2设置浏览器加载模式

python
import os # 环境变量控制后台运行
import time # 计时用
from selenium import webdriver # Selenium 入口
from selenium.webdriver.chrome.service import Service # 指定 chromedriver
from selenium.webdriver.chrome.options import Options # ChromeOptions:本例主角
from selenium.webdriver.common.by import By # 定位方式
from webdriver_manager.chrome import ChromeDriverManager # 自动管理 chromedriver
HEADLESS = os.environ.get("DEMO_HEADLESS", "0") == "1" # 批量验证置 1 后台跑
driver_path = ChromeDriverManager().install() # 匹配本地 chromedriver
def make_opts(strategy):
o = Options() # 每组实验独立的参数对象
o.page_load_strategy = strategy # 课件 setPageLoadStrategy(...)
if HEADLESS:
o.add_argument("--headless") # 验证环境兼容
o.add_argument("--no-sandbox")
o.add_argument("--disable-dev-shm-usage")
return o
# ---------- 实验1:策略 NONE(课件演示档) ----------
d1 = webdriver.Chrome(service=Service(driver_path), options=make_opts("none"))
t0 = time.time() # 计时开始
d1.get("https://www.baidu.com") # none:get 几乎立即返回
cost_none = time.time() - t0 # get 耗时
print("== 策略 none(课件 setPageLoadStrategy(NONE))==")
print("get() 返回耗时 = %.2f 秒(不等页面加载完就返回了)" % cost_none)
n0 = len(d1.find_elements(By.ID, "chat-textarea")) # 返回瞬间页面可能还没渲染
print("get 返回的瞬间找搜索框:命中 %d 个(0 说明页面还没加载出来)" % n0)
time.sleep(3) # 页面在后台继续加载
n1 = len(d1.find_elements(By.ID, "chat-textarea")) # 等一会再找
print("等 3 秒后再找搜索框:命中 %d 个(页面后台加载完成)" % n1)
assert n1 > 0, "none 策略下页面最终应加载出元素" # 断言最终加载完成
print("title 此刻 =", d1.title)
d1.quit()
#
# ---------- 实验2:策略 normal(默认档,作对照) ----------
d2 = webdriver.Chrome(service=Service(driver_path), options=make_opts("normal"))
t0 = time.time() # 计时开始
d2.get("https://www.baidu.com") # normal:get 阻塞到加载完成
cost_normal = time.time() - t0 # get 耗时
print("== 策略 normal(默认,对照组)==")
print("get() 返回耗时 = %.2f 秒(等页面加载完才返回)" % cost_normal)
n2 = len(d2.find_elements(By.ID, "chat-textarea")) # 返回瞬间元素就该在
print("get 返回的瞬间找搜索框:命中 %d 个" % n2)
assert n2 > 0, "normal 策略返回时页面应已就绪" # 断言返回即就绪
d2.quit()
print("对比结论:none 的 get 耗时(%.2fs) 明显小于 normal(%.2fs),"
"代价是返回后元素可能还没出现,需要自己等待" % (cost_none, cost_normal))
print("示例17b 运行结束:浏览器加载策略演示 PASS")

11. 本次实操踩坑实录
这 5 个坑全部是本次真机运行中实际遇到的,更有复习价值:
