SeleniumWebdriver元素定位的八種常用方式
在使用selenium webdriver進(jìn)行元素定位時(shí),通常使用findElement或findElements方法結(jié)合By類(lèi)返回的元素句柄來(lái)定位元素。其中By類(lèi)的常用定位方式共八種,現(xiàn)分別介紹如下。
1. By.name()
頁(yè)面源碼如下:
<button id="gbqfba"aria-label="Google Search" name="btnK" class="gbqfba">Google Search
當(dāng)我們要用name屬性來(lái)引用這個(gè)button并點(diǎn)擊它時(shí),代碼如下:
public class SearchButtonByName {
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
WebElement searchBox =driver.findElement(By.name("btnK"));
searchBox.click();
}
}
2. By.id()
頁(yè)面源碼如下:
<button id="gbqfba" aria-label="GoogleSearch" name="btnK" class="gbqfba">Google Search
要引用該button并點(diǎn)擊它時(shí),代碼如下:
public class SearchButtonById {
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
WebElement searchBox =driver.findElement(By.id("gbqfba"));
searchBox.click();
}
}
3. By.tagName()
該方法可以通過(guò)元素的標(biāo)簽名稱(chēng)來(lái)查找元素。該方法跟之前兩個(gè)方法的區(qū)別是,這個(gè)方法搜索到的元素通常不止一個(gè),所以一般建議結(jié)合使用findElements方法來(lái)使用。比如我們現(xiàn)在要查找頁(yè)面上有多少個(gè)button,就可以用button這個(gè)tagName來(lái)進(jìn)行查找,代碼如下:
public class SearchPageByTagName{
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
List buttons =driver.findElements(By.tagName("button"));
System.out.println(buttons.size()); //打印出button的個(gè)數(shù)
}
}
另外,在使用tagName方法進(jìn)行定位時(shí),還有一個(gè)地方需要注意的是,通常有些HTML元素的tagName是相同的,如下圖(1)所示。
圖(1)
從圖中我們可以看到,單選框、復(fù)選框、文本框和密碼框的元素標(biāo)簽都是input,此時(shí)單靠tagName無(wú)法準(zhǔn)確地得到我們想要的元素,需要結(jié)合type屬性才能過(guò)濾出我們要的元素。示例代碼如下:
public class SearchElementsByTagName{
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
List allInputs =driver.findElements(By.tagName("input"));
//只打印所有文本框的值
for(WebElement e: allInputs){
if(e.getAttribute(“type”).equals(“text”)){
System.out.println(e.getText().toString()); //打印出每個(gè)文本框里的值
}
}
}
}
4. By.className()
className屬性是利用元素的css樣式表所引用的偽類(lèi)名稱(chēng)來(lái)進(jìn)行元素查找的方法。對(duì)于任何HTML頁(yè)面的元素來(lái)說(shuō),一般程序員或頁(yè)面設(shè)計(jì)師會(huì)給元素直接賦予一個(gè)樣式屬性或者利用css文件里的偽類(lèi)來(lái)定義元素樣式,使元素在頁(yè)面上顯示時(shí)能夠更加美觀。一般css樣式表可能會(huì)長(zhǎng)成下面這個(gè)樣子:
.buttonStyle{
width: 50px;
height: 50px;
border-radius: 50%;
margin: 0% 2%;
}
定義好后,就可以在頁(yè)面元素中引用上述定義好的樣式,如下:
<buttonname="sampleBtnName" id="sampleBtnId" class="buttonStyle">I'mButton
如果此時(shí)我們要通過(guò)className屬性來(lái)查找該button并操作它的話(huà),就可以使用className屬性了,代碼如下:
public class SearchElementsByClassName{
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
WebElement searchBox =driver.findElement(By.className("buttonStyle"));
searchBox.sendKeys("Hello, world");
}
}
注意:使用className來(lái)進(jìn)行元素定位時(shí),有時(shí)會(huì)碰到一個(gè)元素指定了若干個(gè)class屬性值的“復(fù)合樣式”的情況,如下面這個(gè)button:。這個(gè)button元素指定了三個(gè)不同的css偽類(lèi)名作為它的樣式屬性值,此時(shí)就必須結(jié)合后面要介紹的cssSelector方法來(lái)定位了,稍后會(huì)有詳細(xì)例子。
5. By.linkText()
這個(gè)方法比較直接,即通過(guò)超文本鏈接上的文字信息來(lái)定位元素,這種方式一般專(zhuān)門(mén)用于定位頁(yè)面上的超文本鏈接。通常一個(gè)超文本鏈接會(huì)長(zhǎng)成這個(gè)樣子:About Google。我們定位這個(gè)元素時(shí),可以使用下面的代碼進(jìn)行操作:
public class SearchElementsByLinkText{
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
WebElement aboutLink =driver.findElement(By.linkText("About Google"));
aboutLink.click();
}
}
6. By.partialLinkText()
這個(gè)方法是上一個(gè)方法的擴(kuò)展。當(dāng)你不能準(zhǔn)確知道超鏈接上的文本信息或者只想通過(guò)一些關(guān)鍵字進(jìn)行匹配時(shí),可以使用這個(gè)方法來(lái)通過(guò)部分鏈接文字進(jìn)行匹配。代碼如下:
public class SearchElementsByPartialLinkText{
public static void main(String[] args){
WebDriver driver = new FirefoxDriver();
driver.get("http://www.forexample.com");
WebElement aboutLink = driver.findElement(By.partialLinkText("About"));
aboutLink.click();
}
}
注意:使用這種方法進(jìn)行定位時(shí),可能會(huì)引起的問(wèn)題是,當(dāng)你的頁(yè)面中不止一個(gè)超鏈接包含About時(shí),findElement方法只會(huì)返回第一個(gè)查找到的元素,而不會(huì)返回所有符合條件的元素。如果你要想獲得所有符合條件的元素,還是只能使用findElements方法。
7. By.xpath()
這個(gè)方法是非常強(qiáng)大的元素查找方式,使用這種方法幾乎可以定位到頁(yè)面上的任意元素。在正式開(kāi)始使用XPath進(jìn)行定位前,我們先了解下什么是XPath。XPath是XML Path的簡(jiǎn)稱(chēng),由于HTML文檔本身就是一個(gè)標(biāo)準(zhǔn)的XML頁(yè)面,所以我們可以使用XPath的語(yǔ)法來(lái)定位頁(yè)面元素。
假設(shè)我們現(xiàn)在以圖(2)所示HTML代碼為例,要引用對(duì)應(yīng)的對(duì)象,XPath語(yǔ)法如下:
圖(2)
絕對(duì)路徑寫(xiě)法(只有一種),寫(xiě)法如下:
引用頁(yè)面上的form元素(即源碼中的第3行):/html/body/form[1]
注意:1. 元素的xpath絕對(duì)路徑可通過(guò)firebug直接查詢(xún)。2. 一般不推薦使用絕對(duì)路徑的寫(xiě)法,因?yàn)橐坏╉?yè)面結(jié)構(gòu)發(fā)生變化,該路徑也隨之失效,必須重新寫(xiě)。3. 絕對(duì)路徑以單/號(hào)表示,而下面要講的相對(duì)路徑則以//表示,這個(gè)區(qū)別非常重要。另外需要多說(shuō)一句的是,當(dāng)xpath的路徑以/開(kāi)頭時(shí),表示讓Xpath解析引擎從文檔的根節(jié)點(diǎn)開(kāi)始解析。當(dāng)xpath路徑以//開(kāi)頭時(shí),則表示讓xpath引擎從文檔的任意符合的元素節(jié)點(diǎn)開(kāi)始進(jìn)行解析。而當(dāng)/出現(xiàn)在xpath路徑中時(shí),則表示尋找父節(jié)點(diǎn)的直接子節(jié)點(diǎn),當(dāng)//出現(xiàn)在xpath路徑中時(shí),表示尋找父節(jié)點(diǎn)下任意符合條件的子節(jié)點(diǎn),不管嵌套了多少層級(jí)(這些下面都有例子,大家可以參照來(lái)試驗(yàn))。弄清這個(gè)原則,就可以理解其實(shí)xpath的路徑可以絕對(duì)路徑和相對(duì)路徑混合在一起來(lái)進(jìn)行表示,想怎么玩就怎么玩。
下面是相對(duì)路徑的引用寫(xiě)法:
查找頁(yè)面根元素://
查找頁(yè)面上所有的input元素://input
查找頁(yè)面上第一個(gè)form元素內(nèi)的直接子input元素(即只包括form元素的下一級(jí)input元素,使用絕對(duì)路徑表示,單/號(hào))://form[1]/input
查找頁(yè)面上第一個(gè)form元素內(nèi)的所有子input元素(只要在form元素內(nèi)的input都算,不管還嵌套了多少個(gè)其他標(biāo)簽,使用相對(duì)路徑表示,雙//號(hào))://form[1]//input
查找頁(yè)面上第一個(gè)form元素內(nèi)的最后一個(gè)input元素://form[1]//input[last()](注意:在xpath中沒(méi)有first()這樣的方法,用//form[1]//input[1]這種表示法就可以取第一個(gè))
查找頁(yè)面上第一個(gè)form元素://form[1]
查找頁(yè)面上id為loginForm的form元素://form[@id='loginForm']
查找頁(yè)面上具有name屬性為username的input元素://input[@name='username']
查找頁(yè)面上id為loginForm的form元素下的第一個(gè)input元素://form[@id='loginForm']/input[1](注意:在xpath中沒(méi)有第0元素這樣的表示方法,都是從1開(kāi)始)
查找頁(yè)面具有name屬性為contiune并且type屬性為button的input元素://input[@name='continue'][@type='button']
查找頁(yè)面上id為loginForm的form元素下第4個(gè)input元素://form[@id='loginForm']/input[4]
Xpath功能很強(qiáng)大,所以也可以寫(xiě)得更加復(fù)雜一些,如下面圖(3)的HTML源碼。
圖(3)
如果我們現(xiàn)在要引用id為“J_password”的input元素,該怎么寫(xiě)呢?我們可以像下面這樣寫(xiě):
WebElement password =driver.findElement(By.xpath("//[@id='J_login_form']/dl/dt/input[@id='J_password']"));
也可以寫(xiě)成:
WebElement password = driver.findElement(By.xpath("//[@id='J_login_form']///input[@id='J_password']"));
這里解釋一下,其中//[@id=’J_login_form’]這一段是指在根元素下查找任意id為J_login_form的元素,此時(shí)相當(dāng)于引用到了form元素。后面的路徑必須按照源碼的層級(jí)依次往下寫(xiě)。因?yàn)槲覀円业膇nput元素包含在一個(gè)dt標(biāo)簽內(nèi),而dt又包含在dl標(biāo)簽內(nèi),所以中間必須寫(xiě)上dl和dt兩層,才到input這層。當(dāng)然我們也可以用號(hào)省略具體的標(biāo)簽名稱(chēng),但元素的層級(jí)關(guān)系必須體現(xiàn)出來(lái),比如我們不能寫(xiě)成//[@id='J_login_form']/input[@id='J_password'],這樣肯定會(huì)報(bào)錯(cuò)的。
前面講的都是xpath中基于準(zhǔn)確元素屬性的定位,其實(shí)xpath作為定位神器也可以用于模糊匹配。比如下面圖(4)所示代碼:
圖(4)
這段代碼中的“退出”這個(gè)超鏈接,沒(méi)有標(biāo)準(zhǔn)id元素,只有一個(gè)rel和href,不是很好定位。不妨我們就用xpath的幾種模糊匹配模式來(lái)定位它吧,主要有三種方式,舉例如下。
a. 用contains關(guān)鍵字,定位代碼如下:
driver.findElement(By.xpath(“//a[contains(@href,‘logout’)]”));
這句話(huà)的意思是尋找頁(yè)面中href屬性值包含有l(wèi)ogout這個(gè)單詞的所有a元素,由于這個(gè)退出按鈕的href屬性里肯定會(huì)包含logout,所以這種方式是可行的,也會(huì)經(jīng)常用到。其中@后面可以跟該元素任意的屬性名。
b. 用start-with,定位代碼如下:
driver.findElement(By.xpath(“//a[starts-with(@rel, ‘nofo’)]));
這句的意思是尋找rel屬性以nofo開(kāi)頭的a元素。其中@后面的rel可以替換成元素的任意其他屬性。
c. 用Text關(guān)鍵字,定位代碼如下:
driver.findElement(By.xpath(“//[text()=’退出’]));
這個(gè)方法可謂相當(dāng)霸氣啊。直接查找頁(yè)面當(dāng)中所有的退出二字,根本就不用知道它是個(gè)a元素了。這種方法也經(jīng)常用于純文字的查找。
另外,如果知道超鏈接元素的文本內(nèi)容,也可以用driver.findElement(By.xpath(“//a[contains(text(), ’退出’)]));
這種方式一般用于知道超鏈接上顯示的部分或全部文本信息時(shí),可以使用。
最后,關(guān)于xpath這種定位方式,webdriver會(huì)將整個(gè)頁(yè)面的所有元素進(jìn)行掃描以定位我們所需要的元素,所以這是一個(gè)非常費(fèi)時(shí)的操作,如果你的腳本中大量使用xpath做元素定位的話(huà),將導(dǎo)致你的腳本執(zhí)行速度大大降低,所以請(qǐng)慎用。
8. By.cssSelector()
cssSelector這種元素定位方式跟xpath比較類(lèi)似,但執(zhí)行速度較快,而且各種瀏覽器對(duì)它的支持都相當(dāng)?shù)轿?,所以功能也是蠻強(qiáng)大的。
下面是一些常見(jiàn)的cssSelector的定位方式:
定位id為flrs的div元素,可以寫(xiě)成:#flrs 注:相當(dāng)于xpath語(yǔ)法的//div[@id=’flrs’]
定位id為flrs下的a元素,可以寫(xiě)成 #flrs > a 注:相當(dāng)于xpath語(yǔ)法的//div[@id=’flrs’]/a
定位id為flrs下的href屬性值為/forexample/about.html的元素,可以寫(xiě)成: #flrs > a[href=”/forexample/about.html”]
如果需要指定多個(gè)屬性值時(shí),可以逐一加在后面,如#flrs> input[name=”username”][type=”text”]。
明白基本語(yǔ)法后,我們來(lái)嘗試用cssSelector方式來(lái)引用圖(3)中選中的那個(gè)input對(duì)象,代碼如下:
WebElement password =
driver.findElement(By.cssSelector("#J_login_form>dl>dt>input[id=’J_password’]"));
同樣必須注意層級(jí)關(guān)系,這個(gè)不能省略。
cssSelector還有一個(gè)用處是定位使用了復(fù)合樣式表的元素,之前在第4種方式className里面提到過(guò)?,F(xiàn)在我們就來(lái)看看如何通過(guò)cssSelector來(lái)引用到第4種方式中提到的那個(gè)button。button代碼如下:
cssSelector引用元素代碼如下:
driver.findElement(By.cssSelector("button.btn.btn_big.btn_submit"))。這樣就可以順利引用到使用了復(fù)合樣式的元素了。
此外,cssSelector還有一些高級(jí)用法,如果熟練后可以更加方便地幫助我們定位元素,如我們可以利用^用于匹配一個(gè)前綴,$用于匹配一個(gè)后綴,用于匹配任意字符。例如:
匹配一個(gè)有id屬性,并且id屬性是以”idprefix”開(kāi)頭的超鏈接元素:a[id^='idprefix']
匹配一個(gè)有id屬性,并且id屬性是以”_id_sufix”結(jié)尾的超鏈接元素:a[id$='_id_sufix']
匹配一個(gè)有id屬性,并且id屬性中包含”id_pattern”字符的超鏈接元素:a[id='id_pattern']
最后再總結(jié)一下,各種方式在選擇的時(shí)候應(yīng)該怎么選擇:
1. 當(dāng)頁(yè)面元素有id屬性時(shí),最好盡量用id來(lái)定位。但由于現(xiàn)實(shí)項(xiàng)目中很多程序員其實(shí)寫(xiě)的代碼并不規(guī)范,會(huì)缺少很多標(biāo)準(zhǔn)屬性,這時(shí)就只有選擇其他定位方法。
2. xpath很強(qiáng)悍,但定位性能不是很好,所以還是盡量少用。如果確實(shí)少數(shù)元素不好定位,可以選擇xpath或cssSelector。
3. 當(dāng)要定位一組元素相同元素時(shí),可以考慮用tagName或name。
4. 當(dāng)有鏈接需要定位時(shí),可以考慮linkText或partialLinkText方式。
更多建議: