對於百度搜索頁面,若是咱們想自動化輸入愛編程的小灰灰,怎麼作呢?html
這就是在網頁中,操控界面元素。web
web界面自動化,要操控元素,首先須要選擇界面元素 ,或者說定位界面元素chrome
就是先告訴瀏覽器,你要操做哪一個界面元素, 讓它找到你要操做的界面元素。編程
咱們必需要讓瀏覽器先找到元素,而後才能操做元素。瀏覽器
對應web自動化來講,就是要告訴瀏覽器,你要操做的界面元素是什麼。服務器
那麼,怎麼告訴瀏覽器呢?工具
方法就是:告訴瀏覽器,你要操做的這個web元素的特徵。網站
就是告訴瀏覽器,這個元素它有什麼不同凡響的地方,可讓瀏覽器一會兒找到它。spa
元素的特徵怎麼查看?3d
可使用瀏覽器的開發者工具欄幫咱們查看、選擇 web 元素。
請你們用chrome瀏覽器訪問百度,按F12後,點擊下圖箭頭處的Elements標籤,便可查看頁面對應的HTML 元素
而後,再點擊最左邊的圖標,以下所示
以後,鼠標在界面上點擊哪一個元素,就能夠查看該元素對應的html標籤了。
也能夠直接在你想要查看的位置,右鍵選擇檢查,就能夠直接定位到元素了。
咱們在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方法就能夠點擊該元素。
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')
和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)
使用find_elements選擇的是符合條件的全部元素,若是沒有符合條件的元素,返回空列表。
使用find_element選擇的是符合條件的第一個 元素, 若是沒有符合條件的元素, 拋出 NoSuchElementException異常。
不只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)
輸出結果就只有
在咱們進行網頁操做的時候,有的元素內容不是能夠當即出現的,可能會等待一段時間。
好比百度搜索一個詞語,咱們點擊搜索後,瀏覽器須要把這個搜索請求發送給百度服務器, 百度服務器進行處理後,把搜索結果返回給咱們。
因此,從點擊搜索到獲得結果,須要必定的時間,只是一般百度服務器的處理比較快,咱們感受好像是當即出現了搜索結果。
百度搜索的每一個結果對應的界面元素,其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)
你們再運行一下,能夠發現不會有錯誤了。