主頁 > .NET開發 > C# 利用Selenium實作瀏覽器自動化操作

C# 利用Selenium實作瀏覽器自動化操作

2020-09-09 23:56:30 .NET開發

概述

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進行需要的軟體包的安裝與管理,如下所示:

示例效果圖

本例采用Chrome瀏覽器,用于監控某一個網站并獲取相應內容,如下所示:

Selenium示例介紹

定義一個webDriver,如下所示:

1 //谷歌瀏覽器
2 ChromeOptions options = new ChromeOptions();
3 this.driver = new ChromeDriver(options);

通過ID獲取元素并填充內容和觸發事件,如下所示:

1 this.driver.FindElement(By.Id("email")).SendKeys(username);
2 this.driver.FindElement(By.Id("password")).SendKeys(password);
3 //# 7. 點擊登錄按鈕
4 this.driver.FindElement(By.Id("sign-in")).Click();

通過XPath獲取元素,如下所示:

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

核心代碼

主要的核心代碼,就是瀏覽器的元素定位查找和事件觸發,如下所示:

  1 using OpenQA.Selenium;
  2 using OpenQA.Selenium.IE;
  3 using OpenQA.Selenium.Chrome;
  4 using System;
  5 using System.Collections.Generic;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Threading;
  9 using System.Threading.Tasks;
 10 
 11 namespace AiSmoking.Core
 12 {
 13     public class Smoking
 14     {
 15         /// <summary>
 16         /// 是否正在運行
 17         /// </summary>
 18         private bool running = false;
 19 
 20         /// <summary>
 21         /// 驅動
 22         /// </summary>
 23         private IWebDriver driver = null;
 24 
 25 
 26         /// <summary>
 27         /// # 無貨
 28         /// </summary>
 29         private string no_stock = "Currently Out of Stock";
 30 
 31 
 32         /// <summary>
 33         ///   # 執行緒等待秒數
 34         /// </summary>
 35         private int wait_sec = 2;
 36 
 37         private Dictionary<string, string> cfg_info;
 38 
 39         private string work_path = string.Empty;
 40 
 41         /// <summary>
 42         /// 建構式
 43         /// </summary>
 44         public Smoking()
 45         {
 46 
 47         }
 48 
 49         /// <summary>
 50         /// 帶參建構式
 51         /// </summary>
 52         /// <param name="cfg_info"></param>
 53         /// <param name="work_path"></param>
 54         public Smoking(Dictionary<string, string> cfg_info,string work_path)
 55         {
 56             this.cfg_info = cfg_info;
 57             this.work_path = work_path;
 58             this.wait_sec = int.Parse(cfg_info["wait_sec"]);
 59             //# 如果小于2,則等于2
 60             this.wait_sec = (this.wait_sec < 2 ? 2 : this.wait_sec);
 61             this.wait_sec = this.wait_sec * 1000;
 62         }
 63 
 64         /// <summary>
 65         /// 開始跑
 66         /// </summary>
 67         public void startRun()
 68         {
 69             //"""運行起來"""
 70             try
 71             {
 72                 this.running = true;
 73                 string url = this.cfg_info["url"];
 74                 string username = this.cfg_info["username"];
 75                 string password = this.cfg_info["password"];
 76                 string item_id = this.cfg_info["item_id"];
 77                 if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(item_id))
 78                 {
 79                     LogHelper.put("配置資訊不全,請檢查config.cfg檔案是否為空,然后再重啟");
 80                     return;
 81                 }
 82                 if (this.driver == null)
 83                 {
 84                     string explorer = this.cfg_info["explorer"];
 85                     if (explorer == "Chrome")
 86                     {
 87                         //谷歌瀏覽器
 88                         ChromeOptions options = new ChromeOptions();
 89                         this.driver = new ChromeDriver(options);
 90                     }
 91                     else
 92                     {
 93                         //默認IE
 94                         var options = new InternetExplorerOptions();
 95                         //options.AddAdditionalCapability.('encoding=UTF-8')
 96                         //options.add_argument('Accept= text / css, * / *')
 97                         //options.add_argument('Accept - Language= zh - Hans - CN, zh - Hans;q = 0.5')
 98                         //options.add_argument('Accept - Encoding= gzip, deflate')
 99                         //options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko')
100                         //# 2. 定義瀏覽器驅動物件
101                         this.driver = new InternetExplorerDriver(options);
102                     }
103                 }
104                 this.run(url, username, password, item_id);
105             }
106             catch (Exception e)
107             {
108                 LogHelper.put("運行程序中出錯,請重新打開再試"+e.StackTrace);
109             }
110         }
111 
112 
113         /// <summary>
114         /// 運行
115         /// </summary>
116         /// <param name="url"></param>
117         /// <param name="username"></param>
118         /// <param name="password"></param>
119         /// <param name="item_id"></param>
120         private void run(string url, string username, string password, string item_id)
121         {
122             //"""運行起來"""
123             //# 3. 訪問網站
124             this.driver.Navigate().GoToUrl(url);
125             //# 4. 最大化視窗
126             this.driver.Manage().Window.Maximize();
127             if (this.checkIsExists(By.LinkText("賬戶登錄")))
128             {
129                 //# 判斷是否登錄:未登錄
130                 this.login(username, password);
131             }
132             if (this.checkIsExists(By.PartialLinkText("歡迎回來")))
133             {
134                 //# 判斷是否登錄:已登錄
135                 LogHelper.put("登錄成功,下一步開始作業了");
136                 this.working(item_id);
137             }
138             else
139             {
140                 LogHelper.put("登錄失敗,請設定賬號密碼");
141             }
142         }
143 
144         /// <summary>
145         /// 停止運行
146         /// </summary>
147         public void stopRun()
148         {
149             //"""停止"""
150             try
151             {
152                 this.running = false;
153                 //# 如果驅動不為空,則關閉
154                 //self.close_browser_nicely(self.__driver)
155                 if (this.driver != null)
156                 {
157                     this.driver.Quit();
158                     //# 關閉后切要為None,否則啟動報錯
159                     this.driver = null;
160                 }
161             }
162             catch (Exception e)
163             {
164                 //print('Stop Failure')
165             }
166             finally
167             {
168                 this.driver = null;
169             }
170         }
171 
172 
173         private void login(string username, string password)
174         {
175             //# 5. 點擊鏈接跳轉到登錄頁面
176             this.driver.FindElement(By.LinkText("賬戶登錄")).Click();
177             //# 6. 輸入賬號密碼
178             //# 判斷是否加載完成
179             if (this.checkIsExists(By.Id("email")))
180             {
181                 this.driver.FindElement(By.Id("email")).SendKeys(username);
182                 this.driver.FindElement(By.Id("password")).SendKeys(password);
183                 //# 7. 點擊登錄按鈕
184                 this.driver.FindElement(By.Id("sign-in")).Click();
185             }
186         }
187 
188         /// <summary>
189         /// 作業狀態
190         /// </summary>
191         /// <param name="item_id"></param>
192         private void working(string item_id)
193         {
194             while (this.running)
195             {
196                 try
197                 {
198                     //# 正常獲取資訊
199                     if (this.checkIsExists(By.Id("string")))
200                     {
201                         this.driver.FindElement(By.Id("string")).Clear();
202                         this.driver.FindElement(By.Id("string")).SendKeys(item_id);
203                         this.driver.FindElement(By.Id("string")).SendKeys(Keys.Enter);
204                     }
205                     //# 判斷是否查詢到商品
206                     string xpath = "//div[@class=\"specialty-header search\"]/div[@class=\"specialty-description\"]/div[@class=\"gt-450\"]/span[2] ";
207                     if (this.checkIsExists(By.XPath(xpath)))
208                     {
209                         int count = int.Parse(this.driver.FindElement(By.XPath(xpath)).Text);
210                         if (count < 1)
211                         {
212                             Thread.Sleep(this.wait_sec);
213                             LogHelper.put("沒有查詢到item id =" + item_id + "對應的資訊");
214                             continue;
215                         }
216                     }
217                     else
218                     {
219                         Thread.Sleep(this.wait_sec);
220                         LogHelper.put("沒有查詢到item id2 =" + item_id + "對應的資訊");
221                         continue;
222                     }
223                     //# 判斷當前庫存是否有貨
224 
225                     string xpath1 = "//div[@class=\"product-list\"]/div[@class=\"product\"]/div[@class=\"price-and-detail\"]/div[@class=\"price\"]/span[@class=\"noStock\"]";
226                     if (this.checkIsExists(By.XPath(xpath1)))
227                     {
228                         string txt = this.driver.FindElement(By.XPath(xpath1)).Text;
229                         if (txt == this.no_stock)
230                         {
231                             //# 當前無貨
232                             Thread.Sleep(this.wait_sec);
233                             LogHelper.put("查詢一次" + item_id + ",無貨");
234                             continue;
235                         }
236                     }
237                     //# 鏈接path1
238                     string xpath2 = "//div[@class=\"product-list\"]/div[@class=\"product\"]/div[@class=\"imgDiv\"]/a";
239                     //# 判斷是否加載完畢
240                     //# this.waiting((By.CLASS_NAME, "imgDiv"))
241                     if (this.checkIsExists(By.XPath(xpath2)))
242                     {
243                         this.driver.FindElement(By.XPath(xpath2)).Click();
244                         Thread.Sleep(this.wait_sec);
245                         //# 加入購物車
246                         if (this.checkIsExists(By.ClassName("add-to-cart")))
247                         {
248                             this.driver.FindElement(By.ClassName("add-to-cart")).Click();
249                             LogHelper.put("加入購物車成功,商品item-id:" + item_id);
250                             break;
251                         }
252                         else
253                         {
254                             LogHelper.put("未找到加入購物車按鈕");
255                         }
256                     }
257                     else
258                     {
259                         LogHelper.put("沒有查詢到,可能是商品編碼不對,或者已下架");
260                     }
261                     Thread.Sleep(this.wait_sec);
262                 }
263                 catch (Exception e)
264                 {
265                     Thread.Sleep(this.wait_sec);
266                     LogHelper.put(e);
267                 }
268             }
269         }
270 
271         /// <summary>
272         /// 判斷是否存在
273         /// </summary>
274         /// <param name="by"></param>
275         /// <returns></returns>
276         private bool checkIsExists(By by)
277         {
278             try
279             {
280                 int i = 0;
281                 while (this.running && i < 3)
282                 {
283                     if (this.driver.FindElements(by).Count > 0)
284                     {
285                         break;
286                     }
287                     else
288                     {
289                         Thread.Sleep(this.wait_sec);
290                         i = i + 1;
291                     }
292                 }
293                 return this.driver.FindElements(by).Count > 0;
294             }
295             catch (Exception e)
296             {
297                 LogHelper.put(e);
298                 return false;
299             }
300         }
301 
302     }
303 }
View Code

關于日志幫助類,代碼如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using log4net;
 7 
 8 [assembly: log4net.Config.XmlConfigurator(Watch = true)]
 9 namespace AiSmoking.Core
10 {
11     /// <summary>
12     /// 日志幫助類
13     /// </summary>
14     public static class LogHelper
15     {
16         /// <summary>
17         /// 日志實體
18         /// </summary>
19         private static ILog logInstance = LogManager.GetLogger("smoking");
20 
21         private static Queue<string> queue = new Queue<string>(2000);
22 
23         public static void put(string msg)
24         {
25             queue.Enqueue(msg);
26             WriteLog(msg, LogLevel.Info);
27         }
28 
29         public static void put(Exception ex)
30         {
31             WriteLog(ex.StackTrace, LogLevel.Error);
32         }
33 
34         public static string get()
35         {
36             if (queue.Count > 0)
37             {
38                 return queue.Dequeue();
39             }
40             else
41             {
42                 return string.Empty;
43             }
44         }
45 
46         public static void WriteLog(string message, LogLevel level)
47         {
48             switch (level)
49             {
50                 case LogLevel.Debug:
51                     logInstance.Debug(message);
52                     break;
53                 case LogLevel.Error:
54                     logInstance.Error(message);
55                     break;
56                 case LogLevel.Fatal:
57                     logInstance.Fatal(message);
58                     break;
59                 case LogLevel.Info:
60                     logInstance.Info(message);
61                     break;
62                 case LogLevel.Warn:
63                     logInstance.Warn(message);
64                     break;
65                 default:
66                     logInstance.Info(message);
67                     break;
68             }
69         }
70 
71 
72     }
73 
74 
75     public enum LogLevel
76     {
77         Debug = 0,
78         Error = 1,
79         Fatal = 2,
80         Info = 3,
81         Warn = 4
82     }
83 }
View Code

備注

行路難·其一

【作者】李白 【朝代】唐

金樽清酒斗十千,玉盤珍羞直萬錢,

停杯投箸不能食,拔劍四顧心茫然,

欲渡黃河冰塞川,將登太行雪滿山,

閑來垂釣碧溪上,忽復乘舟夢日邊,

行路難,行路難,多歧路,今安在?

長風破浪會有時,直掛云帆濟滄海, 

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/135.html

標籤:WinForm

上一篇:DevExpress的WidgetView的使用介紹

下一篇:應用程式使用統計資訊 – .NET CORE(C#) WPF界面設計

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more