年前走查腳本代碼時(shí),發(fā)現(xiàn)大家對(duì)selenium功能都在重復(fù)造輪子,而且容易出現(xiàn)一些常見(jiàn)低級(jí)bug。于是在閑暇之余,封裝一些常用的selenium功能。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供鄄城網(wǎng)站建設(shè)、鄄城做網(wǎng)站、鄄城網(wǎng)站設(shè)計(jì)、鄄城網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、鄄城企業(yè)網(wǎng)站模板建站服務(wù),十年鄄城做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
在某些網(wǎng)頁(yè)中,存在多個(gè)frame嵌套。而selenium提供的find_element函數(shù)只能在當(dāng)前frame中查找,不能切換到其他frame中,需要從最上級(jí)frame中逐步切換(當(dāng)然也可以指定xpath的絕對(duì)路徑,但是一般沒(méi)人這么做)。在我們寫代碼過(guò)程中,需要明確知道當(dāng)前frame位置和需要尋找元素的frame位置。在frame切換過(guò)程中,容易因?yàn)槭韬鰧?dǎo)致frame切換錯(cuò)誤導(dǎo)致元素?zé)o法找到的bug。
頁(yè)面中分布的frame,可以理解為樹(shù)狀結(jié)構(gòu)。因此我們可以采用遞歸的方式, 沿著某條搜索路線frame節(jié)點(diǎn),依次對(duì)樹(shù)中每個(gè)節(jié)點(diǎn)均做一次訪問(wèn)。
我們以163網(wǎng)址上的登錄框?yàn)槔狐c(diǎn)擊登錄按鈕,彈出登錄iframe頁(yè)面。輸入框位置在iframe中,因此我們不能使用xpath獲取元素位置,需要進(jìn)入iframe中,然后獲取元素。
手動(dòng)切換ifame可能會(huì)產(chǎn)生bug,因此需要一套自動(dòng)切換和檢索frame的機(jī)制。具體代碼如下:
需要注意的是:如果頁(yè)面中多個(gè)frame中,存在相同的xpath元素。還是需要指定frame的路徑,否則會(huì)返回搜索到的第一個(gè)元素。
強(qiáng)制等待
直接調(diào)用系統(tǒng)time.sleep函數(shù),不管頁(yè)面加載情況一定會(huì)等待指定的時(shí)間, 即使元素已被加載 。
1.如果設(shè)置的時(shí)間較長(zhǎng),會(huì)浪費(fèi)時(shí)間
2.如果設(shè)置的時(shí)間較短,元素可能沒(méi)有加載。
頁(yè)面中某元素如果未能立即加載,隱式等待告訴WebDriver需等待一定的時(shí)間,然后去查找元素。默認(rèn)不等待,隱式等待作用于整個(gè)WebDriver周期,只需設(shè)置一次即可。
1.在上文的find_element函數(shù)中,采用遞歸方式在所有frame尋找元素。若采用隱式等待,則在每個(gè)frame中都需要等待設(shè)定的時(shí)間,耗時(shí)非常長(zhǎng)。
2.某些頁(yè)面我們想要的元素已經(jīng)加載完畢,但是部分其他資源未加載。隱式等待必須等待所有元素加載完畢,增加額外等待時(shí)間。
顯示等待一般作用于某一個(gè)元素,在設(shè)定的時(shí)間范圍內(nèi),默認(rèn)每間隔0.5秒查找元素。返回被加載的元素,若超過(guò)設(shè)定的時(shí)間范圍未能查找則報(bào)錯(cuò)。顯示等待作為selenium常用的等待機(jī)制,我們來(lái)看下他的源碼和機(jī)制。
driver 注釋中解釋為WebDriver實(shí)例,但是代碼中并未有相關(guān)檢測(cè),因此可以傳入任何對(duì)象
但是__repr__函數(shù)中使用到session_id屬性,如果需要顯示屬性或者轉(zhuǎn)為str對(duì)象,最好在driver對(duì)象中添加session_id屬性
在until函數(shù)中,我們可以看到driver對(duì)象傳入method函數(shù)。在計(jì)時(shí)結(jié)束前,在不斷循環(huán)執(zhí)行method函數(shù),如果method函數(shù)有正常返回值則退出循環(huán),否則報(bào)TimeoutException錯(cuò)誤。
可以采用裝飾器對(duì)隱式等待進(jìn)行封裝,這樣代碼更加精簡(jiǎn)
同樣的,采用裝飾器對(duì)其他常用的函數(shù)進(jìn)行封裝,例如強(qiáng)制等待、點(diǎn)擊、輸入文本等。
裝飾器雖然很方便,但也會(huì)產(chǎn)生一些麻煩。例如在find_element函數(shù)遞歸調(diào)用過(guò)程中,理應(yīng)只要執(zhí)行一次裝飾器函數(shù)。但因?yàn)檠b飾器已經(jīng)裝飾完畢,導(dǎo)致每次遞歸都會(huì)執(zhí)行。例如強(qiáng)制等待的sleep函數(shù),如果遞歸次數(shù)越多等待時(shí)間越長(zhǎng)。
解除裝飾器一般有兩種做法:一是約定參數(shù),當(dāng)遞歸第二次調(diào)用時(shí)則不生效。例如
這種方式實(shí)現(xiàn)簡(jiǎn)單,容易理解。但是增加了參數(shù)限制,在fun函數(shù)中就不能使用first_sleep參數(shù)。
二是采用裝飾器采用wrapped實(shí)現(xiàn),通過(guò)訪問(wèn)wrapped屬性獲得原始函數(shù)。例如
但是某一個(gè)函數(shù)被多個(gè)裝飾器裝飾時(shí),需要遞歸解除裝飾器。例如
最后整體代碼如下
這次的封裝其實(shí)還存在很多問(wèn)題
1.find_element函數(shù)不僅僅只是提供查找元素功能,還提供一些其他功能,因此叫element_operation更為合適。
2.find_element函數(shù)的參數(shù)過(guò)多,并且很多參數(shù)的使用并不在函數(shù)本身中,對(duì)代碼閱讀很不友好。
3.得小心避免參數(shù)重復(fù)問(wèn)題,假設(shè)裝飾器sleep和裝飾器wait_time都使用time這個(gè)參數(shù),將無(wú)法區(qū)分具體是哪個(gè)函數(shù)使用。
4.不利于擴(kuò)展和維護(hù),當(dāng)功能過(guò)多時(shí)find_element的參數(shù)過(guò)于龐大。
如果只是簡(jiǎn)單地封裝和使用,上面這種方式也能達(dá)到較好的效果。如果想進(jìn)一步封裝,建議采用鏈?zhǔn)秸{(diào)用方式,裝飾器輔助封裝。例如
這樣函數(shù)的擴(kuò)展性和可閱讀性有較大的提升
樓主你好!
看你的代碼存在很多問(wèn)題,一個(gè)個(gè)來(lái)說(shuō)明
1)首先你代碼的報(bào)錯(cuò)源于你想用list來(lái)展開(kāi)你的SLinkedList類,在python中,除非內(nèi)置的可迭代對(duì)象外,其他都需要實(shí)現(xiàn)__iter__()函數(shù),才能用list來(lái)進(jìn)行展開(kāi)。注意:判斷一個(gè)對(duì)象是否可迭代,請(qǐng)使用isinstance(obj, Iterable)來(lái)判斷obj是不是可以迭代,Iterable需要從collections中導(dǎo)入
2)插入的方法存在嚴(yán)重問(wèn)題,按樓主的方法插入的話,因?yàn)轭^節(jié)點(diǎn)始終在變,所以當(dāng)你需要遍歷鏈表的時(shí)候就會(huì)找不到頭節(jié)點(diǎn);
3)pop的方法實(shí)現(xiàn)也有問(wèn)題,因?yàn)槭菃蜗蜴?,所以無(wú)法從末節(jié)點(diǎn)開(kāi)始刪除,只能刪除頭節(jié)點(diǎn)
4)top方法的意圖未知
其他:
下面列舉了一下我修改后的方案,做了一些錦上添花的操作,每個(gè)基本操作都會(huì)返回鏈表對(duì)象,這樣就可以使用鏈?zhǔn)讲僮鱽?lái)寫代碼;迭代函數(shù)使用yield來(lái)實(shí)現(xiàn),避免展開(kāi)時(shí)占用不必要的內(nèi)存。
另:我的展開(kāi)時(shí)直接取鏈表中各個(gè)節(jié)點(diǎn)的元素,加了一些關(guān)鍵注釋在代碼中;
#?-*-?coding:?utf-8?-*-
class?Node:
def?__init__(self):
'''
elm:節(jié)點(diǎn)元素
nxt:下個(gè)節(jié)點(diǎn)指針
'''
self.elm,?self.nxt?=?None,?None
class?SLinkedList:
def?__init__(self):
'''
head:?鏈表頭
end_point:?鏈表尾
'''
self.head??????=?None
self.end_point?=?None
def?push(self,?x):
p?=?Node()
p.elm?=?x
if?self.head?is?None:
self.head??????=?p
self.end_point?=?p
return?self
self.end_point.nxt?=?p
self.end_point?????=?p
return?self
def?pop(self):
'''因?yàn)閷?shí)現(xiàn)的是一個(gè)單鏈表,所以只能從頭開(kāi)始刪除節(jié)點(diǎn)'''
if?self.head.nxt?is?None:
return
self.head?=?self.head.nxt
return?self
def?__iter__(self):
temp_node?=?self.head
while?temp_node?is?not?None:
yield?temp_node.elm
temp_node?=?temp_node.nxt
if?__name__?==?'__main__':
'''增加1,2,5三個(gè)元素,并刪除一個(gè)頭節(jié)點(diǎn)'''
mylinklist?=?SLinkedList().push(1).push(2).push(5).pop()
print(list(mylinklist))
其實(shí)python這個(gè)語(yǔ)言使用鏈表有些畫蛇添足,但是如果拿來(lái)當(dāng)作需求練手也無(wú)妨。
望采納,謝謝!
順序存儲(chǔ)結(jié)構(gòu)最大的缺點(diǎn)是插入和刪除時(shí)需要移動(dòng)大量元素,耗費(fèi)大量時(shí)間。
如果讓相鄰元素間留有足夠余地,也就是不考慮相鄰位置了,那么,我們這里可以引入鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。
鏈表結(jié)構(gòu)可以充分利用計(jì)算機(jī)內(nèi)存空間,實(shí)現(xiàn)靈活的內(nèi)存動(dòng)態(tài)管理。
二、鏈表的定義
鏈表(Linked list)是一種常見(jiàn)的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),是一種線性表,但是不像順序表一樣連續(xù)存儲(chǔ)數(shù)據(jù),而是在每一個(gè)節(jié)點(diǎn)(數(shù)據(jù)存儲(chǔ)單元)里存放下一個(gè)節(jié)點(diǎn)的位置信息(即地址)。
1、單向鏈表
單向鏈表也叫單鏈表,是鏈表中最簡(jiǎn)單的一種形式,一個(gè)信息域(元素域)和一個(gè)鏈接域組成一個(gè)節(jié)點(diǎn)。
這個(gè)鏈接指向鏈表中的下一個(gè)節(jié)點(diǎn),而最后一個(gè)節(jié)點(diǎn)的鏈接域則指向一個(gè)空值。
鏈表的每個(gè)結(jié)點(diǎn)中只包含一個(gè)鏈接域,所以叫做單鏈表。
表元素域elem用來(lái)存放具體的數(shù)據(jù)。
鏈接域next用來(lái)存放下一個(gè)節(jié)點(diǎn)的位置(python中的標(biāo)識(shí))
變量p指向鏈表的頭節(jié)點(diǎn)(首節(jié)點(diǎn))的位置,從p出發(fā)能找到表中的任意節(jié)點(diǎn)。
鏈表中第一個(gè)結(jié)點(diǎn)的長(zhǎng)處位置叫做頭指針
顯著性鏈表的最后一個(gè)結(jié)點(diǎn)指針為“空”(通常用NULL或“^”符號(hào)表示)
通常會(huì)在單鏈表的第一個(gè)結(jié)點(diǎn)前附設(shè)一個(gè)結(jié)點(diǎn),稱為頭結(jié)點(diǎn)。它的信息域可以不存儲(chǔ)數(shù)據(jù),也可以存儲(chǔ)線性表長(zhǎng)度等附加信息,頭結(jié)點(diǎn)的鏈接域指向第一個(gè)結(jié)點(diǎn)的指針。
頭指針與頭結(jié)點(diǎn)的異同
無(wú)論鏈表是否為空,頭指針均不為空,頭指針是鏈表的必要元素;頭結(jié)點(diǎn)不一定是鏈表的必要要素。
頭指針具有標(biāo)識(shí)作用,所以常用頭指針冠以鏈表的名字。
python join函數(shù)用法如下:
join函數(shù)python就是把一個(gè)list中所有的串按照你定義的分隔符連接起來(lái)。join是string類型的一個(gè)函數(shù),用調(diào)用他的字符串去連接參數(shù)里的列表,python里面萬(wàn)物皆對(duì)象,調(diào)用join函數(shù),將后面的列表里的值用逗號(hào)連接成新的字符串。str(i)foriinlist這是一個(gè)映射,就是把list中每個(gè)值都轉(zhuǎn)換成字符串。
函數(shù)含義
python中得thread的一些機(jī)制和C/C++不同:在C/C++中,主線程結(jié)束后,其子線程會(huì)默認(rèn)被主線程kill掉。而在python中,主線程結(jié)束后,會(huì)默認(rèn)等待子線程結(jié)束后,主線程才退出。
python對(duì)于thread的管理中有兩個(gè)函數(shù):join和setDaemon。
join:如在一個(gè)線程B中調(diào)用threada。join(),則threada結(jié)束后,線程B才會(huì)接著threada。join()往后運(yùn)行。
setDaemon:主線程A啟動(dòng)了子線程B,調(diào)用b。setDaemaon(True),則主線程結(jié)束時(shí),會(huì)把子線程B也殺死,與C/C++中得默認(rèn)效果是一樣的。
網(wǎng)站標(biāo)題:包含python鏈?zhǔn)胶瘮?shù)的詞條
當(dāng)前URL:http://aaarwkj.com/article36/docpssg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、網(wǎng)站內(nèi)鏈、企業(yè)建站、網(wǎng)站建設(shè)、Google、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)