使用Airtest超快速開發(fā)App爬蟲

安裝和使用

由于本文的目的是介紹如何使用Airtest來(lái)開發(fā)App爬蟲,那么Airtest作為測(cè)試開發(fā)工具的方法介紹將會(huì)一帶而過(guò),僅僅說(shuō)明如何安裝并進(jìn)行基本的操作。

安裝Airtest

從Airtest官網(wǎng):https://airtest.netease.com下載Airtest,然后像安裝普通軟件一樣安裝即可。安裝過(guò)程沒有什么需要特別說(shuō)明的地方。Airtest已經(jīng)幫你打包好了開發(fā)需要的全部環(huán)境,所以安裝完成Airtest以后就能夠直接使用了。
Airtest運(yùn)行以后的界面如下圖所示。

image

連接手機(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

image

此時(shí)在Airtest界面右上角應(yīng)該能夠看到手機(jī)的信息,如下圖所示。

image

點(diǎn)擊connect按鈕,此時(shí)可以在界面上看到手機(jī)的界面,并且當(dāng)你手動(dòng)操作手機(jī)屏幕時(shí),Airtest中的手機(jī)畫面實(shí)時(shí)更新。如下圖所示。

image.png

對(duì)于某些手機(jī),例如小米,在第一次使用Airtest時(shí),請(qǐng)注意手機(jī)上將會(huì)彈出提示,詢問你是否允許安裝App,此時(shí)需要點(diǎn)擊允許按鈕。

打開微信

先通過(guò)一個(gè)簡(jiǎn)單的例子,來(lái)看看如何快速上手Airtest,稍后再來(lái)詳解。

例如我現(xiàn)在想使用電腦控制手機(jī),打開微信。

此時(shí),點(diǎn)擊下圖中方框框住的touch按鈕:

image

此時(shí),把鼠標(biāo)移動(dòng)到Airtest右邊的手機(jī)屏幕區(qū)域,鼠標(biāo)會(huì)變成十字型。在微信圖標(biāo)的左上角按下鼠標(biāo)左鍵不放,并拖到微信右下角松開鼠標(biāo)。此時(shí)請(qǐng)注意中間代碼區(qū)域發(fā)生了什么變化,如下圖所示。

image

好了。以上就是你需要使用電腦打開微信所要進(jìn)行的全部操作。

點(diǎn)擊上方工具欄中的三角形圖標(biāo),運(yùn)行代碼,如下圖所示。

image

代碼運(yùn)行完成以后,微信被打開了。

界面介紹

在有了一個(gè)直觀的使用以后,我們?cè)賮?lái)介紹一下Airtest的界面,將會(huì)更加有針對(duì)性。

Airtest的界面如下圖所示。

image

這里,我把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ū)是一些常用工具,從左到右,依次為:

  1. 新建項(xiàng)目
  2. 打開項(xiàng)目
  3. 保存項(xiàng)目
  4. 運(yùn)行代碼
  5. 停止代碼
  6. 查看運(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ì)到每一步操作了什么元素。

image

通過(guò)截圖功能操作手機(jī)雖然方便,但是截圖涉及到分辨率的問題,代碼不能在不同的手機(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……如下圖所示。

image

以手機(jī)版知乎為例,由于它是Android原生的App,所以在F區(qū)下拉菜單選擇Android,此時(shí)注意B區(qū)彈出提示,詢問你是否要插入poco初始代碼到當(dāng)前輸入光標(biāo)的位置,點(diǎn)擊Yes,如下圖所示。

image

此時(shí),B區(qū)自動(dòng)插入了一段代碼,如下圖所示。

image

定位并點(diǎn)擊

現(xiàn)在,點(diǎn)擊E區(qū)的鎖形圖標(biāo),如下圖所示。

image

鎖形圖標(biāo)激活以后,你再操作D區(qū)的屏幕,點(diǎn)擊知乎App下面的知乎兩個(gè)字,會(huì)發(fā)現(xiàn)屏幕上被點(diǎn)擊的App并不會(huì)打開。但E區(qū)和C區(qū)卻發(fā)生了變化,如下圖所示。

image

其中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被打開了。如下圖所示。

image

注意,如果你發(fā)現(xiàn)手機(jī)真機(jī)顯示的界面與Airtest屏幕顯示的手機(jī)界面不一致,可能是因?yàn)锳irtest的屏幕被你鎖定了。在F區(qū)點(diǎn)一下鎖形圖標(biāo),取消鎖定,Airtest中的手機(jī)屏幕就會(huì)更新了。

定位并輸入

打開知乎以后,我想使用知乎的搜索功能,那么繼續(xù),把鎖形圖標(biāo)激活,然后點(diǎn)擊知乎頂部的搜索框,如下圖所示:

image

繼續(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è)面,如下圖所示。

image

因此你需要先點(diǎn)擊一下這個(gè)輸入框,跳轉(zhuǎn)到真正的搜索界面:

poco(name="com.zhihu.android:id/input").click()

在真正的搜索界面如下圖所示。

image

可以看到,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é)果:

image
image
image

通過(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é)果以后,需要查看下面的各種問題,此時(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])

方向示意圖如下圖所示:

image

在一般情況下:

  • 向上滑動(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中,如下圖所示。

image

運(yùn)行這段代碼,如果是Linux或者macOS的用戶,請(qǐng)注意看運(yùn)行結(jié)果是不是有報(bào)錯(cuò),提示adb沒有運(yùn)行權(quán)限。這是因?yàn)殡SPoco安裝的adb沒有運(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ī)被成功控制了,如下圖所示。

image

如何獲取屏幕文字

由于Airtest的編輯器中的代碼運(yùn)行后無(wú)法正常打印出中文,因此后面的代碼都直接在PyCharm中執(zhí)行。

既然要做爬蟲,就需要獲取手機(jī)上的文字內(nèi)容?;氐剿阉黜?yè)面,我想知道“古劍奇譚”三這個(gè)關(guān)鍵字能搜索出多少條結(jié)果,每條結(jié)果有多少個(gè)討論,如下圖所示:

image

此時(shí)我們需要做兩件事情:

  1. 分別查看每一個(gè)搜索結(jié)果
  2. 獲取屏幕上的文字

E區(qū)的樹狀結(jié)構(gòu)如下圖所示:

image

每一個(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)行效果如下圖所示:

image

但是這種做法實(shí)際上是很危險(xiǎn)的,假設(shè)會(huì)有某一個(gè)很生僻的搜索結(jié)果,只有標(biāo)題沒有討論數(shù),那么這樣分開抓取再組合的做法,就會(huì)導(dǎo)致最后匹配錯(cuò)位。所以合理的做法是先抓大再抓小。每一組標(biāo)題和討論數(shù),他們都有自己的父節(jié)點(diǎn),如下圖箭頭所指向的三個(gè)android.widget.LinearLayout:

image

那么現(xiàn)在,使用先抓大再抓小的技巧,先把每一組結(jié)果的父節(jié)點(diǎn)抓下來(lái),再到每一個(gè)結(jié)果里面分別獲取標(biāo)題和討論數(shù)。

然而這個(gè)父節(jié)點(diǎn)又怎么獲取呢?如下圖所示,這個(gè)父節(jié)點(diǎn)每一個(gè)屬性值都沒有什么特殊的,寫任何一個(gè)都有可能與別的節(jié)點(diǎn)撞上。

image

此時(shí),最簡(jiǎn)單的辦法,就是在E區(qū),雙擊父節(jié)點(diǎn)。定位代碼就會(huì)自動(dòng)添加,如下圖所示。

image

這個(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]?,F(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)行效果如下圖所示。

image

控制多臺(tái)手機(jī)

當(dāng)我們?cè)陔娔X上插入多個(gè)Android手機(jī)時(shí),執(zhí)行命令:

adb devices -l

運(yùn)行效果如下圖所示。

image

每個(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ī)是完全沒有問題的。

無(wú)線模式

Airtest支持無(wú)線模式,不需要USB,只要電腦和手機(jī)連接同一個(gè)WIFI就能控制:

image

如果大家對(duì)如何開啟無(wú)線模式有興趣,請(qǐng)留言,我就會(huì)繼續(xù)寫。

搭建手機(jī)爬蟲集群

一臺(tái)電腦可以連接三十臺(tái)手機(jī),那么如果有很多電腦和很多手機(jī),就可以實(shí)現(xiàn)手機(jī)爬蟲集群,其運(yùn)行效果如下圖所示。

image

本文轉(zhuǎn)載:https://www.cnblogs.com/xieqiankun/p/use_airtest.html#4321441

喜歡爬蟲的關(guān)注個(gè)人公眾號(hào):python擼碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,684評(píng)論 1 32
  • airtest 官網(wǎng) 文檔和IDE下載,請(qǐng)點(diǎn)擊下方鏈接。 http://airtest.netease.com a...
    陳宇同學(xué)名字好長(zhǎng)閱讀 7,060評(píng)論 0 1
  • ??JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的。 ??事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,715評(píng)論 1 11
  • 因?yàn)橐Y(jié)局swift3.0中引用snapKit的問題,看到一篇介紹Xcode8,swift3變化的文章,覺得很詳細(xì)...
    uniapp閱讀 4,877評(píng)論 0 12
  • 老姨搬家了。 原來(lái)坐擁某一線城市三套房產(chǎn)的她,正在把其他兩套房子的所有東西塞進(jìn)那個(gè)三十平的老房子里,再加上兩只貓,...
    王小茶閱讀 713評(píng)論 0 0

友情鏈接更多精彩內(nèi)容