年前走查腳本代碼時,發(fā)現大家對selenium功能都在重復造輪子,而且容易出現一些常見低級bug。于是在閑暇之余,封裝一些常用的selenium功能。
我們提供的服務有:做網站、成都做網站、微信公眾號開發(fā)、網站優(yōu)化、網站認證、五臺ssl等。為1000+企事業(yè)單位解決了網站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的五臺網站制作公司
在某些網頁中,存在多個frame嵌套。而selenium提供的find_element函數只能在當前frame中查找,不能切換到其他frame中,需要從最上級frame中逐步切換(當然也可以指定xpath的絕對路徑,但是一般沒人這么做)。在我們寫代碼過程中,需要明確知道當前frame位置和需要尋找元素的frame位置。在frame切換過程中,容易因為疏忽導致frame切換錯誤導致元素無法找到的bug。
頁面中分布的frame,可以理解為樹狀結構。因此我們可以采用遞歸的方式, 沿著某條搜索路線frame節(jié)點,依次對樹中每個節(jié)點均做一次訪問。
我們以163網址上的登錄框為例:點擊登錄按鈕,彈出登錄iframe頁面。輸入框位置在iframe中,因此我們不能使用xpath獲取元素位置,需要進入iframe中,然后獲取元素。
手動切換ifame可能會產生bug,因此需要一套自動切換和檢索frame的機制。具體代碼如下:
需要注意的是:如果頁面中多個frame中,存在相同的xpath元素。還是需要指定frame的路徑,否則會返回搜索到的第一個元素。
強制等待
直接調用系統time.sleep函數,不管頁面加載情況一定會等待指定的時間, 即使元素已被加載 。
1.如果設置的時間較長,會浪費時間
2.如果設置的時間較短,元素可能沒有加載。
頁面中某元素如果未能立即加載,隱式等待告訴WebDriver需等待一定的時間,然后去查找元素。默認不等待,隱式等待作用于整個WebDriver周期,只需設置一次即可。
1.在上文的find_element函數中,采用遞歸方式在所有frame尋找元素。若采用隱式等待,則在每個frame中都需要等待設定的時間,耗時非常長。
2.某些頁面我們想要的元素已經加載完畢,但是部分其他資源未加載。隱式等待必須等待所有元素加載完畢,增加額外等待時間。
顯示等待一般作用于某一個元素,在設定的時間范圍內,默認每間隔0.5秒查找元素。返回被加載的元素,若超過設定的時間范圍未能查找則報錯。顯示等待作為selenium常用的等待機制,我們來看下他的源碼和機制。
driver 注釋中解釋為WebDriver實例,但是代碼中并未有相關檢測,因此可以傳入任何對象
但是__repr__函數中使用到session_id屬性,如果需要顯示屬性或者轉為str對象,最好在driver對象中添加session_id屬性
在until函數中,我們可以看到driver對象傳入method函數。在計時結束前,在不斷循環(huán)執(zhí)行method函數,如果method函數有正常返回值則退出循環(huán),否則報TimeoutException錯誤。
可以采用裝飾器對隱式等待進行封裝,這樣代碼更加精簡
同樣的,采用裝飾器對其他常用的函數進行封裝,例如強制等待、點擊、輸入文本等。
裝飾器雖然很方便,但也會產生一些麻煩。例如在find_element函數遞歸調用過程中,理應只要執(zhí)行一次裝飾器函數。但因為裝飾器已經裝飾完畢,導致每次遞歸都會執(zhí)行。例如強制等待的sleep函數,如果遞歸次數越多等待時間越長。
解除裝飾器一般有兩種做法:一是約定參數,當遞歸第二次調用時則不生效。例如
這種方式實現簡單,容易理解。但是增加了參數限制,在fun函數中就不能使用first_sleep參數。
二是采用裝飾器采用wrapped實現,通過訪問wrapped屬性獲得原始函數。例如
但是某一個函數被多個裝飾器裝飾時,需要遞歸解除裝飾器。例如
最后整體代碼如下
這次的封裝其實還存在很多問題
1.find_element函數不僅僅只是提供查找元素功能,還提供一些其他功能,因此叫element_operation更為合適。
2.find_element函數的參數過多,并且很多參數的使用并不在函數本身中,對代碼閱讀很不友好。
3.得小心避免參數重復問題,假設裝飾器sleep和裝飾器wait_time都使用time這個參數,將無法區(qū)分具體是哪個函數使用。
4.不利于擴展和維護,當功能過多時find_element的參數過于龐大。
如果只是簡單地封裝和使用,上面這種方式也能達到較好的效果。如果想進一步封裝,建議采用鏈式調用方式,裝飾器輔助封裝。例如
這樣函數的擴展性和可閱讀性有較大的提升
在C語言中,字符串處理是每天都要面對的問題。我們都知道C語言中其實并沒有一種原生的字符串類型,‘字符串’在C語言里只是一種特殊的以''結尾的字符數組。因此,如何將C語言與更高層次的Python語言在‘字符串’處理這個問題上對接是一個有難度的問題。所幸有swig這種強大的工具。
如何封裝一個函數,它修改參數字符串的內容
假如有這樣一個C語言的函數,
!-- lang: cpp --
void FillZero(char* pc,size_t * piLen)
{
size_t i=0;
while(i++*piLen/2 )
*pc++ = '0';
*pc = 0;
*piLen = i+1;
}
這個函數的功能是把字符串變成n個0。不過我們更關注函數的形式。這樣的函數,表面上看char* pc是函數的參數,可是實際上它才是函數的返回值和執(zhí)行的結果。piLen這個參數既是pc的最大長度,也是新的字符串的長度。我們直接用python封裝,看看運行結果。
Type "help", "copyright", "credits" or "license" for more information.
import cchar
s='123456'
cchar.FillZero(s,6)
Traceback (most recent call last):
File "stdin", line 1, in module
TypeError: in method 'FillZero', argument 2 of type 'size_t *'
結果差強人意,不是我們想要得到的結果。函數的第二個參數為size_t* 我們很難用python來表示,而且python中也不存在既是輸入,也是輸出的參數。
swig有一個標準庫,其中有一個cstring.i文件就是用來解決C語言字符串類型的問題。
我們在.i文件中加入這樣幾行
!-- lang: cpp --
%include "cstring.i"
%cstring_output_withsize(char* pc,size_t* pi)
void FillZero(char* pc, size_t* pi);
然后運行看結果
Type "help", "copyright", "credits" or "license" for more information.
import cchar
cchar.FillZero(10)
'00000\x00'
s=cchar.FillZero(10)
print s
00000
我們看函數的變化。首先在python里, FillZero變成了只有一個參數的函數。然后函數的返回值變成了一個字符串。其實cstring_output_size其實是一個宏,通過這個宏的定義改變了函數的形式,直接在Python中得到我們想要的結果。
其實類似cstring_output_size的宏還有好幾個,我列舉一下:
cstring_output_allocate(char *s,free($1));
第一個參數是指向字符串地址的指針,第二個參數為釋放空間的方法。
大家考慮這一下這樣的函數:
void foo(char* s)
{
s = (char*)malloc(10);
memcpy(s,"123456789",9);
}
s這個參數表面上看是輸入,實際上是函數真正的輸出。 函數中真正改變的東西是chars指向的字符串的值。而且char這個類型,
python或者其他腳本語言里應該都沒有對應的類型。那么我們用cstring_output_allocate將這個函數轉換成另外一個形式的python或者其他腳本語言的函數。轉換后的函數其實是這樣的,以python為例str
foo()。
!-- lang: cpp --
%module a
%include "cstring.i"
%{
void foo(char* s);
%}
%cstring_output_allocate(char *s, free(*$1));
void foo(char *s);
在python中的調用:
!-- lang: python --
import a
a.foo()
'123456789'
cstring_output_maxsize(char *path, int maxpath);
第一個參數也是可以改變的字符串首地址,第二個參數為字符串的最大長度。在Python中調用的時候,只有maxpath這個參數,返回字符串。
cstring_output_allocate(char *s, free($1));
第一個參數為指向字符串首地址的指針,第二個參數為釋放指針的方法。這個宏主要是封裝一種直接在函數內部malloc空間的函數。在Python中調用時沒有參數,直接返回字符串。
cstring_output_allocate_size(char *s, int slen, free(*$1));
這個相當于前面兩個函數的組合。在函數內部malloc空間,然后將字符串長度通過slen返回。其實在調用的時候非常簡單,沒有參數,直接返回字符串。
如何處理c++的std::string
std::string是C++標準類庫STL中常見的類。在平時工作中大家肯定是沒少用。在python中如何封裝std::string? swig提供了標準庫
例如函數:
!-- lang: cpp --
string Repeat(const string s)
{
return s+s;
}
只要在swig中加入這樣幾行:
!-- lang: cpp --
%include "std_string.i"
using namespace std;
string Repeat(const string s);
運行結果:
Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
import cchar
cchar.Repeat('123')
'123123'
使用起來很方便,但需要注意的是,假如函數的參數的內容是可以被修改,就不能用這種方式封裝。
例如:
!-- lang: cpp --
void repeat(string s)
{
s+=s;
}
這樣的函數直接使用 'std_string.i' 就是無效的。遇到這種函數,只能用C語言封裝成 void repeat(chars, int maxsize), 再用swig調用 'cstring_output_withsize' 這個宏再封裝一次了。
題主你好,
方法及相應代碼見截圖:
*.方法不只一種, 題主看看如果不合適請追問. 上面這種做法的好處是封裝的這個函數func可以帶任意多個位置參數.? //就圖主的問題來看, *args就夠了, 如果func函數中還有關鍵字參數,則還需要使用**argv.
-----
希望可以幫到題主, 歡迎追問
可以定義一個類,類里定義很多函數(主要用它做什么)或直接定義函數在一個py文件中
在另一個文件中導入這個那個py包,調用類和方法
就是封裝了
Python:常用函數封裝:
def is_chinese(uchar):
"""判斷一個unicode是否是漢字"""
if uchar = u'\u4e00' and uchar=u'\u9fa5':
return True
else:
return False
def is_number(uchar):
"""判斷一個unicode是否是數字"""
if uchar = u'\u0030' and uchar=u'\u0039':
return True
else:
return False
def is_alphabet(uchar):
"""判斷一個unicode是否是英文字母"""
if (uchar = u'\u0041' and uchar=u'\u005a') or (uchar = u'\u0061' and uchar=u'\u007a'):
return True
else:
return False
def is_other(uchar):
"""判斷是否非漢字,數字和英文字符"""
if not (is_chinese(uchar) or is_number(uchar) or is_alphabet(uchar)):
return True
else:
return False
def B2Q(uchar):
"""半角轉全角"""
inside_code=ord(uchar)
if inside_code0x0020 or inside_code0x7e: #不是半角字符就返回原來的字符
return uchar
if inside_code==0x0020: #除了空格其他的全角半角的公式為:半角=全角-0xfee0
inside_code=0x3000
else:
inside_code+=0xfee0
return unichr(inside_code)
def Q2B(uchar):
"""全角轉半角"""
inside_code=ord(uchar)
if inside_code==0x3000:
inside_code=0x0020
else:
inside_code-=0xfee0
if inside_code0x0020 or inside_code0x7e: #轉完之后不是半角字符返回原來的字符
return uchar
return unichr(inside_code)
def stringQ2B(ustring):
"""把字符串全角轉半角"""
return "".join([Q2B(uchar) for uchar in ustring])
def uniform(ustring):
"""格式化字符串,完成全角轉半角,大寫轉小寫的工作"""
return stringQ2B(ustring).lower()
def string2List(ustring):
"""將ustring按照中文,字母,數字分開"""
retList=[]
utmp=[]
for uchar in ustring:
if is_other(uchar):
if len(utmp)==0:
continue
else:
retList.append("".join(utmp))
utmp=[]
else:
utmp.append(uchar)
if len(utmp)!=0:
retList.append("".join(utmp))
return retList