第10章--Android原生App爬蟲(chóng)_第1頁(yè)
第10章--Android原生App爬蟲(chóng)_第2頁(yè)
第10章--Android原生App爬蟲(chóng)_第3頁(yè)
第10章--Android原生App爬蟲(chóng)_第4頁(yè)
第10章--Android原生App爬蟲(chóng)_第5頁(yè)
已閱讀5頁(yè),還剩104頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、Python Crawler Development 極客學(xué)院 J互聯(lián)網(wǎng)+職業(yè)技能系列Python爬蟲(chóng)開(kāi)發(fā) 從入門(mén)到實(shí)戰(zhàn)(微課版)人民郵電出版社謝乾坤著第10章 Android原生App爬蟲(chóng) 本章所講到的爬蟲(chóng)能夠無(wú)視網(wǎng)站后臺(tái)和App數(shù)據(jù)傳遞過(guò)程中的任何加密算法,能夠爬取任意Android原生App顯示在屏幕上的文本型數(shù)據(jù)。并且理論上可以突破任何反爬蟲(chóng)機(jī)制。 前面的章節(jié)所講到的爬蟲(chóng)無(wú)外乎兩種情況:第一種情況,爬蟲(chóng)偽裝成瀏覽器,向服務(wù)器要數(shù)據(jù);第二種情況,在服務(wù)器往瀏覽器發(fā)送數(shù)據(jù)時(shí),爬蟲(chóng)從中攔截,獲取信息。 這兩種情況,無(wú)論是暗號(hào)(參數(shù))不對(duì)還是行為不對(duì),都會(huì)被服務(wù)器識(shí)別。那么有沒(méi)有什么辦法可以做

2、到幾乎毫無(wú)痕跡地爬取數(shù)據(jù)呢?答案是有。當(dāng)然可能有讀者會(huì)認(rèn)為可以使用Selenium + ChromeDriver。這種方式只能操作網(wǎng)頁(yè)。本章將要介紹針對(duì)Android原生App的爬蟲(chóng)。 通過(guò)這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)。(1)Android測(cè)試環(huán)境的搭建。(2)使用Python操作Android手機(jī)。(3)使用Python操作Android手機(jī)實(shí)現(xiàn)爬蟲(chóng)。10.1 實(shí)現(xiàn)原理 目前,Android App主要有兩種實(shí)現(xiàn)形式。第一種是Android原生App。這種App的全部或者大部分內(nèi)容使用Android提供的各個(gè)接口來(lái)開(kāi)發(fā),例如Android版的微信就是一個(gè)Android原生的App。第二種

3、是基于網(wǎng)頁(yè)的App。這種App本質(zhì)上就是一個(gè)瀏覽器,里面的所有內(nèi)容實(shí)際上都是網(wǎng)頁(yè)。例如,12306的App就是這樣一種基于網(wǎng)頁(yè)的App。 Android原生App爬蟲(chóng)(以下簡(jiǎn)稱App爬蟲(chóng))可以直接讀取Android原生App上面的文本信息,如圖10-1所示。圖10-1 App爬蟲(chóng)直接讀取Android原生App上面的文本信息 例如想爬TapTap這個(gè)App上面的各個(gè)游戲,可以直接從手機(jī)屏幕上把游戲的名字讀出來(lái)。當(dāng)然,要讀游戲描述也是完全沒(méi)有問(wèn)題的,如圖10-2所示。圖10-2 讀取游戲描述 App爬蟲(chóng)可以實(shí)現(xiàn)自動(dòng)滾動(dòng)屏幕,自動(dòng)單擊進(jìn)入詳情頁(yè)。凡是人可以對(duì)手機(jī)進(jìn)行的操作,App爬蟲(chóng)都可以進(jìn)行。

4、UiAutomator是Google官方提供的Android自動(dòng)化圖形接口測(cè)試框架。通過(guò)它可以實(shí)現(xiàn)對(duì)Android設(shè)備屏幕的各種操作,或者直接從屏幕上讀取文字。 大部分系統(tǒng)版本大于4.1的Android系統(tǒng),都會(huì)內(nèi)置UiAutomator。小米手機(jī)原裝的MIUI系統(tǒng)除外,MIUI系統(tǒng)UiAutomator被移除了,需要刷開(kāi)發(fā)版或者換其他系統(tǒng)才能使用。10.1.1 環(huán)境搭建1安裝JRE 要使用UiAutomator操作Android手機(jī),首先需要在計(jì)算機(jī)上安裝Android的軟件開(kāi)發(fā)工具包(Software Development Kit, SDK)。要安裝Android SDK,首先需要安裝J

5、ava運(yùn)行時(shí)環(huán)境(Java Runtime Environment, JRE)。 對(duì)于Mac OS,使用Homebrew安裝Java開(kāi)發(fā)套件(Java SE Development Kit,JDK)。 JRE包含在了JDK里面:brew updatebrew cask install java 對(duì)于Ubuntu,使用如下命令直接安裝JRE:sudo apt-get updatesudo apt-get install default-jre 對(duì)于Windows,可以訪問(wèn)/technetwork/java/javase/downloads/jre8-downloads-2133155.html下

6、載JRE 8。在這個(gè)頁(yè)面,首先選擇“Accept License Agreement”單選按鈕,如圖10-3所示。圖10-3 首先選擇“Accept License Agreement”單選按鈕才能下載2安裝Android SDK 安裝完成JRE以后,再來(lái)安裝Android SDK。請(qǐng)打開(kāi)https:/developer. /studio/index.html,這是Android開(kāi)發(fā)者中國(guó)官網(wǎng),在中國(guó)可以直接打開(kāi)。打開(kāi)網(wǎng)頁(yè),拖到最下方,在“僅獲取命令行工具”中下載自己系統(tǒng)對(duì)應(yīng)的SDK,如圖10-4所示。 下載的是一個(gè).zip格式的壓縮包,將之解壓可以得到一個(gè)名為tools的文件夾。對(duì)于Mac

7、OS與Ubuntu系統(tǒng),本書(shū)把這個(gè)tools文件夾放在“/book/sdk”文件夾里面。對(duì)于Windows系統(tǒng),本書(shū)把這個(gè)tools文件夾放在“E:Program Filessdksdk”文件夾里面。 圖10-4 在網(wǎng)頁(yè)最下面下載系統(tǒng)對(duì)應(yīng)的Android SDK 在Mac OS與Ubuntu的終端輸入并執(zhí)行如下命令,如圖10-5所示。cd /book/sdkbin/sdkmanager platform-tools圖10-5 執(zhí)行命令安裝platform-tools Windows系統(tǒng)直接在tools文件夾中打開(kāi)CMD窗口,并輸入命令:bin/sdkmanager.exe platform-t

8、ools 輸入完成命令并按Enter鍵,可以看到在終端窗口彈出安裝協(xié)議,輸入y并按Enter鍵即可安裝“platform-tools”。 需要注意的是,這里執(zhí)行這個(gè)命令時(shí),程序會(huì)從Google獲取一些數(shù)據(jù),此時(shí)可能會(huì)由于網(wǎng)絡(luò)問(wèn)題導(dǎo)致失敗。如果遇到網(wǎng)絡(luò)問(wèn)題,那么就需要使用能訪問(wèn)Google的代理,并且命令也需要做一些修改,修改為:bin/sdkmanager.exe platform-tools -proxy=http -proxy_host=代理IP -proxy-port=代理端口 安裝完成以后,會(huì)在“/book/sdk”文件夾或者“E:Program Filessdksdk”文件夾下出現(xiàn)一

9、個(gè)“platform-tools”文件夾?,F(xiàn)在需要將tools文件夾和platform-tools文件夾添加到系統(tǒng)的環(huán)境變量中。3設(shè)置環(huán)境變量 對(duì)于使用Mac OS或Ubuntu系統(tǒng)并安裝了zsh和Oh-my-zsh的讀者,請(qǐng)打開(kāi)/.zshrc并檢查是否已經(jīng)有export PATH開(kāi)頭的一句話,如果有,請(qǐng)修改為下面所示的代碼這樣,其中的tools和platform-tools文件夾的地址請(qǐng)改為實(shí)際地址:export PATH=/Users/kingname/book/sdk/platform-tools:/Users/kingname/ book/sdk/tools:/usr/local/bi

10、n:/usr/bin:/bin:/usr/sbin:/sbin 如果是Mac OS或者Ubuntu系統(tǒng)且沒(méi)有安裝zsh和Oh-my-zsh的讀者,或者想臨時(shí)添加環(huán)境變量進(jìn)行測(cè)試,可直接在終端執(zhí)行下面代碼,將tools和platform-tools的地址改為實(shí)際地址:exportPATH=$PATH:/Users/kingname/book/sdk/platform-tools:/Users/kingname/book/sdk/tools 需要注意的是,這種方法是臨時(shí)添加環(huán)境變量,當(dāng)前的終端窗口不能關(guān)閉。一旦關(guān)閉再重新打開(kāi),就需要再一次執(zhí)行上面的代碼。 對(duì)于Windows,打開(kāi)任意一個(gè)文件夾,并

11、在左側(cè)導(dǎo)航窗口中右鍵單擊“計(jì)算機(jī)”,選擇“屬性”命令,打開(kāi)屬性面板。在屬性面板左側(cè)選擇“高級(jí)系統(tǒng)設(shè)置”選項(xiàng),進(jìn)入“高級(jí)”選項(xiàng)卡,最后單擊右下角的“環(huán)境變量”按鈕進(jìn)入“環(huán)境變量”對(duì)話框,如圖10-6所示。 圖10-6 單擊“環(huán)境變量”按鈕 4開(kāi)啟開(kāi)發(fā)者模式 要通過(guò)計(jì)算機(jī)控制手機(jī),還需要打開(kāi)Android手機(jī)的開(kāi)發(fā)者模式。本書(shū)以小米手機(jī)的MIUI開(kāi)發(fā)版和Android原生系統(tǒng)為例來(lái)演示如何開(kāi)啟開(kāi)發(fā)者模式。 對(duì)于小米手機(jī)MIUI開(kāi)發(fā)版,依次進(jìn)入“系統(tǒng)設(shè)置”-“我的設(shè)備”-“全部參數(shù)”,快速連續(xù)單擊“MIUI版本”這一欄5次,開(kāi)發(fā)者模式就會(huì)打開(kāi)。打開(kāi)以后,回到“系統(tǒng)設(shè)置”主界面,依次進(jìn)入“更多設(shè)置”-

12、“開(kāi)發(fā)者選項(xiàng)”。在“開(kāi)發(fā)者選項(xiàng)”中分別打開(kāi)“USB調(diào)試”、“USB安裝”和“USB調(diào)試(安全模式)”這幾個(gè)開(kāi)關(guān),系統(tǒng)會(huì)彈出警告,選擇允許。圖10-7 雙擊“Path”并添加tools文件夾 對(duì)于Andorid原生系統(tǒng),以Google生產(chǎn)的Nexus為例。打開(kāi)“系統(tǒng)設(shè)置”,進(jìn)入最下方的“系統(tǒng)”,打開(kāi)系統(tǒng)信息界面。拖動(dòng)最下方,找到“關(guān)于手機(jī)”并點(diǎn)擊,進(jìn)入手機(jī)狀態(tài)界面,如圖10-8所示。 快速單擊“版本號(hào)”5次,打開(kāi)開(kāi)發(fā)者選項(xiàng)。 開(kāi)發(fā)者選項(xiàng)打開(kāi)以后,就可以在手機(jī)信息界面找到進(jìn)入開(kāi)發(fā)者選項(xiàng)的入口了,如圖10-9所示。 進(jìn)入開(kāi)發(fā)者選項(xiàng)的設(shè)置頁(yè)面,打開(kāi)“USB調(diào)試”,如圖10-10所示。圖10-8 手機(jī)狀

13、態(tài)界面圖10-9 開(kāi)發(fā)者選項(xiàng)入口出現(xiàn)在系統(tǒng)設(shè)置中圖10-10 在開(kāi)發(fā)者選項(xiàng)中打開(kāi)USB調(diào)試開(kāi)關(guān) 由于各大廠商對(duì)Android系統(tǒng)幾乎都有自己的定制化,因此不能在這里一一列舉所有型號(hào)手機(jī)開(kāi)啟開(kāi)發(fā)者模式的操作。對(duì)于沒(méi)有提及的手機(jī)型號(hào),請(qǐng)通過(guò)網(wǎng)絡(luò)搜索。 設(shè)置好環(huán)境變量以后,在終端窗口輸入“uiautomatorviewer”并按Enter鍵,如果可以彈出圖10-11所示的UI Automator Viewer窗口,表明環(huán)境設(shè)置成功。 將Android手機(jī)連接到計(jì)算機(jī)上,保持手機(jī)屏幕為亮起狀態(tài),單擊UI Automator Viewer左上角文件夾右側(cè)的手機(jī)圖標(biāo),如果能夠看到手機(jī)屏幕出現(xiàn)在窗口中,則表

14、示一切順利,環(huán)境搭建成功完成。如果在這個(gè)過(guò)程中手機(jī)彈出了任何警告窗口,都選擇“運(yùn)行”或者“確定”。圖10-11 UI Automator Viewer 窗口10.1.2 使用Python 操縱手機(jī) 要使用Python來(lái)操作UI Automator從而控制手機(jī),需要安裝一個(gè)第三方庫(kù)。 這個(gè)庫(kù)的名字就是uiautomator,使用pip進(jìn)行安裝,如圖10-12所示。圖10-12 使用pip安裝uiautomator 安裝完成以后運(yùn)行Python并導(dǎo)入uiautomator: from uiautomator import Device device = Device() print(device.

15、dump() 由于是第一次運(yùn)行,uiautomator會(huì)往手機(jī)中安裝兩個(gè)沒(méi)有圖標(biāo)的程序。有一些手機(jī)系統(tǒng)可能會(huì)彈出窗口詢問(wèn)是否允許安裝,單擊“繼續(xù)安裝”按鈕,如圖10-13所示。圖10-13 系統(tǒng)詢問(wèn)是否安裝,單擊“繼續(xù)安裝”按鈕 安裝完成以后,可以看到終端窗口出現(xiàn)了類似于XML的內(nèi)容,如圖10-14所示。這說(shuō)明uiautomator這個(gè)第三方庫(kù)成功安裝。此時(shí)就可以使用Python控制手機(jī)了。圖10-14 終端窗口出現(xiàn)了類似于XML的內(nèi)容 有一點(diǎn)需要特別說(shuō)明,UI Automator Viewer與Python uiautomator不能同時(shí)使用。一旦Python的uiautomator運(yùn)行過(guò)一

16、次,它安裝的兩個(gè)文件就會(huì)在手機(jī)后臺(tái)運(yùn)行。 這兩個(gè)文件會(huì)占用Android內(nèi)部的一個(gè)叫作UI hierarchy的東西,從而導(dǎo)致SDK自帶的UI Automator Viewer一旦嘗試獲取手機(jī)屏幕就會(huì)報(bào)錯(cuò),如圖10-15所示。圖10-15 一旦運(yùn)行過(guò)uiautomator,就會(huì)導(dǎo)致SDK自帶的UI Automator Viewer不能獲取手機(jī)屏幕 與Selenium一樣,要操作手機(jī)上面的元素,首先要找到被操作的東西。以打開(kāi)微信為例,首先翻到有微信的那一頁(yè),如圖10-16所示。圖10-16 翻到有微信的一頁(yè) 編寫(xiě)如下代碼:from uiautomator import Devicedevice

17、= Device()device(text=微信).click() 運(yùn)行以后可以看到,微信自動(dòng)被點(diǎn)擊并打開(kāi)。 如果計(jì)算機(jī)上面只連接了一臺(tái)Android手機(jī),那么初始化設(shè)備連接只需要使用device = Device()即可。那么如果計(jì)算機(jī)上連接了很多臺(tái)手機(jī),該怎么辦呢?此時(shí)就需要指定手機(jī)的串號(hào)。要查看手機(jī)串號(hào),需要在終端輸入以下命令:adb devices -l 從輸出的內(nèi)容可以看到手機(jī)的串號(hào),如圖10-17所示。圖10-17 使用命令查詢手機(jī)串號(hào)10.1.3 選擇器 如何知道有哪些選擇器可供使用呢?請(qǐng)執(zhí)行以下代碼:from uiautomator import Devicedevice =

18、Device()print(device.dump() 此時(shí)終端會(huì)以XML輸出當(dāng)前手機(jī)屏幕顯示的窗口布局信息,如圖10-18所示。 這里的XML就相當(dāng)于網(wǎng)頁(yè)中的HTML,用來(lái)描述窗口上面各個(gè)部分的布局信息。XML的格式與HTML非常像,格式為:文本 可以用一個(gè)標(biāo)簽來(lái)作為選擇器,也可以同時(shí)使用多個(gè)標(biāo)簽來(lái)更精確地描述某一個(gè)元素。一般來(lái)說(shuō),操作一個(gè)有文字的元素,主要是使用text這個(gè)屬性;如果是從屏幕上讀文字,就使用其他的屬性。圖10-18 當(dāng)前手機(jī)屏幕顯示的窗口布局信息10.1.4 操作 在選擇好元素以后,就需要對(duì)它進(jìn)行操作。最常見(jiàn)的操作如下:獲得屏幕文字;滾動(dòng)屏幕;滑動(dòng)屏幕;點(diǎn)擊屏幕;輸入文字;

19、判斷元素是否存在;點(diǎn)亮關(guān)閉屏幕;操作實(shí)體按鍵;watcher。1獲得屏幕文字 如果要從Android手機(jī)上讀取當(dāng)前屏幕上顯示的文本內(nèi)容,用到的是一個(gè)元素的“.text”屬性。這里以獲取游戲商場(chǎng)類App“TapTap”當(dāng)前屏幕的游戲名稱為例。 當(dāng)前手機(jī)上的TapTap界面如圖10-19所示。 從屏幕上可以看到有3個(gè)游戲的名字,分別是“霍基2”“yellow”和“我的星球”?,F(xiàn)在的目的是要把這3個(gè)名字讀取下來(lái)。首先使用以下代碼獲取當(dāng)前屏幕的XML布局,如圖10-20所示。圖10-19 TapTap首頁(yè) 圖10-20 TapTap首頁(yè)對(duì)應(yīng)的XML布局 從這個(gè)布局可以看到,所有標(biāo)題的resource-

20、id都是“com.taptap:id/ bottom_app_name”。如果需要獲取第一個(gè)游戲“霍基2”的名字,可以使用下面這一行代碼:game_1_title = device(resourceId=com.taptap:id/bottom_app_name).text 運(yùn)行結(jié)果如圖10-21所示。圖10-21 獲取第一個(gè)游戲的名稱 但實(shí)際上,當(dāng)前屏幕有3個(gè)游戲,它們的resource-id都是相同的。如何把3個(gè)游戲的名字都獲取下來(lái)呢?方法很簡(jiǎn)單,那就是“先別急著獲取.text屬性先尋找元素,再用for循環(huán)展開(kāi),最后再獲取.text屬性”。其代碼如下。game_title_list = d

21、evice(resourceId=com.taptap:id/bottom_app_name)for title in game_title_list: print(title.text) 運(yùn)行結(jié)果如圖10-22所示。圖10-22 同時(shí)獲取當(dāng)前屏幕的3個(gè)游戲名2滾動(dòng)屏幕 滾動(dòng)屏幕對(duì)應(yīng)的操作為“.scroll()”。它的操作對(duì)象是一個(gè)可以滾動(dòng)的對(duì)象。如果手動(dòng)操作可以把屏幕向上滾動(dòng),那么屏幕上應(yīng)該至少有一個(gè)元素是可以滾動(dòng)的,因此選擇器可以寫(xiě)為:device(scrollable=True) 那么向上滾動(dòng)一屏可以寫(xiě)為:device(scrollable=True).scroll.vert.forwa

22、rd() 如果想向下滾動(dòng)一屏,則需要把forward()換為backward():device(scrollable=True).scroll.vert.forward() 由于向上、向下是“垂直方向”,所以代碼中使用了vert,這是英語(yǔ)單詞vertical(垂直的)的簡(jiǎn)寫(xiě)??赡苡行〢pp會(huì)出現(xiàn)左右滾動(dòng)的情況,這時(shí)候就需要使用horiz,這是英語(yǔ)單詞horizontal(水平的)的簡(jiǎn)寫(xiě)。于是,向左及向右滾動(dòng)就可以寫(xiě)為:device(scrollable=True).scroll.horiz.forward() #向右滾動(dòng)device(scrollable=True).scroll.horiz.

23、backward() #向左滾動(dòng) 使用滾動(dòng)屏幕的方式,就可以把所有游戲的名字都讀取下來(lái),如圖10-23所示。圖10-23 滾動(dòng)屏幕獲取所有游戲名稱 有一點(diǎn)需要注意,由于滾動(dòng)一屏?xí)r,有可能前一屏最下面的元素滾動(dòng)以后剛好到了后一屏的最上面,因此可能出現(xiàn)重復(fù)獲取同一個(gè)游戲名字兩次的情況,如圖10-23中的方框所示。在實(shí)際使用時(shí)要注意去重處理。3滑動(dòng)屏幕 在某些情況下,整個(gè)窗口布局的XML里面,所有元素的scrollable屬性值全部都是False,例如小米手機(jī)的桌面。在這種情況下,沒(méi)有辦法使用“scrollable=True”來(lái)左右滾動(dòng)桌面,于是就需要使用根據(jù)坐標(biāo)來(lái)滑動(dòng)桌面的“.swipe()”方法

24、。 在Android系統(tǒng)的屏幕上,左上角為坐標(biāo)原點(diǎn)(0, 0),越向下,y軸數(shù)字越大;越向右,x軸數(shù)字越大。 “.swipe()”這個(gè)方法操作的對(duì)象是整個(gè)手機(jī)屏幕,所以不需要為device設(shè)定選擇器?!?swipe()”的用法為:device.swipe(400, 600, 0, 600) 它接收4個(gè)參數(shù),分別為起始點(diǎn)x坐標(biāo),起始點(diǎn)y坐標(biāo),終點(diǎn)x坐標(biāo),終點(diǎn)y坐標(biāo)。對(duì)于左右滑動(dòng)來(lái)說(shuō),只需要改變x坐標(biāo)即可。如果要顯示右邊的一屏,那么起始點(diǎn)的x坐標(biāo)要大于終點(diǎn)的x坐標(biāo)。如果要顯示左邊的一屏,起始點(diǎn)的x坐標(biāo)要小于終點(diǎn)的x坐標(biāo)。 如果在實(shí)際寫(xiě)代碼的時(shí)候搞不清x坐標(biāo)哪個(gè)大,就親自把手放在屏幕上滑動(dòng)進(jìn)行查看。

25、如果要顯示右邊的一屏,那么會(huì)首先把手放在屏幕右側(cè),按住然后往左移動(dòng),所以此時(shí)起始點(diǎn)的x坐標(biāo)要大一些。4點(diǎn)擊屏幕 選擇一個(gè)元素以后,除了獲取它上面的文字外,還可以點(diǎn)擊。就如同前面通過(guò)點(diǎn)擊打開(kāi)微信一樣。點(diǎn)擊操作“.click()”和“.long_click()”分別對(duì)應(yīng)短按點(diǎn)擊和長(zhǎng)按點(diǎn)擊,它們可以直接應(yīng)用于一個(gè)被選擇出來(lái)的元素上。例如:device(text=微信).click()device(text=微信).long_click() 點(diǎn)擊操作也可以直接應(yīng)用于坐標(biāo)位置。例如:device.click(230, 567) #第1個(gè)參數(shù)為橫坐標(biāo)x軸,第2個(gè)參數(shù)為縱坐標(biāo)y軸device.long_cl

26、ick(230, 567) #第1個(gè)參數(shù)為橫坐標(biāo)x軸,第2個(gè)參數(shù)為縱坐標(biāo)y軸 在某些App中,可能某一個(gè)元素并沒(méi)有一個(gè)獨(dú)特的標(biāo)志來(lái)讓選擇器選擇。這個(gè)時(shí)候就可以直接使用坐標(biāo)來(lái)操作。 以TapTap的搜索按鈕為例,搜索按鈕的坐標(biāo)如圖10-24所示。圖10-24 TapTap搜索按鈕的坐標(biāo) 對(duì)于這個(gè)按鈕,在右側(cè)信息窗口中可以看到“ImageButton640,80 688,128”中的640,80對(duì)應(yīng)了這個(gè)搜索按鈕的左上角的坐標(biāo),688,128對(duì)應(yīng)了搜索按鈕右下角的坐標(biāo)。對(duì)于一個(gè)矩形來(lái)說(shuō),確定了一對(duì)對(duì)角的坐標(biāo),也就確定了4個(gè)角的坐標(biāo)。因此,只要點(diǎn)擊這兩個(gè)坐標(biāo)之間的位置,就等于點(diǎn)擊了這個(gè)搜索按鈕。 為

27、保險(xiǎn)和準(zhǔn)確起見(jiàn),可以點(diǎn)擊這個(gè)搜索按鈕矩形的正中心位置,也就是664,104。那么代碼就可以寫(xiě)成:device.click(664, 104) 當(dāng)然還有一種更簡(jiǎn)單的辦法,那就是把Ui Automator Viewer的窗口拉大一些,將鼠標(biāo)指針在屏幕上面隨意移動(dòng),就可以直接看到鼠標(biāo)指針當(dāng)前位置的坐標(biāo),如圖10-25所示。圖10-25 箭頭指向當(dāng)前鼠標(biāo)指針?biāo)谖恢玫淖鴺?biāo)5輸入文字 既然進(jìn)入了搜索界面,那就需要搜索內(nèi)容了。輸入文本使用的操作是“.set_text()”。TapTap的搜素框?qū)?yīng)的resource-id為“com.taptap:id/input_ box”,如圖10-26所示。圖10-2

28、6 搜索框?qū)?yīng)的resource_id 因此可以用這個(gè)resource-id來(lái)作為選擇器定位到搜索框,再輸入文本。 例如搜索名為“漢家江湖”的游戲,那么就使用以下代碼:device(resourceId=com.taptap:id/input_box).set_text(漢家江湖)6判斷元素是否存在 由于手機(jī)上面的各個(gè)元素加載是需要一定時(shí)間的,如果在元素加載出來(lái)之前就對(duì)其進(jìn)行操作,就會(huì)導(dǎo)致程序報(bào)錯(cuò),如圖10-27所示。這是因?yàn)椴淮嬖谝粋€(gè)resource-id為“com.taptap:id/input_box33”的元素。 在操作一個(gè)元素之前,先判斷一下它是否存在是比較明智的做法。判斷元素是否存

29、在,使用“.exists”屬性。如果存在,值為T(mén)rue,否則為False。其用法為:input_box = device(resourceId=com.taptap:id/input_box)if input_box.exists: input_box.set_text(漢家江湖)else: print(搜索框不存在)圖10-27 操作一個(gè)不存在的元素導(dǎo)致程序報(bào)錯(cuò)7點(diǎn)亮關(guān)閉屏幕 使用“.wakeup()”方法和“.sleep()”方法可以點(diǎn)亮或者關(guān)閉屏幕。當(dāng)手機(jī)屏幕處于關(guān)閉狀態(tài)時(shí),使用“.wake()”方法可以讓屏幕點(diǎn)亮;當(dāng)手機(jī)屏幕處于點(diǎn)亮狀態(tài)時(shí),使用“.sleep()”方法可以將其關(guān)閉。其用

30、法為:device.wakeup() # 點(diǎn)亮屏幕device.sleep() # 關(guān)閉屏幕8操作實(shí)體按鍵 Android一般自帶不少實(shí)體按鍵,使用Python的uiautomator可以模擬這些按鍵被按下的狀態(tài)。其使用方法為:device.press.實(shí)體按鍵名稱() 常見(jiàn)的實(shí)體按鍵名稱和作用如表10-1所示。實(shí)體按鍵名稱作用power電源鍵back返回鍵menu菜單鍵volume_up音量增大volume_down音量減小home返回桌面表10-1 常見(jiàn)的實(shí)體按鍵名稱和作用9watcher 使用Android手機(jī)的時(shí)候,經(jīng)常會(huì)遇到這樣的情況:手機(jī)用著用著,突然彈出一個(gè)對(duì)話框,提示當(dāng)前App

31、有新版本可用。對(duì)于用戶來(lái)說(shuō),遇到這種對(duì)話框,又不想升級(jí),那么按一下返回鍵就可以暫時(shí)把這個(gè)對(duì)話框關(guān)閉。但是這種對(duì)話框?qū)τ谑褂贸绦騺?lái)操作手機(jī)的場(chǎng)景來(lái)說(shuō),就是一個(gè)災(zāi)難。 假設(shè)現(xiàn)在有幾百個(gè)游戲,需要通過(guò)TapTap搜索它們,并獲取它們的下載量。正常的流程應(yīng)該是下面這樣。(1)初始位置:搜索框。(2)清空搜索框原有的文字。(3)輸入游戲名字。(4)點(diǎn)擊搜索按鈕。(5)點(diǎn)擊第1個(gè)搜索結(jié)果進(jìn)入游戲詳情頁(yè)。(6)從游戲詳情頁(yè)獲取下載量。(7)點(diǎn)擊實(shí)體按鍵back返回搜索界面。(8)轉(zhuǎn)到步驟(1)。圖10-28 假設(shè)手機(jī)屏幕當(dāng)前在詳情頁(yè) 在正常情況下,可以按照從(1)(8)的順序循環(huán)往復(fù),直到搜索完所有的游戲。

32、但是如果在第(6)步獲取了下載量以后,突然彈出了升級(jí)提示框。 此時(shí)由于程序不知道彈出了框,它還在執(zhí)行第(7)步,按下了返回鍵。 此時(shí)僅僅是關(guān)閉了升級(jí)提示的對(duì)話框,手機(jī)屏幕仍然還處于游戲的詳情頁(yè),但是程序以為已經(jīng)返回了搜索界面,于是嘗試操作搜索框??墒窃谠斍轫?yè)根本就沒(méi)有搜索框,于是就會(huì)導(dǎo)致程序報(bào)錯(cuò)。 這種情況當(dāng)然可以使用exists來(lái)判斷搜索框是否存在。但問(wèn)題在于,即使知道了搜索框不在,卻并不能解決該問(wèn)題。因?yàn)楦驹蛟谟谑謾C(jī)屏幕和程序出現(xiàn)了不同步,要解決問(wèn)題,就需要讓它們同步。 使用watcher可以讓程序在找不到元素時(shí)自動(dòng)嘗試解決問(wèn)題。假設(shè)目前手機(jī)屏幕所在的界面為詳情頁(yè),如圖10-28所示。

33、10其他操作 Android的UI Automator框架可以實(shí)現(xiàn)所有圖形界面的操作,只要是人能做的它都能做。Python的uiautomator庫(kù)完整實(shí)現(xiàn)了這些操作。但是由于一些操作對(duì)于開(kāi)發(fā)爬蟲(chóng)來(lái)說(shuō)并沒(méi)有什么作用,例如雙指縮放屏幕、旋轉(zhuǎn)屏幕等操作。本書(shū)對(duì)于這些暫時(shí)用不到的操作省略。感興趣的讀者可以參閱uiautomator的官方文檔。10.2 綜合應(yīng)用10.2.1 單設(shè)備應(yīng)用 設(shè)想這樣一個(gè)場(chǎng)景,你需要每天晚上23點(diǎn)給女朋友發(fā)送一條晚安微信,但是那個(gè)時(shí)間你可能正在打游戲,忘記發(fā)微信了。1微信自動(dòng)發(fā)信器 使用Python的uiautomator,可以做一個(gè)自動(dòng)發(fā)送微信的程序,每天晚上23點(diǎn)自動(dòng)打

34、開(kāi)微信,打開(kāi)女朋友的聊天窗口,從數(shù)據(jù)庫(kù)里隨機(jī)選擇一條晚安短信,并自動(dòng)發(fā)送出去。 這個(gè)自動(dòng)發(fā)送微信的程序應(yīng)該具備如下的功能。(1)自動(dòng)點(diǎn)亮屏幕。(2)自動(dòng)從桌面點(diǎn)開(kāi)微信。(3)自動(dòng)從微信最近聯(lián)系人列表中找到女朋友。(4)自動(dòng)點(diǎn)開(kāi)聊天窗口,輸入內(nèi)容。(5)自動(dòng)發(fā)送聊天內(nèi)容。圖10-29 在沒(méi)有輸入內(nèi)容時(shí),微信沒(méi)有“發(fā)送”按鈕 為了能夠簡(jiǎn)化代碼,需要首先去掉手機(jī)的屏鎖,保證一點(diǎn)亮屏幕就能直接進(jìn)入桌面。雖然Python的uiautomator是可以操作屏幕實(shí)現(xiàn)自動(dòng)滑動(dòng)解鎖,甚至直接輸入密碼或者自動(dòng)找出圖像解鎖密碼的,但是這樣會(huì)增加工作量,反而不劃算。 一般在Android的系統(tǒng)設(shè)置中,在“屏鎖、密碼和

35、指紋”或者“安全設(shè)置”里面可以找到去除屏鎖的選項(xiàng)。 去除屏鎖以后,使用USB與計(jì)算機(jī)保持連接。這樣準(zhǔn)備工作就做好了。 點(diǎn)亮屏幕以后,程序應(yīng)該要去尋找微信圖標(biāo),如果當(dāng)前屏幕頁(yè)找不到,那么就左右滑動(dòng)屏幕,直到找到為止。對(duì)于小米手機(jī)的系統(tǒng)來(lái)說(shuō),所有圖標(biāo)都是直接擺放在桌面上的,直接左右拖動(dòng)就能找到。而另外大部分Android手機(jī),都有一個(gè)“抽屜”的功能,很多App都要進(jìn)入抽屜以后才能找到。對(duì)于這種情況,建議把微信的快捷方式添加到桌面,這樣可以減少進(jìn)入抽屜的步驟。 進(jìn)入微信以后,尋找名字為“女朋友”的微信號(hào),為了防止女朋友隨時(shí)改微信名字導(dǎo)致程序失效,建議給女朋友的微信添加一個(gè)備注名。之后就直接用這個(gè)備注

36、名來(lái)尋找。如果當(dāng)前屏幕找不到,那就向下滾動(dòng)屏幕,直到找到為止。 進(jìn)入聊天窗口以后,找到輸入框,輸入文本并單擊發(fā)送按鈕就是很常見(jiàn)的操作了。 唯一需要注意的是,對(duì)于微信聊天界面,在初始沒(méi)有輸入文字的時(shí)候,是沒(méi)有“發(fā)送”按鈕的,只有一個(gè)表情按鈕和一個(gè)“+”號(hào),如圖10-29所示。2創(chuàng)建Mac OS與Linux定時(shí)任務(wù) 對(duì)于Mac OS與Linux,可以使用crontab來(lái)進(jìn)行定時(shí)。 首先確定自己的Python所在的位置。打開(kāi)終端,輸入which python,屏幕上會(huì)出現(xiàn)Python的路徑,如圖10-30所示。圖10-30 查看當(dāng)前Python的位置 將這個(gè)路徑復(fù)制下來(lái),并以下面這個(gè)格式寫(xiě)到程序的第

37、1行,如圖10-31所示。#! Python路徑 在終端進(jìn)入這個(gè)程序所在的文件夾,為這個(gè)程序添加可執(zhí)行權(quán)限:chmod +x example_automatic_wechat.py 這樣操作以后,只要在終端進(jìn)入這個(gè).py文件所在的文件夾,并輸入./example_automatic_wechat.py,就可以運(yùn)行這個(gè)程序。圖10-31 將Python的路徑添加到程序的第1行 權(quán)限添加好以后,就可使用crontab來(lái)設(shè)置定時(shí)執(zhí)行了。在終端輸入crontabe,打開(kāi)crontab的編輯界面。如果是第一次使用crontab,那么打開(kāi)以后里面應(yīng)該是空白的,如圖10-32所示。圖10-32 第一次使用c

38、rontab,打開(kāi)以后應(yīng)該是空白 在英文輸入法的狀態(tài)下,按下鍵盤(pán)的i鍵進(jìn)入輸入模式,此時(shí)左下角會(huì)出現(xiàn)“-INSERT-”。輸入以下內(nèi)容:0 23 * * * /Users/kingname/book/chapter_10/program/example_automatic_wechat.py 注意,把文件路徑修改為實(shí)際路徑。輸入完成以后,如圖10-33所示。圖10-33 創(chuàng)建一條定時(shí)任務(wù)在每天23點(diǎn)運(yùn)行程序 輸入完成以后,按Esc鍵,確保左下角的“-INSERT-”消失。然后按Shift+:組合鍵,輸入英文冒號(hào)再輸入wq,這3個(gè)字符會(huì)出現(xiàn)在左下角,如圖10-34所示。 按下Enter鍵,定時(shí)任

39、務(wù)就保存好了。這樣每到晚上23點(diǎn)0分,程序就會(huì)自動(dòng)打開(kāi)微信發(fā)送晚安信息了。圖10-34 輸入:wq3Windows創(chuàng)建定時(shí)任務(wù) 要在Windows中創(chuàng)建定時(shí)任務(wù),首先需要在example_automatic_ wechat.py文件所在的文件夾下面創(chuàng)建一個(gè)run_automatic_wechat.txt文件,在文件里面輸入python3 example_automatic_wechat.py后保存,并把文件名run_automatic_wechat.txt改為run_automatic_wechat.bat。此時(shí),如果雙擊這個(gè)run_automatic_wechat.bat,應(yīng)該可以看到自動(dòng)發(fā)

40、微信的程序成功運(yùn)行。 打開(kāi)CMD,輸入以下命令來(lái)創(chuàng)建定時(shí)任務(wù),使程序在每天的23點(diǎn)自動(dòng)運(yùn)行:schtasks /create /tn run_automatic_wechat/tr C:run_automatic_wechat /sc DAILY /st 23:00:0010.2.2 多設(shè)備應(yīng)用(群控)1實(shí)現(xiàn)原理 使用uiautomator來(lái)做爬蟲(chóng)開(kāi)發(fā),最主要的瓶頸在于速度。因?yàn)槠聊簧系脑丶虞d是需要時(shí)間的,這個(gè)時(shí)間受到手機(jī)性能和網(wǎng)速的多重限制。因此比較好的辦法是使用多臺(tái)Android手機(jī)實(shí)現(xiàn)分布式抓取。使用USBHub擴(kuò)展計(jì)算機(jī)的USB口以后,一臺(tái)計(jì)算機(jī)控制30臺(tái)Android手機(jī)是完全沒(méi)有

41、問(wèn)題的。只要能實(shí)現(xiàn)良好的調(diào)度和任務(wù)派分邏輯,就可以大大提高數(shù)據(jù)的抓取效率。2另一種多線程 在第4章介紹了一種比較簡(jiǎn)單的實(shí)現(xiàn)多線程的方式,即使用multiprocess.dummy來(lái)實(shí)現(xiàn)。那種方式的代碼看起來(lái)非常簡(jiǎn)單,但是不方便做各種定制化操作。因此本小節(jié)將會(huì)介紹Python中另一種實(shí)現(xiàn)多線程的辦法threading.Thread。 為了便于管理和調(diào)度各個(gè)子線程,將子線程定義為一個(gè)類是比較好的做法,每臺(tái)手機(jī)對(duì)應(yīng)一個(gè)線程。而要將子線程定義為一個(gè)類,就需要這個(gè)類繼承于threading.Thread。 一個(gè)做線程的類,其代碼結(jié)構(gòu)如下:class PhoneThread(threading.Threa

42、d): def _init_(self): threading.Thread._init_(self) def run(self): pass 這段代碼實(shí)現(xiàn)了一個(gè)什么事情都不做的子線程類。其中,在“._init_()”初始化方法的第一行必須有threading.Thread._init_ (self),否則會(huì)導(dǎo)致程序報(bào)錯(cuò)?!?run()”方法是子線程的入口,啟動(dòng)子線程后,它會(huì)從run()方法開(kāi)始執(zhí)行。 要啟動(dòng)一個(gè)子線程,需要先將它初始化為一個(gè)實(shí)例,然后調(diào)用.start()方法。例如:phone = PhoneThread()phone.start() 當(dāng)調(diào)用.start()方法以后,子線程類里

43、面的.run()方法中的代碼就會(huì)被運(yùn)行。 需要注意的是,一個(gè)子線程實(shí)例的生命只有一次。一旦它里面的代碼被運(yùn)行完成,生命就結(jié)束了,這個(gè)實(shí)例就再也不能用了,只能重新創(chuàng)建新的子線程實(shí)例。通過(guò)子線程實(shí)例的“.is_alive()”方法可以查看它是否還活著,如圖10-35所示。 圖10-35 判斷子線程是否還活著 為了防止子線程提前死掉,就需要將子線程實(shí)現(xiàn)功能的代碼放到while循環(huán)里面,讓它一直不停地工作,只有工作完成以后才允許它死掉。 由于子線程是被主線程啟動(dòng)的,因此必須要讓主線程在所有子線程都結(jié)束以后才能結(jié)束。否則,一旦主線程運(yùn)行完成,所有子線程都會(huì)被強(qiáng)行關(guān)閉。因此,在主線程啟動(dòng)了所有的子線程以后

44、,必須“做點(diǎn)什么事”來(lái)等待,例如輪詢檢查是否所有的子線程都結(jié)束了,如圖10-36所示。 圖10-36 主線程輪詢檢查是否所有的子線程都結(jié)束了 在圖10-36所示的這段代碼中,由于子線程會(huì)隨機(jī)睡10500s,主線程不知道它們什么時(shí)候才會(huì)全部結(jié)束。因此主線程要在每60s檢查一下10個(gè)子線程的狀態(tài),直到發(fā)現(xiàn)所有的子線程都結(jié)束了,主線程才能跳出while循環(huán)。3應(yīng)用實(shí)例 整個(gè)系統(tǒng)的架構(gòu)可以設(shè)計(jì)成圖10-37所示的形式。 主線程負(fù)責(zé)啟動(dòng)子線程,讀數(shù)據(jù)庫(kù),得到需要手機(jī)處理的各種目標(biāo),并將目標(biāo)存放在Redis里面。 手機(jī)子線程循環(huán)不斷地從Redis中讀取目標(biāo),在手機(jī)上面抓取并將原始數(shù)據(jù)保存到Redis中。另

45、一個(gè)負(fù)責(zé)數(shù)據(jù)處理的子線程負(fù)責(zé)將手機(jī)抓取的原始數(shù)據(jù)清洗并放入數(shù)據(jù)庫(kù)中。 這里使用一個(gè)例子來(lái)演示這個(gè)架構(gòu)的使用。使用兩臺(tái)Android手機(jī),從TapTap上面抓取20個(gè)游戲的安裝量或者預(yù)訂量。 首先創(chuàng)建一個(gè)文本文件target,用來(lái)保存20個(gè)目標(biāo)游戲的名稱。當(dāng)然讀者也可以使用數(shù)據(jù)庫(kù)來(lái)保存。這20個(gè)游戲的名稱如圖10-38所示。 圖10-37 App爬蟲(chóng)系統(tǒng)架構(gòu)的形式 圖10-38 20個(gè)目標(biāo)游戲名稱 正常情況下,在搜素框輸入內(nèi)容并單擊搜索圖標(biāo)即可出現(xiàn)結(jié)果,如圖10-39所示。 但是在少數(shù)情況下,可能由于網(wǎng)速問(wèn)題或者手機(jī)性能問(wèn)題,在點(diǎn)了搜索圖標(biāo)以后,并沒(méi)有出現(xiàn)搜索結(jié)果,如圖10-40所示。圖10-39 正常情況下直接搜索出結(jié)果圖10-40 點(diǎn)了搜索圖標(biāo)卻沒(méi)有反應(yīng) 匹配游戲名字的時(shí)候,用的是“textContains=game_name”而不是“text=name”。textContains的意思為文本包含某些字符即可。這是因?yàn)橛幸恍┯螒蛟诿值那昂髸?huì)有特殊符號(hào),例如爐石傳說(shuō),名字上多了書(shū)名號(hào),顯然“爐石傳說(shuō)不等于爐石傳說(shuō)”,但是“爐石傳說(shuō)包含爐石傳說(shuō)”,如圖10-41所示。圖10-41 爐石傳說(shuō)的搜索結(jié)果包含書(shū)名號(hào) 在主線程中調(diào)用子線程的代碼如下:for serial in serial_list: phone_thread = PhoneThread(serial) phone_th

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論