筆者近期都在忙著籌備第二個專案,最近在研究Selenium的使用方法,日後會用來抓取專案會使用的資料。Selenium常常伴隨著出現的關鍵字可能會是自動化測試、爬蟲,究竟Selenium到底是什麼,又可以做些甚麼事情,就讓我們繼續看下去!
什麼是Selenium?
Selenium是為瀏覽器自動化需求所設計的開源工具集合(參1),像是在網頁中常見的操作功能,透過網址拜訪該網頁、上下頁等,可透過各程式語言(C#、Python、JAVA等)開發,並轉換成HTTP的請求,並直接運行在瀏覽器中。更白話一點,就是透過程式碼模擬人類操作瀏覽器的行為。
在正式講解Selenium的操作之前,也順便提一下Selenium的歷史脈絡(參2)。從2004年起,由ThoughtWorks公司開發出了Selenium 1.0,Selenium 1.0 包含了 Selenium IDE + Selenium Grid + Selenium RC。Selenium 1.0使用JavaScript與瀏覽器溝通及調用元素,統一透過Selenium RC的伺服器作為代理伺服器,其中Client Libraries是撰寫指令程式碼,用來控制Selenium Server,而Selenium Server則負責控制瀏覽器的行為(參3)。
(圖片來源,參4)
2007年ThoughtWorks的Simon Stewart開發出名為WebDriver的高級瀏覽器自動化工具,故Selenium 2.0 = WebDriver + Selenium 1.0。Selenium 2.0的核心就在於將Selenium RC 更換成WebDriver,WebDriver一樣可以直接操作瀏覽元素,甚至還可以控制瀏覽器本身(視窗大小、截圖、新增關閉頁面等),但WebDriver是針對各瀏覽器而獨立設計,故在速度上會比Selenium 1.0更快。另外,順道一提,自動化測試工具可不是只有Selenium,像QTP、loadrunner等都可以做到自動化測試 (參5)。
圖片來源(參4),
Selenium的概念與操作
搜索 Selenium關鍵字,出現的幾乎都是Python的教學,筆者之前也是使用Python的Selenium,但感覺C#在觀念上都差不多,只是函數名稱有點不太一樣而已。剛剛上一節提到,Selenium 2.0之後重點在於WebDriver,而WebDriver又依瀏覽器類型及版本作區分,所以第一個學習重點就是要找到對應的WebDriver。
查看瀏覽器的版本
Chrome:
Firefox:
確定版本後就可以去下載對應的版本囉(參6、參7),其他的瀏覽器就自行尋找囉!
在C#專案中安裝並啟用WebDriver
如果把WebDriver的檔案跟C#專案放在同一個資料夾裡,只要下列指令就可以囉
IWebDriver driver = new ChromeDriver();
因筆者想做一些測試,故建立類別放入瀏覽器類別與路徑
IWebDriver driver;
public Selenium(String browser, string path)
{
switch (browser.ToLower())
{
case "chrome":
this.driver = new ChromeDriver(path);
break;
case "firefox":
this.driver = new FirefoxDriver(path);
break;
case "ie":
this.driver = new InternetExplorerDriver(path);
default:
this.driver = new ChromeDriver(path);
break;
}
}
Selenium常見功能
這裡介紹一些常用的方法。
IWebDriver driver = new ChromeDriver();
//拜訪URL
driver.Navigate().GoToUrl("https://www.ptt.cc/bbs/hotboards.html")
//返回上一頁
driver.Navigate().Back ( );
//設定cookie,如PTT 八卦版需要new Cookie("over18","1")
driver.Manage().Cookies.AddCookie(new Cookie(key, value));
//找網頁元素,定位後可再接Text、Click、SendKeys等取文字或瀏覽器操作
driver.FindElement()
driver.FindElements()
//定位元素
//driver.FindElement(By.Id("main-content"))
//driver.FindElement(By.CssSelector("#main-content > div:nth-child(4) > span,article-meta-value"))
//driver.FindElement(By.ClassName("push"))
Selenium進階寫法,利用轉型實現更多功能
//透過javascript的語法,新增分頁並拜訪網頁
((IJavaScriptExecutor)driver).ExecuteScript($"window.open('{url}');");
driver.SwitchTo().Window(driver.WindowHandles.Last()); //將控制權更改為最後一個分頁
driver.SwitchTo().Window(driver.WindowHandles[driver.WindowHandles.Count - 1]) //也可以這樣寫
//截圖
Screenshot screenshot = ((ITakesScreenshot)selenium).GetScreenshot();
screenshot.SaveAsFile("beauty.jpg", ScreenshotImageFormat.Jpeg);
強制等待、顯式等待與隱式等待
使用Selenium在取網頁元素時,可能會發生該元素尚未被渲染,所以會有可能抓不到該元素,因此一個完整的selenium架構中,也應該要寫好等待的規則,並針對報錯的狀態做處理。
強制等待: 顧名思義就是傻傻地等,故非必要不使用。
隱式等待: 比強制等待更聰明一點,只需要宣告一次,之後只要開啟新的頁面或URL,會判斷整個網頁是否載入完畢(網頁Tab的小圈圈是否轉完),若秒數內沒載完就報錯。但因為要等待整個頁面載完才能執行下個動作。
顯式等待: 直接對要等待的元素前面聲明,所以通常會跟FindElement一起出現。
//傻傻地等
Thread.Sleep(10) //不管怎樣,等10秒就對了
//隱式等待,秒數內載完網頁所有元素,否則報錯。載完就提早進入下一步,故比Thread.Sleep更優
this.driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); //10秒內載完網頁所有元素
//顯式等待,需要給定元素位置,只要該元素找到就進入下一步。
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.ElementExists(by));
還有查到一些加速的方法,例如關掉圖片下載、關閉瀏覽器顯示等等,有興趣自己玩玩看吧(參8、參9)
參考資料
- https://blog.poychang.net/note-selenium/
- https://en.wikipedia.org/wiki/Selenium_(software)
- https://www.itread01.com/content/1549266495.html
- https://ek21.com/news/tech/27426/
- https://kknews.cc/zh-tw/code/6akr49v.html
- https://chromedriver.chromium.org/downloads
- https://github.com/mozilla/geckodriver/releases
- www.twblogs.net/a/5d53092dbd9eee541c3173fc
- https://www.itdaan.com/tw/8cfbedf8cdf1e14d6419af20f401a63e