一日一技:Selenium 如何獲取鼠標指向的元素?
攝影:產品經理
一些前菜,大餐隨後到
有一個同學在 Gne 的羣裏面諮詢如何通過 Selenium 獲取當前鼠標指向的元素,在我講了方法以後,他過了兩天又來問:
那麼,我今天就來寫一篇文章,具體說說應該怎麼操作。
這個方法的核心,是藉助 JavaScript 的事件 (event) 來獲取鼠標所在的元素。然後再把這個元素傳遞給 Selenium。我們先來第一步,不考慮 Selenium,只使用 JavaScript,如何獲取當前鼠標指向的元素呢?
我們首先需要知道在 JavaScript 中的一個事件句柄,叫做window.onmousemove
。默認情況下,它的值是 null:
我們可以把它的值修改成一個函數,這個函數接收一個event
參數,這樣當鼠標在網頁上移動的時候,這個函數就會被調用。而event
參數是一個對象,這個對象有兩個屬性.clientX
和.clientY
,分別表示鼠標相當於網頁的橫座標和縱座標:
function track_mouse(event){
var x = event.clientX, y = event.clientY
console.log('當前鼠標所在位置的座標:x=' + x + 'y=' + y)
}
運行效果如下圖所示:
你執行命令以後,只要在頁面上移動鼠標,你就會在控制檯看到大量的座標被打印出來。
接下來,既然你有了當前鼠標所在位置的座標,那麼你只需要根據座標查詢到這個元素是什麼就可以了。在 JavaScript 中,有一個函數叫做document.elementFromPoint
,就能實現這個效果:
function track_mouse(event){
var x = event.clientX, y = event.clientY
var element = document.elementFromPoint(x, y)
if (!element) {
return // 當前位置沒有元素
}
return element
}
那麼,如何把這個參數返回給 Selenium 呢?其實也非常簡單,我們設置一個全局變量window.hovered_element
,然後把當前鼠標對應的元素賦值給它。然後在 Selenium 中,使用.execute_script
獲取window.hovered_element
就可以了。
我們先來看看完整的 JavaScript:
window.hovered_element = null
function track_mouse(event){
var x = event.clientX, y = event.clientY
var element = document.elementFromPoint(x, y)
if (!element) {
window.hovered_element = null
return // 當前位置沒有元素
}
window.hovered_element = element
}
window.onmousemove = track_mouse
然後我們再來看看 Selenium 中的 Python 代碼:
import time
from selenium.webdriver import Chrome
driver = Chrome('./chromedriver')
driver.get('https://www.kingname.info/')
js = '''
window.hovered_element = null
function track_mouse(event){
var x = event.clientX, y = event.clientY
var element = document.elementFromPoint(x, y)
if (!element) {
window.hovered_element = null
return // 當前位置沒有元素
}
window.hovered_element = element
}
window.onmousemove = track_mouse
'''
driver.execute_script(js)
while True:
element = driver.execute_script('return window.hovered_element')
if element:
print(f'當前鼠標所在的標籤爲:{element.tag_name}, 其中的文本內容爲:{element.text}')
time.sleep(1)
運行效果如下圖所示:
獲取到了當前鼠標所在的元素的標籤和標籤裏面的文字。
到這裏,這個同學需要的功能已經完全實現了。
但可能有聰明的同學會發現,他這個需求是有問題的。我們能看到至少有三個問題:
-
因爲
window.onmousemove
太靈敏了,它的採樣時間是毫秒級別的,鼠標稍稍移動一點點就會生成一個事件。但是,一個元素的區域是很大的,在一個元素內部移動鼠標,其實根本沒有必要更新window.hovered_element
。 -
在 Selenium 裏面,是通過
while True
每 1 秒查詢一次window.hovered_element
,雖然我們已經降低了頻率,但大家從上面的圖中可以看到,還是會獲取到很多重複的數據。這是由於有一些元素非常大,我們鼠標如果在上面慢慢移動,時間會超過 1 秒,那麼 Selenium 就會重複獲取到數據。 -
由於
window.onmousemove
的採樣時間間隔很小,所以我們可以近似把鼠標的移動看做是連續的移動。因此,這段代碼會記錄鼠標軌跡路徑上面的每一個元素。但實際上,我們並不會對網頁上所有的內容都感興趣,我們只會對特定的內容感興趣。因此,獲取當前鼠標所在位置
的元素,其實是一個僞需求,它根本沒有什麼實際上用處,因爲噪聲太大了,無用的數據太多了!
實際上,我覺得真正的需求應該是這樣的:如果鼠標在網頁上面某個元素停留時間超過 5 秒,那麼獲取這個元素。
但這樣做太費時間了。每次都要等 5 秒,豈不是帶薪摸魚?那需求能不能改成獲取當前鼠標點擊的元素呢?如果你實踐一下,你會發現,當你點擊一個鏈接的時候,網頁自動就跳轉到另一個頁面去了,並不能獲取到你需要的數據。
那麼這個時候怎麼辦呢?請大家期待我的下一篇文章,不僅能解決這個問題,而且會給大家帶來 Gne 項目的新產品,GneList,爲你自動獲取網站列表頁的各個條目。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/CfCv-ulCI6uuw1UK-fNetg