Selenium(二):選擇元素的基本方法


1. 選擇元素的基本方法

對於百度搜索頁面,如果我們想自動化輸入愛編程的小灰灰,怎么做呢?

這就是在網頁中,操控界面元素。

web界面自動化,要操控元素,首先需要選擇界面元素 ,或者說定位界面元素

就是先告訴瀏覽器,你要操作哪個界面元素, 讓它找到你要操作的界面元素。

我們必須要讓瀏覽器先找到元素,然后才能操作元素。

1.1 查看元素的方法

對應web自動化來說,就是要告訴瀏覽器,你要操作的界面元素是什么。

那么,怎么告訴瀏覽器呢?

方法就是:告訴瀏覽器,你要操作的這個web元素的特征。

就是告訴瀏覽器,這個元素它有什么與眾不同的地方,可以讓瀏覽器一下子找到它。

元素的特征怎么查看?

可以使用瀏覽器的開發者工具欄幫我們查看、選擇 web 元素。

請大家用chrome瀏覽器訪問百度,按F12后,點擊下圖箭頭處的Elements標簽,即可查看頁面對應的HTML 元素

 

然后,再點擊最左邊的圖標,如下所示

 

之后,鼠標在界面上點擊哪個元素,就可以查看該元素對應的html標簽了。

也可以直接在你想要查看的位置,右鍵選擇檢查,就可以直接定位到元素了。

1.2 根據元素的id屬性選擇元素

我們在input中,可以看到有一個屬性叫id。

我們可以把id想象成元素的編號,是用來在html中標記該元素的。根據規范,如果元素有id屬性,這個id必須是當前html中唯一的。

所以如果元素有id,根據id選擇元素是最簡單高效的方式。

這里,百度搜索框元素的id值為kw

下面的代碼,可以自動化在瀏覽器中訪問百度,並且在輸入框中搜索愛編程的小灰灰。

大家可以運行一下看看。

from selenium import webdriver

# 創建 WebDriver 對象,指明使用chrome瀏覽器驅動
wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

# 調用WebDriver 對象的get方法 可以讓瀏覽器打開指定網址
wd.get('https://www.baidu.com')

# 根據id選擇元素,返回的就是該元素對應的WebElement對象
element = wd.find_element_by_id('kw')

# 通過該 WebElement對象,就可以對頁面元素進行操作了
# 比如輸入字符串到 這個 輸入框里
element.send_keys('愛編程的小灰灰\n')

其中

wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

前面講過,driver賦值的是WebDriver類型的對象,我們可以通過這個對象來操控瀏覽器,比如打開網址、選擇界面元素等。

下面的代碼 

wd.find_element_by_id('kw')

使用了 WebDriver對象的find_element_by_id方法。

這行代碼運行時,就會發起一個請求,通過瀏覽器驅動轉發給瀏覽器,告訴它,需要選擇一個id為kw的元素。

瀏覽器找到id為kw的元素后,將結果通過瀏覽器驅動返回給自動化程序,所以find_element_by_id方法會返回一個WebElement 類型的對象。

這個WebElement對象可以看成是對應頁面元素的遙控器。

我們通過這個WebElement對象,就可以操控對應的界面元素。

比如 :

調用這個對象的send_keys方法就可以在對應的元素中輸入字符串,

調用這個對象的click方法就可以點擊該元素。

1.3 根據class屬性、tag名選擇元素 

1.3.1 根據class屬性選擇元素

web自動化的難點和重點之一,就是如何選擇我們想要操作的web頁面元素。

除了根據元素的id,我們還可以根據元素的class屬性選擇元素。

就像一個學生張三可以定義類型為中國人或者學生一樣,中國人和學生都是張三的類型。

元素也有類型, class 屬性就用來標志着元素類型。

html代碼:

<body>
        <div class="raise"><span>喜羊羊</span></div>
        <div class="raise"><span>美羊羊</span></div>
        <div class="raise"><span>暖羊羊</span></div>

        <div class="wolf"><span>灰太狼</span></div>
        <div class="wolf"><span>紅太狼</span></div>
        <div class="wolf"><span>小灰灰</span></div>
</body>

所有的羊元素都有個class屬性值為raise。

所有的狼元素都有個class屬性值為wolf。

如果我們要選擇所有的狼,就可以使用方法find_elements_by_class_name。

注意element后面多了個s。

wd.find_elements_by_class_name('wolf')

注意:

find_elements_by_class_name方法返回的是找到的符合條件的所有元素 (這里有3個元素), 放在一個列表中返回。

而如果我們使用find_element_by_class_name (注意少了一個s) 方法,就只會返回第一個元素。

大家可以運行如下代碼看看。(注意html文件的路徑)

from selenium import webdriver

# 創建WebDriver實例對象,指明使用chrome瀏覽器驅動
wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

# WebDriver 實例對象的get方法 可以讓瀏覽器打開指定網址
wd.get('http://127.0.0.1:8020/day01/index.html')

# 根據 class name 選擇元素,返回的是一個列表
# 里面都是class屬性值為wolf的元素對應的WebElement對象
elements = wd.find_elements_by_class_name('wolf')

# 取出列表中的每個WebElement對象,打印出其text屬性的值
# text屬性就是該WebElement對象對應的元素在網頁中的文本內容
for element in elements:
    print(element.text)

首先,大家要注意:通過WebElement對象的text屬性可以獲取該元素在網頁中的文本內容。

所以下面的代碼,可以打印出element對應網頁元素的文本 。

print(element.text)

如果我們把

elements = wd.find_elements_by_class_name('wolf')

去掉一個s,改為

element = wd.find_element_by_class_name('wolf')
print(element.text)

那么返回的就是第一個class屬性為wolf的元素,也就是這個元素。

<div class="wolf"><span>灰太狼</span></div>

就像一個學生張三可以定義有多個類型: 中國人和學生,中國人和學生都是張三的類型。

元素也可以有類型,多個class類型的值之間用空格隔開,比如

<span class="chinese student">張三</span>

注意,這里span元素有兩個class屬性,分別是chinese和student,而不是一個名為chinese student的屬性。

我們要用代碼選擇這個元素,可以指定任意一個class屬性值,都可以選擇到這個元素,如下

element = wd.find_elements_by_class_name('chinese')

或者 

element = wd.find_elements_by_class_name('student')

而不能這樣寫

element = wd.find_elements_by_class_name('chinese student')

1.3.2 根據tag名選擇元素

和class方法差不多的,我們可以通過方法find_elements_by_tag_name,選擇所有的tag名為div的元素,如下所示 

from selenium import webdriver

wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

wd.get('http://127.0.0.1:8020/day01/index.html')

# 根據 tag name 選擇元素,返回的是 一個列表
# 里面 都是 tag 名為 div 的元素對應的 WebElement對象
elements = wd.find_elements_by_tag_name('div')

# 取出列表中的每個 WebElement對象,打印出其text屬性的值
# text屬性就是該 WebElement對象對應的元素在網頁中的文本內容
for element in elements:
    print(element.text)

1.3.3 find_elementfind_elements的區別

使用find_elements選擇的是符合條件的所有元素,如果沒有符合條件的元素,返回空列表。

使用find_element選擇的是符合條件的第一個 元素, 如果沒有符合條件的元素, 拋出 NoSuchElementException異常。

1.4 通過WebElement對象選擇元素

不僅WebDriver對象有選擇元素的方法,WebElement對象也有選擇元素的方法。

WebElement對象也可以調用find_elements_by_xxx,find_element_by_xxx之類的方法。

WebDriver對象選擇元素的范圍是整個web頁面,而WebElement對象選擇元素的范圍是該元素的內部。

html代碼:

<div id='container'>
    <span>內層11</span>
    <span>內層12</span>
    <span>內層13</span>
</div>
<div>
    <span>內層21</span>
    <span>內層22</span>
    <span>內層23</span>
</div>
from selenium import webdriver

wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

wd.get('http://127.0.0.1:8020/day01/index.html')

element = wd.find_element_by_id('container')

# 限制 選擇元素的范圍是 id 為 container 元素的內部。
spans = element.find_elements_by_tag_name('span')
for span in spans:
    print(span.text)

輸出結果就只有

 

1.5 等待界面元素出現

在我們進行網頁操作的時候,有的元素內容不是可以立即出現的,可能會等待一段時間。

比如百度搜索一個詞語,我們點擊搜索后,瀏覽器需要把這個搜索請求發送給百度服務器, 百度服務器進行處理后,把搜索結果返回給我們。

所以,從點擊搜索到得到結果,需要一定的時間,只是通常百度服務器的處理比較快,我們感覺好像是立即出現了搜索結果。

百度搜索的每個結果對應的界面元素,其ID分別是數字 1, 2 ,3, 4... 

如下:

 

那么我們可以試試用如下代碼,來將第一個搜索結果里面的文本內容打印出來。 

from selenium import webdriver

wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

wd.get('https://www.baidu.com')

element = wd.find_element_by_id('kw')

element.send_keys('愛編程的小灰灰\n')

# id 為 1 的元素 就是第一個搜索結果
element = wd.find_element_by_id('1')

# 打印出 第一個搜索結果的文本字符串
print (element.text)

如果大家去運行一下,就會發現有如下異常拋出

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"id","selector":"1"}

NoSuchElementException的意思就是在當前的網頁上找不到該元素,找不到id為1的元素。

為什么呢?

因為我們的代碼執行的速度比百度服務器響應的速度快。

百度還沒有來得及返回搜索結果,我們就執行了如下代碼

element = wd.find_element_by_id('1')

在那短暫的瞬間,網頁上是沒有用id為1的元素的(因為還沒有搜索結果呢)。自然就會報告錯誤id為1的元素不存在了。

那么怎么解決這個問題呢?

相信很多人都能想到,就是在點擊搜索后,用sleep來等待幾秒鍾,等百度服務器返回結果后,再去選擇id為1的元素,就像下面這樣

from selenium import webdriver

wd = webdriver.Chrome(r'E:\webdrivers\chromedriver.exe')

wd.get('https://www.baidu.com')

element = wd.find_element_by_id('kw')

element.send_keys('愛編程的小灰灰\n')

# 等待 2 秒
from time import sleep
sleep(2)

# 2 秒 過后,再去搜索
element = wd.find_element_by_id('1')

# 打印出 第一個搜索結果的文本字符串
print (element.text)

大家可以運行一下,基本是可以的,不會再報錯了。

但是,這樣的方法有一個很大的問題,就是設置等待多長的時間合適呢?

這次百度網站反應可能比較快,我們等了一秒鍾就可以了。

但是誰知道下次他的反應是不是還這么快呢?百度也曾經出現過服務器癱瘓的事情。

可能有的人說,我干脆sleep比較長的時間,等待 20 秒,總歸可以了吧?

這樣也有很大問題,假如一個自動化程序里面需要10次等待,就要花費 200秒。而可能大部分時間,服務器反映都是很快的,根本不需要等20秒,這樣就造成了大量的時間浪費了。

Selenium提供了一個更合理的解決方案,是這樣的:

當發現元素沒有找到的時候,並不立即返回找不到元素的錯誤。

而是周期性(每隔半秒鍾)重新尋找該元素,直到該元素找到,或者超出指定最大等待時長,這時才拋出異常(如果是find_elements之類的方法,則是返回空列表)。

Selenium的Webdriver對象有個方法叫implicitly_wait

該方法接受一個參數,用來指定最大等待時長。

如果我們加入如下代碼

wd.implicitly_wait(10)

那么后續所有的find_element或者find_elements之類的方法調用都會采用上面的策略:

如果找不到元素,每隔半秒鍾再去界面上查看一次,直到找到該元素,或者過了10秒最大時長。

這樣,我們的百度搜索的例子的最終代碼如下

from selenium import webdriver

wd = webdriver.Chrome()

# 設置最大等待時長為 10秒
wd.implicitly_wait(10)

wd.get('https://www.baidu.com')

element = wd.find_element_by_id('kw')

element.send_keys('愛編程的小灰灰\n')

element = wd.find_element_by_id('1')

print (element.text)

大家再運行一下,可以發現不會有錯誤了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM