1. 程式人生 > >JAVA抓匯實務示範(1) By using Selenium

JAVA抓匯實務示範(1) By using Selenium

JAVA抓匯實務示範(1) By using Selenium

結案實錄影片

規格單:

事前分析:

原本認為是使用Htmlunit就沒問題,但因為這個網站在SSL有做其他防範,使用公司的Htmlunit元件並無法讀取,因此開始思慮其他方法。

在考慮到這份規格單需要用到各種onclick, mouseover行為,這個網站內容更有複雜的iframe及javascript互相包覆等等,決定使用Selenium這個自動化測試用工具來做爬蟲,由於Selenium能真實模擬使用者行為,可以免除平時抓匯各種必須測試且事前分析等行為,直接跟著使用者的方向走即可。

Coding:

//基礎設定
ChromeDriver driver = null;
driver = new ChromeDriver();
//測試完成後移動視窗,這樣不會影響其他視窗
//driver.manage().window().setPosition(new Point(-2000, 0));
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
driver.manage().timeouts().pageLoadTimeout(60, TimeUnit.SECONDS);
driver.manage().timeouts().setScriptTimeout(60, TimeUnit.SECONDS);
driver.get(url);

進入頁面後正式開始

第一步 擷取左方選單
// 開啟左方選單 選取Monthly Economic Indicators
Select select = new Select(
driver.findElement(By.id(“_ctl0__ctl0_cphCentre_ContentPlaceHolder1__ctl3_ddlThemesFilter”)));
List<WebElement> options = select.getOptions();
for (WebElement option : options) {
if (option.getText().contains(“Monthly Economic Indicators”)) {
option.click();
break;
}
}

這邊自己一開始在嘗試的想法過於直覺,我把選單的選取兩個動作,點選選單後再選擇選項,這樣並沒有錯誤,但是變成過於直覺化,且點選完選項後預設是選單不會關閉,因此又要重複點選選單才能完成。

後來才使用到比較適合的方法,也就是用Selenium的Select type去接html select tag,此做法才是較正確的做法,也免除了需要點選三次的直覺法。

接到selenium的select型態後,我自己想像是select接回來的物件就是一個array,裡麵包著選單中的所有選項,因此再用select的method getOptions()去接到新的List中,就很容易可以做判斷需要的選項,再做後續動作。

下一步 開啟左方第一層所有選單 點選該選項
//50表秒數,50秒內如果指定區域(By.cssSelector(“[class=’t closed’]”)可以點選時,才去做點選的動作
new WebDriverWait(driver, 50).until(ExpectedConditions.elementToBeClickable(By.cssSelector(“[class=’t closed’]”)));

這邊先提一下這個段落,這串寫法是考量到javascript loading的時間,因為一開始在直接抓左方ul li時完全沒有辦法抓到,後來才查覺是因物件還未loading完畢,就直接去找element,所以才需要這個方法。(可以自由變更秒數及等待須要能點選的指定處)

後面每一次需要loading處都有做,但以下皆省略,以免文章過長
// 把Monthly Economic Indicators下全部沒開啟的選項開啟
List<WebElement> litags = driver.findElementsByCssSelector(“[class=’t closed’]”);
for (WebElement li : litags) {
  li.click();
}
// 點選MEI
driver.findElement(By.cssSelector(“[qid=’6617']”)).click();
new WebDriverWait(driver, 50).until(ExpectedConditions.elementToBeClickable(By.id(“menubar-customize”)));

因我需要的選項 Composite Leading Indicators (MEI),是包在Monthly Ecnomic Indicators>Composite Leading Indicators>Composite Leading Indicators(MEI)之下,所以原本預想是需要一個個做這幾個指定名稱的點選,一層層把ul展開li,但是後來想法是,因為觀察到他開啟的關鍵在於li的class='t’,如果開啟則為t opened,關閉則t closed,所以直接點選所有t close的class將目錄拉開,就可以很快速的抓到我們要的選項。

下一步 點選Customise>Selection...>Subject的選項
// 兩個mouse move to actions
Actions action = new Actions(driver);
WebElement we = driver.findElementById(“menubar-customize”);
WebElement we1 = driver.findElementById(“customize-menu-0”);
action.moveToElement(we).moveToElement(we1).perform();
// 進入dialog
By by = By.id("customize-menu-3");
driver.findElement(by).click();

因為選單必須要遊標移動到上面才會一層層浮現,所以這邊做了action,分別去找到兩個必須mouse in的id選取後去執行moveToElement行為。

點選後pop up出一個新視窗
// 切換到iframe中
driver.switchTo().frame(driver.findElementById(“DialogFrame”));

在點選進入新視窗後,經過分析發現這個div中是由iframe包住,以前在做htmlunit等等抓匯時,iframe實在是不好解,但是這邊Selenium提供了switchTo frame的method,可以直接切換到指定frame中。

// 先將全部取消選取
driver.findElementByXPath(“//a[@id=’lbtnClear_all’]”).click();
List<WebElement> inputs = driver.findElementsByCssSelector(“input[class=’igt_align’]”);
// 點需求選項
int a = 0;
for (WebElement input : inputs) {
if (a == 1 || a == 3)
input.click();
a++;
}
// 送出 回到原頁面
driver.findElementByXPath(“//a[@class=’right updatebutton’]”).click();

因我們要選擇的只有兩個選項,而預設已經有選擇大部分的checkbox,又注意到右上方有一個取消選取所有的button,所以先點選button後,指定那兩個指定選項做選取,接著點選右下方按鈕到新頁面。

WebElement we2 = driver.findElementById(“menubar-customize”);
WebElement we3 = driver.findElementById(“customize-menu-0”);
action.moveToElement(we2).moveToElement(we3).perform();
By by1 = By.id(“customize-menu-5”);
driver.findElement(by1).click();

重複步驟 換個選項

進到Customise>Selection…>Time & Frequency
// 切換到frame(DialogFrame>TimeSelector)
WebElement dialogframe = driver.findElementById(“DialogFrame”);
driver.switchTo().frame(dialogframe);
driver.switchTo().frame(driver.findElementById(“TimeSelector”));

上面已經做過pop up window進入iframe的寫法,但這邊要特別一提,並不是每次iframe都只包一層,像這邊經過分析後他是有兩層的iframe,右下方顆按鈕位於外層(黃色框框),要點選的、拉取選單的為內層(紅色框框),所以再分別進行動作時,都必須考量到目前行為是要在哪個frame中做,並做切換。

driver.findElementById(“rdoPeriodRange”).click();
Select select1 = new Select(driver.findElement(By.id(“cboMonthsFrom”)));
List<WebElement> options1 = select1.getOptions();
int n1 = 0;
for (WebElement option : options1) {
if (n1 == 0) {
option.click();
break;
    }
}
// 送出的的按鈕又在第一層(DialogFrame)
// 所以先從目前的TimeSelector回到上一層DialogFrame
driver.switchTo().parentFrame();
driver.findElementById(“lbtnViewData”).click();