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

1. 選擇元素的基本方法

對於百度搜索頁面,若是咱們想自動化輸入愛編程的小灰灰,怎麼作呢?html

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

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

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

咱們必需要讓瀏覽器先找到元素,而後才能操做元素。瀏覽器

1.1 查看元素的方法

對應web自動化來講,就是要告訴瀏覽器,你要操做的界面元素是什麼。服務器

那麼,怎麼告訴瀏覽器呢?工具

方法就是:告訴瀏覽器,你要操做的這個web元素的特徵。網站

就是告訴瀏覽器,這個元素它有什麼不同凡響的地方,可讓瀏覽器一會兒找到它。spa

元素的特徵怎麼查看?3d

可使用瀏覽器的開發者工具欄幫咱們查看、選擇 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)

你們再運行一下,能夠發現不會有錯誤了。

相關文章
相關標籤/搜索