一、Selenium+Python環境搭建及配置
1.1 selenium 介紹
selenium 是一個 web 的自動化測試工具,不少學習功能自動化的同學開始首選 selenium ,因為它相比 QTP 有諸多有點:
免費,也不用再為破解 QTP 而大傷腦筋小巧,對於不同的語言它只是一個包而已,而 QTP 需要下載安裝1個多 G 的程序。這也是最重要的一點,不管你以前更熟悉 C、 java、ruby、python、或都是 C# ,你都可以通過 selenium 完成自動化測試,而 QTP 只支持 VBS支持多平台:windows、linux、MAC ,支持多瀏覽器:ie、ff、safari、opera、chrome支持分布式測試用例的執行,可以把測試用例分布到不同的測試機器的執行,相當於分發機的功能。1.2 selenium+Python環境配置
前提條件:已安裝好Python開發環境(推薦安裝Python3.5及以上版本)
安裝步驟:
安裝seleniumWin:pip install seleniumMac:pip3 install selenium安裝webdriver注:webdriver需要和對應的瀏覽器版本以及selenium版本對應webdriver安裝路徑Win:復制webdriver到Python安裝目錄下Mac:復制webdriver到/usr/local/bin目錄下二、元素定位及瀏覽器基本操作
2.1 啟動瀏覽器
2.1.1 普通方式啟動
啟動Chrome瀏覽器:
from selenium import webdriverbrowser = webdriver.Chrome()browser.get('URL')
啟動Firefox瀏覽器:
from selenium import webdriverbrowser = webdriver.Firefox()browser.get('URL')
啟動IE瀏覽器:
from selenium import webdriverbrowser = webdriver.Ie()browser.get('URL')
2.1.2 Headless方式啟動
Headless Chrome 是 Chrome 瀏覽器的無界面形態,可以在不打開瀏覽器的前提下,使用所有 Chrome 支持的特性運行你的程序。相比於現代瀏覽器,Headless Chrome 更加方便測試 web 應用,獲得網站的截圖,做爬蟲抓取信息等。相比於較早的 PhantomJS,SlimerJS 等,Headless Chrome 則更加貼近瀏覽器環境。
Headless Chrome 對Chrome版本要求:官方文檔中介紹,mac和linux環境要求chrome版本是59+,而windows版本的chrome要求是60+,同時chromedriver要求2.30+版本。
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.action_chains import ActionChainsfrom selenium.webdriver.common.keys import Keyschrome_options = webdriver.ChromeOptions()# 使用headless無界面瀏覽器模式chrome_options.add_argument('--headless') //增加無界面選項chrome_options.add_argument('--disable-gpu') //如果不加這個選項,有時定位會出現問題# 啟動瀏覽器,獲取網頁源代碼browser = webdriver.Chrome(chrome_options=chrome_options)mainUrl = "URL"browser.get(mainUrl)print(f"browser text = {browser.page_source}")browser.quit()
2.1.3 加載配置啟動瀏覽器
Selenium操作瀏覽器是不加載任何配置的,下面是關於加載Chrome配置的方法:
用Chrome地址欄輸入chrome://version/,查看自己的“個人資料路徑”,然后在瀏覽器啟動時,調用這個配置文件,代碼如下:
#coding=utf-8from selenium import webdriveroption = webdriver.ChromeOptions()option.add_argument('--user-data-dir=C:\Users\Administrator\AppData\Local\Google\Chrome\User Data') #設置成用戶自己的數據目錄driver=webdriver.Chrome(chrome_options=option)
而加載Firefox配置的方法有些不同:
打開Firefox點右上角設置>?(幫助)>故障排除信息>顯示文件夾,打開后把路徑復制下來就可以了
# coding=utf-8from selenium import webdriver# 配置文件地址profile_directory = r'C:\Users\\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\1x41j9of.default'# 加載配置配置profile = webdriver.FirefoxProfile(profile_directory)# 啟動瀏覽器配置driver = webdriver.Firefox(profile)
2.2 元素定位
對象的定位應該是自動化測試的核心,要想操作一個對象,首先應該識別這個對象。一個對象就是一個人一樣,他會有各種的特征(屬性),如比我們可以通過一個人的身份證號,姓名,或者他住在哪個街道、樓層、門牌找到這個人。那么一個對象也有類似的屬性,我們可以通過這個屬性找到這對象。
webdriver 提供了一系列的對象定位方法,常用的有以下幾種:
id定位:find_element_by_id()name定位:find_element_by_name()class定位:find_element_by_class_name()link定位:find_element_by_link_text()partial link定位:find_element_by_partial_link_text()tag定位:find_element_by_tag_name()xpath定位:find_element_by_xpath()css定位:find_element_by_css_selector()#coding=utf-8from selenium import webdriverbrowser=webdriver.Firefox()browser.get("URL")#########百度輸入框的定位方式###########通過id方式定位browser.find_element_by_id("kw").send_keys("selenium")#通過name方式定位browser.find_element_by_name("wd").send_keys("selenium")#通過tag name方式定位browser.find_element_by_tag_name("input").send_keys("selenium")#通過class name方式定位browser.find_element_by_class_name("s_ipt").send_keys("selenium")#通過CSS方式定位browser.find_element_by_css_selector("#kw").send_keys("selenium")#通過xpath方式定位browser.find_element_by_xpath("//input[@id='kw']").send_keys("selenium")############################################browser.find_element_by_id("su").click()time.sleep(3)browser.quit()
2.2.1 class含有空格時解決方法:
在實際進行元素定位時,經常發現class name是有多個class組合的復合類,中間以空格隔開。如果直接進行定位會出現報錯,可以通過以下方式處理:
class屬性唯一但是有空格,選擇空格兩邊唯一的那一個若空格隔開的class不唯一可以通過索引進行定位self.driver.find_elements_by_class_name('table-dragColumn')[0].click()通過css方法進行定位(空格以‘.’代替)#前面加(.)空格地方用點(.)來代替self.driver.find_element_by_css_selector('.dtb-style-1.table-dragColumns').click()#包含整個類self.driver.find_element_by_css_selector('class="dtb-style-1 table-dragColumns').click()
參考代碼:
# coding:utf-8from selenium import webdriverdriver = webdriver.Firefox()driver.get("URL")driver.implicitly_wait(20)driver.switch_to.frame("x-URS-iframe")# 方法一:取單個class屬性driver.find_element_by_class_name("dlemail").send_keys("yoyo")driver.find_element_by_class_name("dlpwd").send_keys("12333")# 方法二:定位一組取下標定位(乃下策)driver.find_elements_by_class_name("j-inputtext")[0].send_keys("yoyo")driver.find_elements_by_class_name("j-inputtext")[1].send_keys("12333")# 方法三:css定位driver.find_element_by_css_selector(".j-inputtext.dlemail").send_keys("yoyo")driver.find_element_by_css_selector(".j-inputtext.dlpwd").send_keys("123")# 方法四:取單個class屬性也是可以的driver.find_element_by_css_selector(".dlemail").send_keys("yoyo")driver.find_element_by_css_selector(".dlpwd").send_keys("123")# 方法五:直接包含空格的CSS屬性定位大法driver.find_element_by_css_selector("[class='j-inputtext dlemail']").send_keys("yoyo")
2.3 selenium三種等待方式
有時候為了保證腳本運行的穩定性,需要腳本中添加等待時間。
2.3.1 強制等待
第一種也是最簡單粗暴的一種辦法就是強制等待sleep(xx),需要引入“time”模塊,這種叫強制等待,不管你瀏覽器是否加載完了,程序都得等待3秒,3秒一到,繼續執行下面的代碼,作為調試很有用,有時候也可以在代碼里這樣等待,不過不建議總用這種等待方式,太死板,嚴重影響程序執行速度。
# -*- coding: utf-8 -*-from selenium import webdriverimport timedriver = webdriver.Firefox()driver.get('URL')time.sleep(3) # 強制等待3秒再執行下一步print(driver.current_url)driver.quit()
2.3.2 隱性等待
第二種辦法叫隱性等待,通過添加 implicitly_wait() 方法就可以方便的實現智能等待;implicitly_wait(30) 的用法應該比 time.sleep() 更智能,后者只能選擇一個固定的時間的等待,前者可以 在一個時間范圍內智能的等待。
# -*- coding: utf-8 -*-from selenium import webdriverdriver = webdriver.Firefox()driver.implicitly_wait(30) # 隱性等待,最長等30秒driver.get('URL')print(driver.current_url)driver.quit()
隱形等待是設置了一個最長等待時間,如果在規定時間內網頁加載完成,則執行下一步,否則一直等到時間截止,然后執行下一步。注意這里有一個弊端,那就是程序會一直等待整個頁面加載完成,也就是一般情況下你看到瀏覽器標簽欄那個小圈不再轉,才會執行下一步,但有時候頁面想要的元素早就在加載完成了,但是因為個別js之類的東西特別慢,我仍得等到頁面全部完成才能執行下一步,我想等我要的元素出來之后就下一步怎么辦?有辦法,這就要看selenium提供的另一種等待方式——顯性等待wait了。需要特別說明的是:隱性等待對整個driver的周期都起作用,所以只要設置一次即可,我曾看到有人把隱性等待當成了sleep在用,走哪兒都來一下…
2.3.3 顯性等待
第三種辦法就是顯性等待,WebDriverWait,配合該類的until()和until_not()方法,就能夠根據判斷條件而進行靈活地等待了。它主要的意思就是:程序每隔xx秒看一眼,如果條件成立了,則執行下一步,否則繼續等待,直到超過設置的最長時間,然后拋出TimeoutException。
wait模塊的WebDriverWait類是顯性等待類,先看下它有哪些參數與方法:
selenium.webdriver.support.wait.WebDriverWait(類)
init
driver: 傳入WebDriver實例,即我們上例中的drivertimeout: 超時時間,等待的最長時間(同時要考慮隱性等待時間)poll_frequency: 調用until或until_not中的方法的間隔時間,默認是0.5秒ignored_exceptions: 忽略的異常,如果在調用until或until_not的過程中拋出這個元組中的異常,則不中斷代碼,繼續等待,如果拋出的是這個元組外的異常,則中斷代碼,拋出異常。默認只有NoSuchElementException。
until
method: 在等待期間,每隔一段時間(__init__中的poll_frequency)調用這個傳入的方法,直到返回值不是Falsemessage: 如果超時,拋出TimeoutException,將message傳入異常
until_not
與until相反,until是當某元素出現或什么條件成立則繼續執行,until_not是當某元素消失或什么條件不成立則繼續執行,參數也相同,不再贅述。
看了以上內容基本上很清楚了,調用方法如下:
WebDriverWait(driver, 超時時長, 調用頻率, 忽略異常).until(可執行方法, 超時時返回的信息)
這里需要特別注意的是until或until_not中的可執行方法method參數,很多人傳入了WebElement對象,如下:
WebDriverWait(driver, 10).until(driver.find_element_by_id('kw')) # 錯誤
這是錯誤的用法,這里的參數一定要是可以調用的,即這個對象一定有 call() 方法,否則會拋出異常:
TypeError: 'xxx' object is not callable
在這里,你可以用selenium提供的 expected_conditions 模塊中的各種條件,也可以用WebElement的 is_displayed() 、is_enabled()、**is_selected() **方法,或者用自己封裝的方法都可以。
#coding=utf-8from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitbase_url = "URL"driver = webdriver.Firefox()driver.implicitly_wait(5)'''隱式等待和顯示等待都存在時,超時時間取二者中較大的'''locator = (By.ID,'kw')driver.get(base_url)WebDriverWait(driver,10).until(EC.title_is(u"百度一下,你就知道"))'''判斷title,返回布爾值'''WebDriverWait(driver,10).until(EC.title_contains(u"百度一下"))'''判斷title,返回布爾值'''WebDriverWait(driver,10).until(EC.presence_of_element_located((By.ID,'kw')))'''判斷某個元素是否被加到了dom樹里,並不代表該元素一定可見,如果定位到就返回WebElement'''WebDriverWait(driver,10).until(EC.visibility_of_element_located((By.ID,'su')))'''判斷某個元素是否被添加到了dom里並且可見,可見代表元素可顯示且寬和高都大於0'''WebDriverWait(driver,10).until(EC.visibility_of(driver.find_element(by=By.ID,value='kw')))'''判斷元素是否可見,如果可見就返回這個元素'''WebDriverWait(driver,10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR,'.mnav')))'''判斷是否至少有1個元素存在於dom樹中,如果定位到就返回列表'''WebDriverWait(driver,10).until(EC.visibility_of_any_elements_located((By.CSS_SELECTOR,'.mnav')))'''判斷是否至少有一個元素在頁面中可見,如果定位到就返回列表'''WebDriverWait(driver,10).until(EC.text_to_be_present_in_element((By.XPATH,"//*[@id='u1']/a[8]"),u'設置'))'''判斷指定的元素中是否包含了預期的字符串,返回布爾值'''WebDriverWait(driver,10).until(EC.text_to_be_present_in_element_value((By.CSS_SELECTOR,'#su'),u'百度一下'))'''判斷指定元素的屬性值中是否包含了預期的字符串,返回布爾值'''#WebDriverWait(driver,10).until(EC.frame_to_be_available_and_switch_to_it(locator))'''判斷該frame是否可以switch進去,如果可以的話,返回True並且switch進去,否則返回False'''#注意這里並沒有一個frame可以切換進去WebDriverWait(driver,10).until(EC.invisibility_of_element_located((By.CSS_SELECTOR,'#swfEveryCookieWrap')))'''判斷某個元素在是否存在於dom或不可見,如果可見返回False,不可見返回這個元素'''#注意#swfEveryCookieWrap在此頁面中是一個隱藏的元素WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//*[@id='u1']/a[8]"))).click()'''判斷某個元素中是否可見並且是enable的,代表可點擊'''driver.find_element_by_xpath("//*[@id='wrapper']/div[6]/a[1]").click()#WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.XPATH,"//*[@id='wrapper']/div[6]/a[1]"))).click()#WebDriverWait(driver,10).until(EC.staleness_of(driver.find_element(By.ID,'su')))'''等待某個元素從dom樹中移除'''#這里沒有找到合適的例子WebDriverWait(driver,10).until(EC.element_to_be_selected(driver.find_element(By.XPATH,"//*[@id='nr']/option[1]")))'''判斷某個元素是否被選中了,一般用在下拉列表'''WebDriverWait(driver,10).until(EC.element_selection_state_to_be(driver.find_element(By.XPATH,"//*[@id='nr']/option[1]"),True))'''判斷某個元素的選中狀態是否符合預期'''WebDriverWait(driver,10).until(EC.element_located_selection_state_to_be((By.XPATH,"//*[@id='nr']/option[1]"),True))'''判斷某個元素的選中狀態是否符合預期'''driver.find_element_by_xpath(".//*[@id='gxszButton']/a[1]").click()instance = WebDriverWait(driver,10).until(EC.alert_is_present())'''判斷頁面上是否存在alert,如果有就切換到alert並返回alert的內容'''print instance.textinstance.accept()driver.close()
2.4 瀏覽器操作
2.4.1 瀏覽器最大化、最小化
將瀏覽器最大化顯示
browser.maximize_window()
將瀏覽器最小化顯示
browser.minimize_window()
2.4.2 瀏覽器設置窗口大小
設置瀏覽器寬480、高800顯示
browser.set_window_size(480, 800)
2.4.3 瀏覽器前進后退
前進
browser.forword()
后退
browser.back()
2.5 操作測試對象
一般來說,webdriver 中比較常用的操作對象的方法有下面幾個:
click——點擊對象send_keys——在對象上模擬按鍵輸入clear——清除對象的內容,如果可以的話submit——提交對象的內容,如果可以的話text——用於獲取元素的文本信息2.6 鍵盤事件
要想調用鍵盤按鍵操作需要引入 keys 包:from selenium.webdriver.common.keys import Keys通過 send_keys()調用按鍵:send_keys(Keys.TAB) # TABsend_keys(Keys.ENTER) # 回車
參考代碼:
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.keys import Keys #需要引入 keys 包import os,timedriver = webdriver.Firefox() driver.get("http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fwebcloud .kuaibo.com%2F")time.sleep(3) driver.maximize_window() # 瀏覽器全屏顯示driver.find_element_by_id("user_name").clear() driver.find_element_by_id("user_name").send_keys("fnngj")#tab 的定位相相於清除了密碼框的默認提示信息,等同上面的 clear() driver.find_element_by_id("user_name").send_keys(Keys.TAB) time.sleep(3) driver.find_element_by_id("user_pwd").send_keys("123456")#通過定位密碼框,enter(回車)來代替登陸按鈕driver.find_element_by_id("user_pwd").send_keys(Keys.ENTER)#也可定位登陸按鈕,通過 enter(回車)代替 click() driver.find_element_by_id("login").send_keys(Keys.ENTER) time.sleep(3)driver.quit()
鍵盤組合鍵的用法:
#ctrl+a 全選輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
#ctrl+x 剪切輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
2.7 鼠標事件
鼠標事件一般包括鼠標右鍵、雙擊、拖動、移動鼠標到某個元素上等等。需要引入ActionChains類。引入方法:from selenium.webdriver.common.action_chains import ActionChains
ActionChains 常用方法:perform() 執行所有ActionChains 中存儲的行為;context_click() 右擊;double_click() 雙擊;drag_and_drop() 拖動;move_to_element() 鼠標懸停。
鼠標雙擊示例:
#定位到要雙擊的元素qqq =driver.find_element_by_xpath("xxx") #對定位到的元素執行鼠標雙擊操作 ActionChains(driver).double_click(qqq).perform()
鼠標拖放示例:
#定位元素的原位置 element = driver.find_element_by_name("source") #定位元素要移動到的目標位置 target = driver.find_element_by_name("target")#執行元素的移動操作 ActionChains(driver).drag_and_drop(element, target).perform()
2.8 多層框架/層級定位
定位元素過程中經常會遇到找不到元素的問題,出現該問題一般都是以下因素導致:
元素定位方法不對頁面存在iframe或內嵌窗口頁面超時webdriver 提供了一個 switch_to_frame 方法,可以很輕松的來解決這個問題。用法:
#先找到到 ifrome1(id = f1)browser.switch_to_frame("f1")
同樣的,如果是內嵌窗口:browser.switch_to_window("f1")
2.9 Expected Conditions解析
Expected Conditions的使用場景有2種:
直接在斷言中使用與WebDriverWait配合使用,動態等待頁面上元素出現或者消失相關方法:
title_is: 判斷當前頁面的title是否精確等於預期title_contains: 判斷當前頁面的title是否包含預期字符串presence_of_element_located:判斷某個元素是否被加到了dom樹里,並不代表該元素一定可見visibility_of_element_located:判斷某個元素是否可見.可見代表元素非隱藏,並且元素的寬和高都不等於0visibility_of:跟上面的方法做一樣的事情,只是上面的方法要傳入locator,這個方法直接傳定位到的element就好了presence_of_all_elements_located:判斷是否至少有1個元素存在於dom樹中。舉個例子,如果頁面上有n個元素的class都是'column-md-3',那么只要有1個元素存在,這個方法就返回Truetext_to_be_present_in_element:判斷某個元素中的text是否包含了預期的字符串text_to_be_present_in_element_value:判斷某個元素中的value屬性是否包含了預期的字符串frame_to_be_available_and_switch_to_it:判斷該frame是否可以switch進去,如果可以的話,返回True並且switch進去,否則返回Falseinvisibility_of_element_located:判斷某個元素中是否不存在於dom樹或不可見element_to_be_clickable:判斷某個元素中是否可見並且是enable的,這樣的話才叫clickablestaleness_of:等某個元素從dom樹中移除,注意,這個方法也是返回True或Falseelement_to_be_selected:判斷某個元素是否被選中了,一般用在下拉列表element_selection_state_to_be:判斷某個元素的選中狀態是否符合預期element_located_selection_state_to_be:跟上面的方法作用一樣,只是上面的方法傳入定位到的element,而這個方法傳入locatoralert_is_present:判斷頁面上是否存在alert,這是個老問題,很多同學會問到示例:判斷title:title_is()、title_contains()
首先導入expected_conditions模塊由於這個模塊名稱比較長,所以為了后續的調用方便,重新命名為EC了(有點像數據庫里面多表查詢時候重命名)打開博客首頁后判斷title,返回結果是True或False# coding:utf-8from selenium import webdriverfrom selenium.webdriver.support import expected_conditions as ECdriver = webdriver.Firefox()driver.get("URL")# 判斷title完全等於title = EC.title_is(u'冰封')print title(driver)# 判斷title包含title1 = EC.title_contains(u'冰封')print title1(driver)# 另外一種寫法r1 = EC.title_is(u'冰封')(driver)r2 = EC.title_contains(u'冰封')(driver)print r1print r2
三、Selenium速查表
3.1 Python Webdriver Exception速查表
webdriver在使用過程中可能會出現各種異常,我們需要了解該異常並知道如何進行異常處理。
異常 描述
WebDriverException 所有webdriver異常的基類,當有異常且不屬於下列異常時拋出 InvalidSwitchToTargetException 下面兩個異常的父類,當要switch的目標不存在時拋出 NoSuchFrameException 當你想要用switch_to.frame()切入某個不存在的frame時拋出 NoSuchWindowException 當你想要用switch_to.window()切入某個不存在的window時拋出 NoSuchElementException 元素不存在,一般由find_element與find_elements拋出 NoSuchAttributeException 一般你獲取不存在的元素屬性時拋出,要注意有些屬性在不同瀏覽器里是有不同的屬性名的 StaleElementReferenceException 指定的元素過時了,不在現在的DOM樹里了,可能是被刪除了或者是頁面或iframe刷新了 UnexpectedAlertPresentException 出現了意料之外的alert,阻礙了指令的執行時拋出 NoAlertPresentException 你想要獲取alert,但實際沒有alert出現時拋出 InvalidElementStateException 下面兩個異常的父類,當元素狀態不能進行想要的操作時拋出 ElementNotVisibleException 元素存在,但是不可見,不可以與之交互 ElementNotSelectableException 當你想要選擇一個不可被選擇的元素時拋出 InvalidSelectorException 一般當你xpath語法錯誤的時候拋出這個錯 InvalidCookieDomainException 當你想要在非當前url的域里添加cookie時拋出 UnableToSetCookieException 當driver無法添加一個cookie時拋出 TimeoutException 當一個指令在足夠的時間內沒有完成時拋出 MoveTargetOutOfBoundsException actions的move操作時拋出,將目標移動出了window之外 UnexpectedTagNameException 獲取到的元素標簽不符合要求時拋出,比如實例化Select,你傳入了非select標簽的元素時 ImeNotAvailableException 輸入法不支持的時候拋出,這里兩個異常不常見,ime引擎據說是僅用於linux下對中文/日文支持的時候 ImeActivationFailedException 激活輸入法失敗時拋出 ErrorInResponseException 不常見,server端出錯時可能會拋 RemoteDriverServerException 不常見,好像是在某些情況下驅動啟動瀏覽器失敗的時候會報這個錯
3.2 Xpath&Css定位方法速查表
描述 Xpath
Css
直接子元素 //div/a div > a
子元素或后代元素 //div//a div a
以id定位 //div[@id='idValue']//a div#idValue a
以class定位 //div[@class='classValue']//a div.classValue a
同級弟弟元素 //ul/li[@class='first']/following- ul>li.first + li 屬性 //form/input[@name='username'] form input[name='username'] 多個屬性 //input[@name='continue' and input[name='continue'][type='button
第4個子元素 //ul[@id='list']/li[4] ul#list li:nth-child(4)
第1個子元素 //ul[@id='list']/li[1] ul#list li:first-child
最后1個子元素 //ul[@id='list']/li[last()] ul#list li:last-child
屬性包含某字段 //div[contains(@title,'Title')] div[title*="Title"]
屬性以某字段開頭 //input[starts-with(@name,'user')] input[name^="user"]
屬性以某字段結尾 //input[ends-with(@name,'name')] input[name$="name"]
text中包含某字段 //div[contains(text(), 'text')]
無法定位 元素有某屬性 //div[@title] div[title]
父節點 //div/.. 無法定位



參考於:https://baijiahao.baidu.com/s?id=1650828444523528232&wfr=spider&for=pc