C# 利用Selenium实现浏览器自动化操作的示例代码

作者:Alan.hsiang 时间:2023-08-10 23:43:15 

概述

Selenium是一款免费的分布式的自动化测试工具,支持多种开发语言,无论是C、 java、ruby、python、或是C# ,你都可以通过selenium完成自动化测试。本文以一个简单的小例子,简述C# 利用Selenium进行浏览器的模拟操作,仅供学习分享使用,如有不足之处,还请指正。

涉及知识点

要实现本例的功能,除了要掌握Html ,JavaScript,CSS等基础知识,还涉及以下知识点:

  • log4net:主要用于日志的记录和存储,本例采用log4net进行日志记录,便于过程跟踪和问题排查,关于log4net的配置和介绍,之前已有说明,本文不做赘述。

  • Queue:队列,先进先出模式,本文主要用于将日志信息保存于队列中,然后再显示到页面上,其中Enqueue用于添加内容到结尾处,Dequeue用于返回并移除一个位置的对象。

  • IWebDriver:浏览器驱动接口,所有的关于浏览器的操作都可以通过此接口进行,不同浏览器有不同的实现类,如:IE浏览器(InternetExplorerDriver)Chrome浏览器(ChromeDriver)等。

  • BackgroundWorker:后台工作线程,区别于主线程,通过事件触发不同的状态。

Selenium安装

本例开发工具为VS2019,通过NuGet进行需要的软件包的安装与管理,如下所示:

C# 利用Selenium实现浏览器自动化操作的示例代码

示例效果图

本例采用Chrome浏览器,用于监控某一个网站并获取相应内容,如下所示:

C# 利用Selenium实现浏览器自动化操作的示例代码

Selenium示例介绍

定义一个webDriver,如下所示:


//谷歌浏览器
ChromeOptions options = new ChromeOptions();
this.driver = new ChromeDriver(options);

通过ID获取元素并填充内容和触发事件,如下所示:


this.driver.FindElement(By.Id("email")).SendKeys(username);
this.driver.FindElement(By.Id("password")).SendKeys(password);
//# 7. 点击登录按钮
this.driver.FindElement(By.Id("sign-in")).Click();

通过XPath获取元素,如下所示:


string xpath1 = "//div[@class=\"product-list\"]/div[@class=\"product\"]/div[@class=\"price-and-detail\"]/div[@class=\"price\"]/span[@class=\"noStock\"]";
string txt = this.driver.FindElement(By.XPath(xpath1)).Text;

核心代码

主要的核心代码,就是浏览器的元素定位查找和事件触发,如下所示:


using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Chrome;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AiSmoking.Core
{
 public class Smoking
 {
   /// <summary>
   /// 是否正在运行
   /// </summary>
   private bool running = false;

/// <summary>
   /// 驱动
   /// </summary>
   private IWebDriver driver = null;

/// <summary>
   /// # 无货
   /// </summary>
   private string no_stock = "Currently Out of Stock";

/// <summary>
   ///  # 线程等待秒数
   /// </summary>
   private int wait_sec = 2;

private Dictionary<string, string> cfg_info;

private string work_path = string.Empty;

/// <summary>
   /// 构造函数
   /// </summary>
   public Smoking()
   {

}

/// <summary>
   /// 带参构造函数
   /// </summary>
   /// <param name="cfg_info"></param>
   /// <param name="work_path"></param>
   public Smoking(Dictionary<string, string> cfg_info,string work_path)
   {
     this.cfg_info = cfg_info;
     this.work_path = work_path;
     this.wait_sec = int.Parse(cfg_info["wait_sec"]);
     //# 如果小于2,则等于2
     this.wait_sec = (this.wait_sec < 2 ? 2 : this.wait_sec);
     this.wait_sec = this.wait_sec * 1000;
   }

/// <summary>
   /// 开始跑
   /// </summary>
   public void startRun()
   {
     //"""运行起来"""
     try
     {
       this.running = true;
       string url = this.cfg_info["url"];
       string username = this.cfg_info["username"];
       string password = this.cfg_info["password"];
       string item_id = this.cfg_info["item_id"];
       if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(item_id))
       {
         LogHelper.put("配置信息不全,请检查config.cfg文件是否为空,然后再重启");
         return;
       }
       if (this.driver == null)
       {
         string explorer = this.cfg_info["explorer"];
         if (explorer == "Chrome")
         {
           //谷歌浏览器
           ChromeOptions options = new ChromeOptions();
           this.driver = new ChromeDriver(options);
         }
         else
         {
           //默认IE
           var options = new InternetExplorerOptions();
           //options.AddAdditionalCapability.('encoding=UTF-8')
           //options.add_argument('Accept= text / css, * / *')
           //options.add_argument('Accept - Language= zh - Hans - CN, zh - Hans;q = 0.5')
           //options.add_argument('Accept - Encoding= gzip, deflate')
           //options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko')
           //# 2. 定义浏览器驱动对象
           this.driver = new InternetExplorerDriver(options);
         }
       }
       this.run(url, username, password, item_id);
     }
     catch (Exception e)
     {
       LogHelper.put("运行过程中出错,请重新打开再试"+e.StackTrace);
     }
   }

/// <summary>
   /// 运行
   /// </summary>
   /// <param name="url"></param>
   /// <param name="username"></param>
   /// <param name="password"></param>
   /// <param name="item_id"></param>
   private void run(string url, string username, string password, string item_id)
   {
     //"""运行起来"""
     //# 3. 访问网站
     this.driver.Navigate().GoToUrl(url);
     //# 4. 最大化窗口
     this.driver.Manage().Window.Maximize();
     if (this.checkIsExists(By.LinkText("账户登录")))
     {
       //# 判断是否登录:未登录
       this.login(username, password);
     }
     if (this.checkIsExists(By.PartialLinkText("欢迎回来")))
     {
       //# 判断是否登录:已登录
       LogHelper.put("登录成功,下一步开始工作了");
       this.working(item_id);
     }
     else
     {
       LogHelper.put("登录失败,请设置账号密码");
     }
   }

/// <summary>
   /// 停止运行
   /// </summary>
   public void stopRun()
   {
     //"""停止"""
     try
     {
       this.running = false;
       //# 如果驱动不为空,则关闭
       //self.close_browser_nicely(self.__driver)
       if (this.driver != null)
       {
         this.driver.Quit();
         //# 关闭后切要为None,否则启动报错
         this.driver = null;
       }
     }
     catch (Exception e)
     {
       //print('Stop Failure')
     }
     finally
     {
       this.driver = null;
     }
   }

private void login(string username, string password)
   {
     //# 5. 点击链接跳转到登录页面
     this.driver.FindElement(By.LinkText("账户登录")).Click();
     //# 6. 输入账号密码
     //# 判断是否加载完成
     if (this.checkIsExists(By.Id("email")))
     {
       this.driver.FindElement(By.Id("email")).SendKeys(username);
       this.driver.FindElement(By.Id("password")).SendKeys(password);
       //# 7. 点击登录按钮
       this.driver.FindElement(By.Id("sign-in")).Click();
     }
   }

/// <summary>
   /// 工作状态
   /// </summary>
   /// <param name="item_id"></param>
   private void working(string item_id)
   {
     while (this.running)
     {
       try
       {
         //# 正常获取信息
         if (this.checkIsExists(By.Id("string")))
         {
           this.driver.FindElement(By.Id("string")).Clear();
           this.driver.FindElement(By.Id("string")).SendKeys(item_id);
           this.driver.FindElement(By.Id("string")).SendKeys(Keys.Enter);
         }
         //# 判断是否查询到商品
         string xpath = "//div[@class=\"specialty-header search\"]/div[@class=\"specialty-description\"]/div[@class=\"gt-450\"]/span[2] ";
         if (this.checkIsExists(By.XPath(xpath)))
         {
           int count = int.Parse(this.driver.FindElement(By.XPath(xpath)).Text);
           if (count < 1)
           {
             Thread.Sleep(this.wait_sec);
             LogHelper.put("没有查询到item id =" + item_id + "对应的信息");
             continue;
           }
         }
         else
         {
           Thread.Sleep(this.wait_sec);
           LogHelper.put("没有查询到item id2 =" + item_id + "对应的信息");
           continue;
         }
         //# 判断当前库存是否有货

string xpath1 = "//div[@class=\"product-list\"]/div[@class=\"product\"]/div[@class=\"price-and-detail\"]/div[@class=\"price\"]/span[@class=\"noStock\"]";
         if (this.checkIsExists(By.XPath(xpath1)))
         {
           string txt = this.driver.FindElement(By.XPath(xpath1)).Text;
           if (txt == this.no_stock)
           {
             //# 当前无货
             Thread.Sleep(this.wait_sec);
             LogHelper.put("查询一次" + item_id + ",无货");
             continue;
           }
         }
         //# 链接path1
         string xpath2 = "//div[@class=\"product-list\"]/div[@class=\"product\"]/div[@class=\"imgDiv\"]/a";
         //# 判断是否加载完毕
         //# this.waiting((By.CLASS_NAME, "imgDiv"))
         if (this.checkIsExists(By.XPath(xpath2)))
         {
           this.driver.FindElement(By.XPath(xpath2)).Click();
           Thread.Sleep(this.wait_sec);
           //# 加入购物车
           if (this.checkIsExists(By.ClassName("add-to-cart")))
           {
             this.driver.FindElement(By.ClassName("add-to-cart")).Click();
             LogHelper.put("加入购物车成功,商品item-id:" + item_id);
             break;
           }
           else
           {
             LogHelper.put("未找到加入购物车按钮");
           }
         }
         else
         {
           LogHelper.put("没有查询到,可能是商品编码不对,或者已下架");
         }
         Thread.Sleep(this.wait_sec);
       }
       catch (Exception e)
       {
         Thread.Sleep(this.wait_sec);
         LogHelper.put(e);
       }
     }
   }

/// <summary>
   /// 判断是否存在
   /// </summary>
   /// <param name="by"></param>
   /// <returns></returns>
   private bool checkIsExists(By by)
   {
     try
     {
       int i = 0;
       while (this.running && i < 3)
       {
         if (this.driver.FindElements(by).Count > 0)
         {
           break;
         }
         else
         {
           Thread.Sleep(this.wait_sec);
           i = i + 1;
         }
       }
       return this.driver.FindElements(by).Count > 0;
     }
     catch (Exception e)
     {
       LogHelper.put(e);
       return false;
     }
   }

}
}

关于日志帮助类,代码如下:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;

[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace AiSmoking.Core
{
 /// <summary>
 /// 日志帮助类
 /// </summary>
 public static class LogHelper
 {
   /// <summary>
   /// 日志实例
   /// </summary>
   private static ILog logInstance = LogManager.GetLogger("smoking");

private static Queue<string> queue = new Queue<string>(2000);

public static void put(string msg)
   {
     queue.Enqueue(msg);
     WriteLog(msg, LogLevel.Info);
   }

public static void put(Exception ex)
   {
     WriteLog(ex.StackTrace, LogLevel.Error);
   }

public static string get()
   {
     if (queue.Count > 0)
     {
       return queue.Dequeue();
     }
     else
     {
       return string.Empty;
     }
   }

public static void WriteLog(string message, LogLevel level)
   {
     switch (level)
     {
       case LogLevel.Debug:
         logInstance.Debug(message);
         break;
       case LogLevel.Error:
         logInstance.Error(message);
         break;
       case LogLevel.Fatal:
         logInstance.Fatal(message);
         break;
       case LogLevel.Info:
         logInstance.Info(message);
         break;
       case LogLevel.Warn:
         logInstance.Warn(message);
         break;
       default:
         logInstance.Info(message);
         break;
     }
   }

}

public enum LogLevel
 {
   Debug = 0,
   Error = 1,
   Fatal = 2,
   Info = 3,
   Warn = 4
 }
}

作者:Alan.hsiang
出处:http://www.cnblogs.com/hsiang/

来源:https://www.cnblogs.com/hsiang/archive/2020/09/09/13643016.html#commentform

标签:c#,Selenium,浏览器,自动化
0
投稿

猜你喜欢

  • Android PickerScrollView滑动选择控件使用方法详解

    2023-06-19 01:00:34
  • c#求两个数中最大值的方法

    2021-09-21 15:24:20
  • 详谈Java中的Object、T(泛型)、?区别

    2022-06-11 21:13:11
  • springboot 使用poi进行数据的导出过程详解

    2022-12-01 07:23:31
  • 浅谈Java 继承接口同名函数问题

    2023-07-22 13:28:47
  • 淘宝IP地址库采集器c#代码

    2022-01-15 22:42:33
  • C#实现翻转字符串的方法

    2021-11-23 11:51:48
  • Spring Boot 详细分析Conditional自动化配置注解

    2021-11-25 21:56:14
  • Android​短信验证码倒计时验证的2种常用方式

    2022-06-23 17:27:42
  • Android 3D滑动菜单完全解析 Android实现推拉门式的立体特效

    2022-04-19 04:36:01
  • SpringBoot事件发布和监听详解

    2022-04-18 02:39:59
  • java8 统计字符串字母个数的几种方法总结(推荐)

    2023-09-25 17:37:48
  • c++ 函数指针相关总结

    2022-09-26 04:42:33
  • C#软件注册码的实现代码

    2021-10-14 06:14:38
  • WPF使用DrawingContext实现二维绘图

    2021-06-08 15:04:27
  • Java如何将大文件切割成小文件

    2022-12-27 07:40:40
  • Java实现定时任务的方法详解

    2022-12-30 04:14:00
  • C#使用LOCK实现线程同步

    2022-02-02 10:51:14
  • Entity Framework配置关系

    2023-10-15 09:45:30
  • unity3D实现摄像机抖动特效

    2022-09-20 12:10:42
  • asp之家 软件编程 m.aspxhome.com