使用 C# 和 Selenium WebDriver 实现自动化
需求如果我现在需要对某一个网页 进行运维 运维操作为点击页面按钮查看所有功能是否正常,现在想开发自动化运维的方式替代重复性操作,请提供可行方案, 我目前的想法是 python selenium自动化操作 或者 本质是对接口能否正常返回数据的测试,也可以使用C# python,进行接口测试
如果需要 进行页面 点击 设置账目密码登陆 判断是否登陆成功 或者判断点击后页面响应是否正常,推荐用什么方法,继续用C# 还是换python的呢
判断页面是否加载完成 特定元素是否出现或存在
1.NUGET 下载浏览器对应版本
工具 > NuGet 包管理器 > 管理解决方案的 NuGet 包
搜索并安装以下包:
Selenium.WebDriverSelenium.WebDriver.ChromeDriver(如果你使用 ChromeSelenium.Support(可选,用于等待、选择等)
Update-Package Selenium.WebDriver
Update-Package Selenium.WebDriver.ChromeDriver
可检查环境变量
确保 Chrome 的安装目录已经添加到了系统的 PATH 环境变量中。这样可以让操作系统知道在哪里查找 Chrome 可执行文件。如果没有添加,可以手动添加:
找到 Chrome 的安装目录,通常是 C:\Program Files\Google\Chrome\Application\。
将这个路径添加到系统的 PATH 环境变量中。
2.使用对应版本的driver
- 访问 EdgeDriver https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/下载页面 确认你需要的版本。
- 更新或降级
Selenium.WebDriver.MSEdgeDriver到正确的版本以匹配你的 Edge 浏览器版本 - 准备浏览器驱动(如 ChromeDriver)
- 下载 ChromeDriver(版本需与你的 Chrome 浏览器一致)
- 解压后将
chromedriver.exe放到项目目录中,或者指定完整路径使用
//IWebDriver driver = new ChromeDriver();
// 初始化EdgeDriver(自动下载匹配的驱动)
IWebDriver driver = new EdgeDriver();
// 指定驱动路径
var service = EdgeDriverService.CreateDefaultService();
Console.WriteLine($"驱动路径: {service.DriverServicePath}");
Console.WriteLine($"驱动是否存在: {File.Exists(service.DriverServicePath)}");
// 指定驱动路径
var options = new EdgeOptions();
//var service = EdgeDriverService.CreateDefaultService(@"C:\Users\AJRSOFT\Desktop\wjl\automaintainer\packages\Selenium.WebDriver.MSEdgeDriver.138.0.3351.83\driver\win32\msedgedriver.exe");
driver = new EdgeDriver(service, options);
public IWebDriver driver ;
关闭driver后才退出 private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// 关闭浏览器
driver.Quit();
Application.Exit();
}
private void button2_Click(object sender, EventArgs e)
{
// 关闭浏览器
driver.Quit();
Application.Exit();
}
注意事项
<span b-rctmneqryd="" id="ldd" onclick="window.location.href='/Home/Downloaddb'" style="cursor: pointer; color: rgb(231, 239, 247);">Down</span>
// 点击bak下载按钮
IWebElement houtaiButton = driver.FindElement(By.XPath("/html/body/header/div[2]/div/div/a/button"));
houtaiButton.Click();
IWebElement bakButton = driver.FindElement(By.XPath("/html/body/header/div[2]/div/div/span[2]"));
bakButton.Click();
为啥第二个按钮不行
<span>不是可点击元素(默认行为)使用 JavaScript 点击更可靠
由于 .Click() 有时会失败,特别是对于非标准可点击元素(如 span),建议你改用 JavaScript 来触发点击事件:
在winform中 怎么使用流程化 展示 使用从上到下的树形 中间圆环节点 的结构
在selenium中明明浏览器已经显示元素,却报错不存在
// 获取当前窗口句柄
string mainWindowHandle = driver.CurrentWindowHandle;
UpdateLog("进入研究工具");
IWebElement bakButton = driver.FindElement(By.XPath("/html/body/div[4]/div[1]/div/div[3]/ul/li[1]/i"));
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bakButton);
// 等待新窗口出现并切换
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(drv => drv.WindowHandles.Count > 1);
foreach (string handle in driver.WindowHandles)
{
if (handle != mainWindowHandle)
{
driver.SwitchTo().Window(handle);
break;
}
}
// 等待新窗口内容加载完成
wait.Until(drv => {
try
{
return drv.FindElement(By.Id("treeZJ_1_a")).Displayed;
}
catch
{
return false;
}
});
UpdateLog("点击数据分析"); //为啥没有找到id'明明已经切换了tab
可能原因及解决方案
新窗口尚未完全加载
// 改进后的等待条件(添加页面就绪状态检查) wait.Until(drv => { try { return ((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete") && drv.FindElement(By.Id("treeZJ_1_a")).Displayed; } catch { return false; } });元素在 iframe 中
// 检查并切换到iframe wait.Until(drv => { try { // 先确保主文档加载完成 if (!((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete")) return false; var iframe = drv.FindElement(By.Id("contentFrame")); // 替换为实际iframe的ID或选择器 drv.SwitchTo().Frame(iframe); return drv.FindElement(By.Id("treeZJ_1_a")).Displayed; } catch { // 如果失败,切换回默认内容(重要!) drv.SwitchTo().DefaultContent(); return false; } });// 检查iframe是否存在 var iframes = driver.FindElements(By.TagName("iframe")); if (iframes.Count > 0) { UpdateLog("检测到iframe,尝试切换..."); driver.SwitchTo().Frame(iframes[0]); }元素ID动态生成
// 使用更灵活的选择器(如XPath通过文本定位) wait.Until(drv => drv.FindElement(By.XPath("//a[contains(text(),'数据分析')]")).Displayed);页面存在延迟渲染(如AJAX)
// 添加额外等待时间 Thread.Sleep(1000); // 临时方案(不推荐长期使用) // 或使用更智能的等待 wait.Until(drv => (bool)((IJavaScriptExecutor)drv) .ExecuteScript("return jQuery.active == 0")); // 等待jQuery请求完成窗口切换未成功
// 验证窗口切换是否真正生效 string newWindowHandle = driver.CurrentWindowHandle; if (newWindowHandle == mainWindowHandle) { UpdateLog("错误:窗口切换失败!"); return; }
查找页面元素 一层CheckElementExists
public bool CheckElementExists(IWebDriver driver, By elementLocator, int timeoutSeconds = 10)
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds));
try
{
return wait.Until(drv =>
{
try
{
return drv.FindElement(elementLocator).Displayed;
}
catch (NoSuchElementException)
{
UpdateLog("等待页面元素未出现");
return false;
}
});
}
catch (WebDriverTimeoutException)
{
UpdateLog("等待页面加载超时");
return false;
}
}
封装好的 CheckElementInIframeExists 方法,它可以智能地在 iframe 中查找元素
/// <summary>
/// 在iframe中检查元素是否存在并可交互
/// </summary>
/// <param name="driver">WebDriver实例</param>
/// <param name="elementLocator">元素定位器(By对象)</param>
/// <param name="iframeLocator">iframe定位器(By对象)</param>
/// <param name="timeoutSeconds">超时时间(秒)</param>
/// <returns>是否找到元素</returns>
public bool CheckElementInIframeExists(
IWebDriver driver,
By elementLocator,
By iframeLocator = null,
int timeoutSeconds = 10)
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds));
try
{
return wait.Until(drv =>
{
try
{
// 1. 确保主文档就绪
if (!((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete"))
return false;
// 2. 切换到iframe(如果指定了iframe定位器)
if (iframeLocator != null)
{
var iframe = drv.FindElement(iframeLocator);
drv.SwitchTo().Frame(iframe);
}
// 3. 查找目标元素
var element = drv.FindElement(elementLocator);
bool isVisible = element.Displayed && element.Enabled;
// 4. 无论成功与否都切换回主文档
drv.SwitchTo().DefaultContent();
return isVisible;
}
catch (NoSuchElementException)
{
drv.SwitchTo().DefaultContent();
UpdateLog($"元素未找到: {elementLocator}");
return false;
}
catch (StaleElementReferenceException)
{
drv.SwitchTo().DefaultContent();
UpdateLog("元素状态已过期,正在重试...");
return false;
}
catch (Exception ex)
{
drv.SwitchTo().DefaultContent();
UpdateLog($"检查元素时出错: {ex.Message}");
return false;
}
});
}
catch (WebDriverTimeoutException)
{
UpdateLog($"操作超时,未找到元素: {elementLocator}");
return false;
}
}
使用示例
// 示例1:在指定iframe中查找元素
bool exists = CheckElementInIframeExists(
driver,
elementLocator: By.Id("treeZJ_1_a"),
iframeLocator: By.Name("ifr1"),
timeoutSeconds: 15);
高级用法(嵌套iframe场景):
// 第一层iframe
if (CheckElementInIframeExists(driver, By.Name("outerFrame")))
{
// 第二层iframe
CheckElementInIframeExists(
driver,
By.Id("targetElement"),
By.Id("innerFrame"));
}
元素为什么一会找到,一会没找到
// 获取当前窗口句柄
string mainWindowHandle = driver.CurrentWindowHandle;
UpdateLog("进入研究工具");
IWebElement bakButton = driver.FindElement(By.XPath("/html/body/div[4]/div[1]/div/div[3]/ul/li[1]/i"));
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bakButton);
// 等待新窗口出现并切换
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(drv => drv.WindowHandles.Count > 1);
foreach (string handle in driver.WindowHandles)
{
if (handle != mainWindowHandle)
{
driver.SwitchTo().Window(handle);
break;
}
}
// 等待新窗口内容加载完成
wait.Until(drv => {
try
{
return drv.FindElement(By.Id("treeZJ_1_a")).Displayed;
}
catch
{
return false;
}
});
UpdateLog("点击数据分析"); //为啥没有找到 明明已经切换了tab
IWebElement analyButton = driver.FindElement(By.Id("treeZJ_1_a"));
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", analyButton);
bool isliumingSuccess = CheckElementInIframeExists(driver, By.XPath("/html/body/div[5]/div[2]/div[1]/div[1]/fieldset[2]/div/div[3]/a[81]"), By.Name("ifr"));
By.XPath("//fieldset[contains(@class,'opotion')]//a[contains(text(),'碱敏分析')]")
同级 的iframe 必须切换回原文档
时间等待不够吗
bool isliumingSuccess = CheckElementInIframeExists(driver, By.XPath("/html/body/div[5]/div[2]/div[1]/div[1]/fieldset[2]/div/div[3]/a[81]"), By.Id("ifr"));
by name 和by id 测试
为什么直接执行找不到元素,慢慢调试就能找到
原因 WebDriver 自动化测试中的经典难题,根本原因在于 运行时与调试时的时序差异 和 绝对 XPath 的脆弱性
修复
第二步:增强 iframe 等待机制
/// <summary>
/// 在iframe中检查元素是否存在并可交互
/// </summary>
/// <param name="driver">WebDriver实例</param>
/// <param name="elementLocator">元素定位器(By对象)</param>
/// <param name="iframeLocator">iframe定位器(By对象)</param>
/// <param name="timeoutSeconds">超时时间(秒)</param>
/// <returns>是否找到元素</returns>
public bool CheckElementInIframeExists(
IWebDriver driver,
By elementLocator,
By iframeLocator = null,
int timeoutSeconds = 15)
{
try {
// 1. 主文档就绪 + iframe存在双重检查
new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds))
.Until(drv =>
((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete")
&& drv.FindElements(iframeLocator).Count > 0);
// 2. 切换到iframe并等待其就绪
driver.SwitchTo().Frame(driver.FindElement(iframeLocator));
new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds))
.Until(drv => ((IJavaScriptExecutor)drv)
.ExecuteScript("return document.readyState").Equals("complete"));
// 3. 查找元素(带重试机制)
for (int i = 0; i < 3; i++)
{
try
{
var element = driver.FindElement(elementLocator);
if (element.Displayed && element.Enabled)
return true;
}
catch
{
Thread.Sleep(1000); // 每次重试间隔1秒
}
}
return false;
}
finally { }
}
第三步:添加智能等待策略
// 专用iframe等待方法
public void WaitForIframeContent(IWebDriver driver, By iframeLocator, int timeout = 15)
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeout));
// 等待iframe存在并可用
var iframe = wait.Until(drv =>
{
var frame = drv.FindElement(iframeLocator);
return frame.Displayed && frame.Enabled ? frame : null;
});
// 切换到iframe并等待内容
driver.SwitchTo().Frame(iframe);
try
{
wait.Until(drv =>
((IJavaScriptExecutor)drv).ExecuteScript(
"return document.readyState === 'complete' && " +
"document.body.children.length > 0"));
}
finally
{
driver.SwitchTo().DefaultContent();
}
}
CheckElementExists等待
CheckElementExists
失败时自动截图
catch (Exception ex)
{
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
((ITakesScreenshot)driver).GetScreenshot()
.SaveAsFile($"error_{timestamp}.png");
throw;
}
窗口切换问题
// 获取当前窗口句柄
string mainWindowHandle = driver.CurrentWindowHandle;
IWebElement cao1Button = driver.FindElement(By.XPath("/html/body/div[1]/div[4]/div/fieldset/div[2]/div/div[7]/div[2]/span[4]"));
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", cao1Button);
// 等待新窗口出现并切换
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(drv => drv.WindowHandles.Count > 1);
foreach (string handle in driver.WindowHandles)
{
if (handle != mainWindowHandle)
{
driver.SwitchTo().Window(handle);
break;
}
} 本来是要进入出现的第三个窗口,但是进入了第一个去了
问题分析
- 窗口句柄顺序不可靠:
WindowHandles集合的顺序可能与窗口打开顺序不一致 - 当前逻辑缺陷:代码只是简单地切换到「非当前窗口」的第一个窗口
替代方案(更可靠)
如果窗口有可预测的特征,最好通过标题或URL来识别:
// 方法1:通过标题切换
string targetTitle = "目标窗口标题";
var targetWindow = driver.WindowHandles
.FirstOrDefault(h => {
driver.SwitchTo().Window(h);
return driver.Title.Contains(targetTitle);
});
// 方法2:通过URL切换
string targetUrl = "expected-part-of-url";
var targetWindow = driver.WindowHandles
.FirstOrDefault(h => {
driver.SwitchTo().Window(h);
return driver.Url.Contains(targetUrl);
});
if (targetWindow != null)
{
driver.SwitchTo().Window(targetWindow);
}
else
{
throw new NoSuchWindowException("未找到目标窗口");
}
定义
导航直接进入链接
driver.Navigate().GoToUrl(url); 这个是在原窗口修改链接直接进入,怎么返回原链接呢
// 1. 先访问 A 页面
driver.Navigate().GoToUrl("https://example.com/pageA");
// 2. 再访问 B 页面
driver.Navigate().GoToUrl("https://example.com/pageB");
// 3. 返回上一页(即 pageA)
driver.Navigate().Back();
// 再次前往 pageB
driver.Navigate().Forward();
// 返回 A 页面(直接重新访问)
driver.Navigate().GoToUrl(urlA);
dirver每次打开都要出现你的连接不是专用连接,怎么办,
在使用 Selenium 的 EdgeDriver(或其他浏览器驱动)时,如果每次打开网页都遇到 "你的连接不是专用连接"(NET::ERR_CERT_AUTHORITY_INVALID) 的安全警告,通常是因为:
- 网站证书无效(自签名证书/过期证书/非受信任 CA 签发)
- 浏览器严格的安全策略
- WebDriver 未配置忽略证书错误
解决方案(5 种方法)
方法 1:让 WebDriver 忽略证书错误(推荐)
在初始化 EdgeOptions 时,添加 AcceptInsecureCertificates 或绕过证书错误:
var options = new EdgeOptions();
options.AddArgument("--ignore-certificate-errors"); // 忽略证书错误
options.AddArgument("--allow-insecure-localhost"); // 允许不安全本地主机
options.AddArgument("--disable-web-security"); // 禁用同源策略(慎用)
// 如果使用 Selenium 4.6+
options.AcceptInsecureCertificates = true;
driver = new EdgeDriver(options);
方法 2:手动添加证书为信任(适用于自签名证书)
- 访问目标网站,手动导出证书(Chrome/Edge → 点击锁图标 → "证书" → "导出")。
- 将证书安装到 "受信任的根证书颁发机构"(Windows 证书管理器)。
- 重启浏览器。
方法 3:强制使用 HTTPS(适用于开发环境)
如果是本地测试(如 https://localhost),可改用 http:// 或配置有效的本地证书。
方法 4:禁用浏览器安全策略(不推荐生产环境)
var options = new EdgeOptions();
options.AddArgument("--disable-extensions");
options.AddArgument("--disable-notifications");
options.AddArgument("--disable-popup-blocking");
options.AddArgument("--disable-infobars");
options.AddArgument("--test-type"); // 绕过某些安全限制
driver = new EdgeDriver(options);
方法 5:使用 Firefox 或 Chrome 的特定配置
如果问题仅在 Edge 出现,可尝试切换浏览器:
// Chrome 示例
var chromeOptions = new ChromeOptions();
chromeOptions.AcceptInsecureCertificates = true;
driver = new ChromeDriver(chromeOptions);
临时绕过警告(非自动化方式)
如果只是想手动继续访问,可通过代码自动点击 "高级" → "继续前往"(不推荐,因页面结构可能变):
try {
driver.FindElement(By.Id("details-button")).Click(); // 点击 "高级"
driver.FindElement(By.Id("proceed-link")).Click(); // 点击 "继续前往"
} catch { /* 忽略错误 */ }
根本解决方案
- 修复服务器证书(最佳实践)。
- 如果是内部系统,让 IT 部门部署有效证书(如 Let's Encrypt 或企业 CA 签发)。
选择最适合你场景的方法即可!