安裝和使用
由于本文的目的是介紹如何使用Airtest來(lái)開發(fā)App爬蟲,那么Airtest作為測(cè)試開發(fā)工具的方法介紹將會(huì)一帶而過(guò)链沼,僅僅說(shuō)明如何安裝并進(jìn)行基本的操作。
安裝Airtest
從Airtest官網(wǎng):https://airtest.netease.com下載Airtest逗物,然后像安裝普通軟件一樣安裝即可方库。安裝過(guò)程沒(méi)有什么需要特別說(shuō)明的地方闪盔。Airtest已經(jīng)幫你打包好了開發(fā)需要的全部環(huán)境弯院,所以安裝完成Airtest以后就能夠直接使用了辱士。
Airtest運(yùn)行以后的界面如下圖所示泪掀。
連接手機(jī)
以Android手機(jī)為例,由于Airtest會(huì)通過(guò)adb命令安裝兩個(gè)輔助App到手機(jī)上颂碘,再用adb命令通過(guò)控制這兩個(gè)輔助App進(jìn)而控制手機(jī)异赫,因此首先需要確保手機(jī)的adb調(diào)試
功能是打開的,并允許通過(guò)adb命令安裝App到手機(jī)上头岔。
啟動(dòng)Airtest以后塔拳,把Android手機(jī)連接到電腦上,點(diǎn)擊下圖方框中的refresh ADB
:
此時(shí)在Airtest界面右上角應(yīng)該能夠看到手機(jī)的信息峡竣,如下圖所示靠抑。
點(diǎn)擊connect
按鈕,此時(shí)可以在界面上看到手機(jī)的界面适掰,并且當(dāng)你手動(dòng)操作手機(jī)屏幕時(shí)颂碧,Airtest中的手機(jī)畫面實(shí)時(shí)更新荠列。如下圖所示。
對(duì)于某些手機(jī)载城,例如小米肌似,在第一次使用Airtest時(shí),請(qǐng)注意手機(jī)上將會(huì)彈出提示诉瓦,詢問(wèn)你是否允許安裝App川队,此時(shí)需要點(diǎn)擊允許按鈕。
打開微信
先通過(guò)一個(gè)簡(jiǎn)單的例子睬澡,來(lái)看看如何快速上手Airtest固额,稍后再來(lái)詳解。
例如我現(xiàn)在想使用電腦控制手機(jī)煞聪,打開微信对雪。
此時(shí),點(diǎn)擊下圖中方框框住的touch
按鈕:
此時(shí)米绕,把鼠標(biāo)移動(dòng)到Airtest右邊的手機(jī)屏幕區(qū)域瑟捣,鼠標(biāo)會(huì)變成十字型。在微信圖標(biāo)的左上角按下鼠標(biāo)左鍵不放栅干,并拖到微信右下角松開鼠標(biāo)迈套。此時(shí)請(qǐng)注意中間代碼區(qū)域發(fā)生了什么變化,如下圖所示碱鳞。
好了桑李。以上就是你需要使用電腦打開微信所要進(jìn)行的全部操作。
點(diǎn)擊上方工具欄中的三角形圖標(biāo)窿给,運(yùn)行代碼贵白,如下圖所示。
代碼運(yùn)行完成以后崩泡,微信被打開了禁荒。
界面介紹
在有了一個(gè)直觀的使用以后,我們?cè)賮?lái)介紹一下Airtest的界面角撞,將會(huì)更加有針對(duì)性呛伴。
Airtest的界面如下圖所示。
這里谒所,我把Airtest分成了A-F6個(gè)區(qū)域热康,他們的功能如下:
- A區(qū):常用操作功能區(qū)
- B區(qū):Python代碼編寫區(qū)
- C區(qū):運(yùn)行日志區(qū)
- D區(qū):手機(jī)屏幕區(qū)
- E區(qū):App頁(yè)面布局信息查看區(qū)
- F區(qū):工具欄
A區(qū)是常用的基于圖像識(shí)別
的屏幕操作功能,例如:
-
touch
: 點(diǎn)擊屏幕元素 -
swipe
: 滑動(dòng)屏幕 -
exists
: 判斷屏幕元素是否存在 -
text
: 在輸入框中輸入文字 -
snashot
: 截圖 - ……
一般來(lái)說(shuō)劣领,是點(diǎn)擊A區(qū)里面的某一個(gè)功能姐军,然后在D區(qū)屏幕上進(jìn)行框選操作,B區(qū)就會(huì)自動(dòng)生成相應(yīng)的操作代碼。
B區(qū)用來(lái)顯示和編寫Python代碼奕锌。在多數(shù)情況下衫贬,不需要手動(dòng)寫代碼,因?yàn)榇a會(huì)根據(jù)你在手機(jī)屏幕上面的操作自動(dòng)生成歇攻。只有一些需要特別定制化的動(dòng)作才需要修改代碼乌助。
D區(qū)顯示了手機(jī)屏幕赡磅,當(dāng)你操作手機(jī)真機(jī)時(shí)欧瘪,這個(gè)屏幕會(huì)實(shí)時(shí)刷新题篷。你也可以直接在D區(qū)屏幕上使用鼠標(biāo)操作手機(jī),你的操作動(dòng)作會(huì)被自動(dòng)在真機(jī)上執(zhí)行屡穗。
F區(qū)是一些常用工具贴捡,從左到右,依次為:
- 新建項(xiàng)目
- 打開項(xiàng)目
- 保存項(xiàng)目
- 運(yùn)行代碼
- 停止代碼
- 查看運(yùn)行報(bào)告
其中1-5很好理解村砂,那么什么是查看運(yùn)行報(bào)告呢烂斋?
當(dāng)你至少運(yùn)行了一次以后,點(diǎn)擊這個(gè)功能础废,會(huì)自動(dòng)給你打開一個(gè)網(wǎng)頁(yè)汛骂。網(wǎng)頁(yè)如下圖所示,這是你的代碼的運(yùn)行報(bào)告评腺,詳細(xì)到每一步操作了什么元素帘瞭。
通過(guò)截圖功能操作手機(jī)雖然方便,但是截圖涉及到分辨率的問(wèn)題蒿讥,代碼不能在不同的手機(jī)上通用蝶念。所以對(duì)于A區(qū)的功能,做點(diǎn)簡(jiǎn)單操作即可芋绸,不用深入了解媒殉。
更高級(jí)的功能,需要通過(guò)E區(qū)實(shí)現(xiàn)摔敛。
基于App布局信息操作手機(jī)
初始化代碼
App的布局信息就像網(wǎng)頁(yè)的HTML一樣廷蓉,保存了App上面各個(gè)元素的相對(duì)位置和各個(gè)參數(shù)。對(duì)于一個(gè)App而言舷夺,在不同分辨率的手機(jī)上苦酱,可能相同的元素有著不同的坐標(biāo)點(diǎn)售貌,但是這個(gè)元素的屬性參數(shù)一般是不會(huì)變的给猾。因此,如果使用元素的屬性參數(shù)來(lái)尋找并控制這個(gè)元素颂跨,就能實(shí)現(xiàn)在不同分辨率手機(jī)上的精確定位敢伸。
App的布局信息的格式與App的開發(fā)環(huán)境有關(guān)。點(diǎn)擊F區(qū)的下拉菜單恒削,可以看到這里能夠指定不同的App開發(fā)環(huán)境池颈。其中的Unity
尾序、Cocos-*
等等一般是做游戲用的,Android
是安卓原生App躯砰,iOS
是蘋果的App……如下圖所示每币。
以手機(jī)版知乎為例,由于它是Android原生的App琢歇,所以在F區(qū)下拉菜單選擇Android
兰怠,此時(shí)注意B區(qū)彈出提示,詢問(wèn)你是否要插入poco初始代碼到當(dāng)前輸入光標(biāo)的位置李茫,點(diǎn)擊Yes
揭保,如下圖所示。
此時(shí)魄宏,B區(qū)自動(dòng)插入了一段代碼秸侣,如下圖所示。
定位并點(diǎn)擊
現(xiàn)在宠互,點(diǎn)擊E區(qū)的鎖形圖標(biāo)味榛,如下圖所示。
鎖形圖標(biāo)激活以后予跌,你再操作D區(qū)的屏幕励负,點(diǎn)擊知乎
App下面的知乎
兩個(gè)字,會(huì)發(fā)現(xiàn)屏幕上被點(diǎn)擊的App并不會(huì)打開匕得。但E區(qū)和C區(qū)卻發(fā)生了變化继榆,如下圖所示。
其中E區(qū)顯示的樹狀結(jié)構(gòu)就是當(dāng)前屏幕的布局信息汁掠,這與Chrome開發(fā)者工具里面顯示的HTML結(jié)構(gòu)如出一轍略吨。C區(qū)顯示的是當(dāng)前被我點(diǎn)中的元素的信息。
請(qǐng)注意在這些元素信息中考阱,有一個(gè)text
屬性翠忠,它的值為知乎
。那么乞榨,這個(gè)屬性就可以作為一個(gè)定位元素秽之,于是可以在B區(qū)編寫代碼:
poco(text="知乎").click()
寫完代碼以后運(yùn)行程序,可以看到知乎App被打開了吃既。如下圖所示考榨。
注意,如果你發(fā)現(xiàn)手機(jī)真機(jī)顯示的界面與Airtest屏幕顯示的手機(jī)界面不一致鹦倚,可能是因?yàn)锳irtest的屏幕被你鎖定了河质。在F區(qū)點(diǎn)一下鎖形圖標(biāo),取消鎖定,Airtest中的手機(jī)屏幕就會(huì)更新了掀鹅。
定位并輸入
打開知乎以后散休,我想使用知乎的搜索功能,那么繼續(xù)乐尊,把鎖形圖標(biāo)激活戚丸,然后點(diǎn)擊知乎頂部的搜索框,如下圖所示:
繼續(xù)看C區(qū)顯示的搜索框?qū)傩匀忧叮梢钥吹竭@里有一個(gè)name
屬性昏滴,它的值是com.zhihu.android:id/input
,還有一個(gè)text
屬性对人,它的值為蔡徐坤任 NBA 新春賀歲大使
谣殊。能不能像前面打開知乎一樣,使用text
這個(gè)屬性呢牺弄?也行姻几,也不行。說(shuō)它行势告,是因?yàn)槟氵@么做確實(shí)現(xiàn)在能工作蛇捌;說(shuō)它不行,因?yàn)檫@是知乎的熱門搜索關(guān)鍵詞咱台,隨時(shí)會(huì)改變络拌。你今天使用這一句話成功了,明天熱門關(guān)鍵詞變化了回溺,那么你的代碼就無(wú)法使用了春贸。所以此時(shí)需要使用name
這個(gè)屬性。
常見的基本上不會(huì)變化的屬性包含但不限于:name
type
resourceId
package
遗遵。
另外還有一點(diǎn)萍恕,知乎首頁(yè)的這個(gè)搜索框,實(shí)際上是不能輸入內(nèi)容的车要,當(dāng)你點(diǎn)擊以后允粤,會(huì)跳轉(zhuǎn)到另一個(gè)頁(yè)面,如下圖所示翼岁。
因此你需要先點(diǎn)擊一下這個(gè)輸入框类垫,跳轉(zhuǎn)到真正的搜索界面:
poco(name="com.zhihu.android:id/input").click()
在真正的搜索界面如下圖所示。
可以看到琅坡,name
屬性的值依然是com.zhihu.android:id/input
悉患,此時(shí)就可以輸入內(nèi)容了。
輸入內(nèi)容使用的方法為set_text
脑蠕,用法為:
poco(name="com.zhihu.android:id/input").set_text('古劍奇譚三')
定位并篩選
輸入了搜索關(guān)鍵詞以后购撼,再來(lái)看看當(dāng)前頁(yè)面跪削,搜索出現(xiàn)了三個(gè)結(jié)果:
通過(guò)對(duì)比這三個(gè)結(jié)果的屬性信息谴仙,發(fā)現(xiàn)他們的name
屬性都是相同的迂求,而text
不同。如果像下面這樣寫點(diǎn)擊動(dòng)作:
poco(name='com.zhihu.android:id/magi_title').click()
那么默認(rèn)就會(huì)點(diǎn)擊第一個(gè)搜索結(jié)果晃跺。
如果我想點(diǎn)擊第二個(gè)搜索結(jié)果怎么辦呢揩局?可以這樣寫代碼:
poco(name='com.zhihu.android:id/magi_title', text='古劍奇譚(電視劇)').click()
或者你也可以像列表一樣使用索引定位:
poco(name='com.zhihu.android:id/magi_title')[1].click()
這兩種寫法的前提掀虎,都是我們已經(jīng)知道了每個(gè)結(jié)果分別是什么凌盯。假設(shè)現(xiàn)在我就想搜索古劍奇譚三
,但我不知道搜索結(jié)果是第幾項(xiàng)烹玉,又應(yīng)該怎么辦呢驰怎?此時(shí)還可以使用正則表達(dá)式:
poco(name='com.zhihu.android:id/magi_title', textMatches='^古劍奇譚三.*$').click()
滑動(dòng)屏幕
進(jìn)入搜索結(jié)果以后,需要查看下面的各種問(wèn)題二打,此時(shí)就需要不斷向上滑動(dòng)屏幕县忌。這里有一點(diǎn)需要特別注意,Airtest只能獲取當(dāng)前屏幕上的元素布局信息继效,不在屏幕上的內(nèi)容是無(wú)法獲取的症杏。這一點(diǎn)和Selenium是不一樣的。
滑動(dòng)屏幕使用的命令為swipe
瑞信,滑動(dòng)屏幕需要使用坐標(biāo)信息厉颤。但這種坐標(biāo)和屏幕分辨率無(wú)關(guān)。這里的坐標(biāo)
定義為:(x, y)凡简,其中x為橫坐標(biāo)逼友,y為縱坐標(biāo)。屏幕左上角為(0, 0)秤涩,屏幕右下角為(1, 1)翁逞,從左向右,橫坐標(biāo)從0逐漸增大到1溉仑,從上到下挖函,縱坐標(biāo)從0逐漸增大到1。
現(xiàn)在我要把屏幕向上滑動(dòng)浊竟,那么在真機(jī)上面怨喘,我是先按住屏幕下方,然后把屏幕向上滑動(dòng)振定,所以代碼可以這樣寫:
# poco.swipe(起點(diǎn)坐標(biāo)必怜,終點(diǎn)左邊)
poco.swipe([0.5, 0.8], [0.5, 0.2])
方向示意圖如下圖所示:
在一般情況下:
- 向上滑動(dòng),只需要改動(dòng)縱坐標(biāo)后频,且起點(diǎn)值大于終點(diǎn)值
- 向下滑動(dòng)梳庆,只需要改動(dòng)縱坐標(biāo)暖途,且起點(diǎn)值小于終點(diǎn)值
- 向左滑動(dòng),只需要改動(dòng)橫坐標(biāo)膏执,且起點(diǎn)值大于終點(diǎn)值
- 向右滑動(dòng)驻售,只需要改動(dòng)橫坐標(biāo),且起點(diǎn)值小于終點(diǎn)值
在爬蟲開發(fā)中更米,涉及到的Airtest操作基本上已經(jīng)介紹完畢欺栗。
單獨(dú)使用Python控制手機(jī)
在Airtest操作手機(jī)雖然方便,但是不可能在每一臺(tái)電腦上都安裝Airtest吧征峦。所以需要想辦法把代碼從Airtest這個(gè)程序中分離出來(lái)迟几。
Airtest基于Python的一個(gè)開源庫(kù)Poco開發(fā),而在Airtest的B區(qū)寫的Python代碼栏笆,實(shí)際上就是Poco的代碼类腮。所以只要安裝Poco庫(kù),就可以在Python中直接控制手機(jī)蛉加。
安裝Poco庫(kù)的命令為:
pip install pocoui
這個(gè)庫(kù)依賴的東西有點(diǎn)多蚜枢,安裝稍稍慢一些。安裝完成以后七婴,我們把代碼復(fù)制到PyCharm中祟偷,如下圖所示。
運(yùn)行這段代碼打厘,如果是Linux或者macOS的用戶修肠,請(qǐng)注意看運(yùn)行結(jié)果是不是有報(bào)錯(cuò),提示adb沒(méi)有運(yùn)行權(quán)限户盯。這是因?yàn)殡SPoco安裝的adb沒(méi)有運(yùn)行權(quán)限嵌施,需要給它添加權(quán)限,在終端執(zhí)行命令:
# chmod +x 報(bào)錯(cuò)信息中給出的adb地址
chmod +x /Users/kingname/.local/share/virtualenvs/ZhihuSpider/lib/python3.7/site-packages/airtest/core/android/static/adb/mac/adb(實(shí)際執(zhí)行時(shí)請(qǐng)換成你的地址)
命令運(yùn)行完成以后再次執(zhí)行代碼莽鸭,可以看到代碼運(yùn)行成功吗伤,手機(jī)被成功控制了,如下圖所示硫眨。
如何獲取屏幕文字
由于Airtest的編輯器中的代碼運(yùn)行后無(wú)法正常打印出中文足淆,因此后面的代碼都直接在PyCharm中執(zhí)行。
既然要做爬蟲礁阁,就需要獲取手機(jī)上的文字內(nèi)容巧号。回到搜索頁(yè)面姥闭,我想知道“古劍奇譚”三這個(gè)關(guān)鍵字能搜索出多少條結(jié)果丹鸿,每條結(jié)果有多少個(gè)討論,如下圖所示:
此時(shí)我們需要做兩件事情:
- 分別查看每一個(gè)搜索結(jié)果
- 獲取屏幕上的文字
E區(qū)的樹狀結(jié)構(gòu)如下圖所示:
每一個(gè)搜索結(jié)果的標(biāo)題作為text屬性的值棚品,在name='com.zhihu.android:id/magi_title'
對(duì)應(yīng)的元素中靠欢;每一個(gè)搜索結(jié)果的討論數(shù)作為text屬性的值廊敌,在name='com.zhihu.android:id/magi_count'
對(duì)應(yīng)的元素中。
最直接的做法就是分別獲取三個(gè)標(biāo)題和三個(gè)討論數(shù)门怪,然后把它們合并在一起:
title_obj_list = poco(name='com.zhihu.android:id/magi_title')
title_list = [title.get_text() for title in title_obj_list]
discuss_obj_list = poco(name='com.zhihu.android:id/magi_count')
discuss_list = [discuss.get_text() for discuss in discuss_obj_list]
for title, discuss in zip(title_list, discuss_list):
print(title, discuss)
運(yùn)行效果如下圖所示:
但是這種做法實(shí)際上是很危險(xiǎn)的骡澈,假設(shè)會(huì)有某一個(gè)很生僻的搜索結(jié)果,只有標(biāo)題沒(méi)有討論數(shù)薪缆,那么這樣分開抓取再組合的做法秧廉,就會(huì)導(dǎo)致最后匹配錯(cuò)位伞广。所以合理的做法是先抓大再抓小拣帽。每一組標(biāo)題和討論數(shù),他們都有自己的父節(jié)點(diǎn)嚼锄,如下圖箭頭所指向的三個(gè)android.widget.LinearLayout
:
那么現(xiàn)在减拭,使用先抓大再抓小的技巧,先把每一組結(jié)果的父節(jié)點(diǎn)抓下來(lái)区丑,再到每一個(gè)結(jié)果里面分別獲取標(biāo)題和討論數(shù)拧粪。
然而這個(gè)父節(jié)點(diǎn)又怎么獲取呢?如下圖所示沧侥,這個(gè)父節(jié)點(diǎn)每一個(gè)屬性值都沒(méi)有什么特殊的可霎,寫任何一個(gè)都有可能與別的節(jié)點(diǎn)撞上。
此時(shí)宴杀,最簡(jiǎn)單的辦法癣朗,就是在E區(qū),雙擊父節(jié)點(diǎn)旺罢。定位代碼就會(huì)自動(dòng)添加旷余,如下圖所示。
這個(gè)定位代碼看起來(lái)非常復(fù)雜扁达,但實(shí)際上它的內(nèi)在邏輯非常簡(jiǎn)單正卧,就是從頂層一層一層往下找而已。
自動(dòng)生成的定位代碼如下:
poco("android.widget.LinearLayout").offspring("com.zhihu.android:id/action_bar_root").offspring("com.zhihu.android:id/parent_fragment_content_id").offspring("android.support.v7.widget.RecyclerView").child("android.widget.LinearLayout")[0]
在這個(gè)自動(dòng)生成的定位代碼中跪解,我們看到了offspring
炉旷、child
這兩種方法。其中child
代表子節(jié)點(diǎn)叉讥,offspring
代表孫節(jié)點(diǎn)窘行、孫節(jié)點(diǎn)的子節(jié)點(diǎn)、孫節(jié)點(diǎn)的孫節(jié)點(diǎn)……节吮。簡(jiǎn)言之抽高,使用child
只會(huì)在子節(jié)點(diǎn)中搜索需要的內(nèi)容,而使用offspring
會(huì)像文件夾遞歸一樣把里面的所有節(jié)點(diǎn)都遍歷一次透绩,直到找到符合條件的屬性為止翘骂。顯然壁熄,offspring速度會(huì)比child慢。
實(shí)際上碳竟,我們可以對(duì)這個(gè)定位代碼做一些精簡(jiǎn):
poco("com.zhihu.android:id/parent_fragment_content_id").offspring("android.support.v7.widget.RecyclerView").child("android.widget.LinearLayout")[0]
這個(gè)精簡(jiǎn)的方法草丧,與從Chrome復(fù)制的XPath中進(jìn)行精簡(jiǎn)是一樣的邏輯,根本原則就是找到“獨(dú)一無(wú)二”的屬性值莹桅,然后用這個(gè)屬性值來(lái)進(jìn)行定位昌执。
由于我點(diǎn)擊的是第一個(gè)搜索結(jié)果,所以定位代碼的最后有一個(gè)[0]
≌┢茫現(xiàn)在由于需要獲得所有搜索結(jié)果的內(nèi)容懂拾,所以應(yīng)該去掉[0]
而使用for循環(huán)展開,然后獲取里面的內(nèi)容:
result_obj = poco("com.zhihu.android:id/parent_fragment_content_id").offspring("android.support.v7.widget.RecyclerView").child("android.widget.LinearLayout")
for result in result_obj:
title = result.child(name='com.zhihu.android:id/magi_title').get_text()
count = result.child(name='com.zhihu.android:id/magi_count').get_text()
print(title, count)
運(yùn)行效果如下圖所示铐达。
控制多臺(tái)手機(jī)
當(dāng)我們?cè)陔娔X上插入多個(gè)Android手機(jī)時(shí)岖赋,執(zhí)行命令:
adb devices -l
運(yùn)行效果如下圖所示。
每個(gè)手機(jī)都會(huì)被列出來(lái)瓮孙。在最左邊的編號(hào)就是手機(jī)串號(hào)唐断。使用這個(gè)串號(hào)可以指定多個(gè)手機(jī):
from airtest.core.api import auto_setup
from airtest.core.android import Android
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
auto_setup(__file__)
device_1 = Android('76efadf3a7ce4')
device_2 = Android('adfasdfasf23')
device_3 = Android('adifu39ernla')
poco_1 = AndroidUiautomationPoco(device_1, use_airtest_input=True, screenshot_each_action=False)
poco_2 = AndroidUiautomationPoco(device_2, use_airtest_input=True, screenshot_each_action=False)
poco_3 = AndroidUiautomationPoco(device_3, use_airtest_input=True, screenshot_each_action=False)
通過(guò)這種方式,在一臺(tái)電腦上使用USBHub杭抠,連上二三十臺(tái)手機(jī)是完全沒(méi)有問(wèn)題的脸甘。
無(wú)線模式
Airtest支持無(wú)線模式,不需要USB偏灿,只要電腦和手機(jī)連接同一個(gè)WIFI就能控制:
如果大家對(duì)如何開啟無(wú)線模式有興趣丹诀,請(qǐng)留言,我就會(huì)繼續(xù)寫菩混。
搭建手機(jī)爬蟲集群
一臺(tái)電腦可以連接三十臺(tái)手機(jī)忿墅,那么如果有很多電腦和很多手機(jī),就可以實(shí)現(xiàn)手機(jī)爬蟲集群沮峡,其運(yùn)行效果如下圖所示疚脐。
本文轉(zhuǎn)載:https://www.cnblogs.com/xieqiankun/p/use_airtest.html#4321441
喜歡爬蟲的關(guān)注個(gè)人公眾號(hào):python擼碼