1. 程式人生 > >python3 爬蟲實戰 :用 Appium 抓取手機 app 微信 的 資料

python3 爬蟲實戰 :用 Appium 抓取手機 app 微信 的 資料

 

From:https://blog.csdn.net/Fan_shui/article/details/81413595

 

本編教程從 appium 的環境配置開始,到抓取手機 app 微信朋友圈結束。

知乎:https://zhuanlan.zhihu.com/p/41311503
GitHub:https://github.com/FanShuixing/git_webspider

 

參考博文
appium+python 環境搭建:https://www.cnblogs.com/yoyoketang/p/6128725.html
[Python3網路爬蟲開發實戰] 1.7.3-Appium的安裝:

https://cuiqingcai.com/5407.html
java環境配置:https://jingyan.baidu.com/article/fd8044fa2c22f15031137a2a.html
appium 獲取 appPackage 和 appActivity : https://blog.csdn.net/mtbaby/article/details/78676477
selenium 動態抓取網頁:https://blog.csdn.net/Fan_shui/article/details/81516645

 

 

環境搭建

 

最開始是按照崔大的環境搭建 https://cuiqingcai.com/5407.html

,然後弄完之後發現在 notepad 裡面寫的 python 執行不了,不能開啟手機端的 app,appium 倒是可以開啟。另外一篇環境搭建的博文安裝的 appium 是舊版,我推薦先按照崔大的教程把appium 環境和 Android studio 搭建下載好,重點:我們在安裝的時候,安裝環境只要是可以改的都改,不要按照預設的下在c 盤,c 盤就那麼大個兒,最重要的是要清楚自己的安裝位置,後面會用到。(當然也可以直接按照悠悠博主的教程一步步的搭建下來,若是這樣,環境搭建下面就都可以不用看了)
我們按照崔大的教程把 appium 環境 和 Android studio 搭建好,其中第三步我再詳細加一點,下圖是崔大文中的第三步

我的安裝位置在F:/SDK

在環境變數中,系統變數下增加一個這樣

在系統變數的path中增加個下面兩個

弄好後,我們開啟另外一篇博文 https://www.cnblogs.com/yoyoketang/p/6128725.html,一步步按照教程來,其中第三步、四步android_sdk下載就不用再下了,崔大的博文中已經下過。(ps:悠悠博主的後面幾篇環境搭建也要看)

 

 

appium 的使用

 

恭喜恭喜,走到這一步,我走到這兒可是花了好幾天的時間。本文使用的真機,沒用模擬器,感興趣的可以搜下模擬器的使用。我們用usb連線上手機,要開啟手機上的usb除錯,然後輸入adb devices -l (不是數字1,是小寫 L )

出現上圖就證明手機和電腦連線成功,若是出現下圖這種,就把手機拔掉再重連一下

成功後,開啟appium

platformName:平臺名稱
deviceName:裝置名稱,就是剛才的adb devices -l中mode後面就是
appPackage:app包名
appActivity:app活動名
有個簡單的方法便可以獲得appPackage和appActivity:https://blog.csdn.net/mtbaby/article/details/78676477

start sessions後

我們可以點選左邊的登陸(忍不住抱怨下微信,我不就是多登陸了兩下,然後微訊號被封了一天(*  ̄︿ ̄)),點選登陸後可以看到中間的App Source有高亮的程式碼,就是這個登陸按鈕的,可以再看右邊的Selected Element中有Tap、Send keys、Clear。
在最左邊圖中點選登陸後,若最右邊下面的clickable是True,則證明可以點選,可以通過點選Tap實現點選功能,appium就介紹到這。 

 

 

python對接Appium

 

首先我們要對接app,就是類似於start session這樣的連線
注:程式碼執行時要保證手機不黑屏

from appium import webdriver 
from selenium.webdriver.support.ui import WebDriverWait 

PLATFORM='Android' 
deviceName='HUAWEI_P7_L09' 
app_package='com.tencent.mm' 
app_activity='.ui.LauncherUI' 
driver_server='http://127.0.0.1:4723/wd/hub' 


class Moments(): 
    def __init__(self): 
        self.desired_caps={ 
            'platformName':PLATFORM, 
            'deviceName':deviceName, 
            'appPackage':app_package, 
            'appActivity':app_activity
        } 
        self.driver=webdriver.Remote(driver_server, self.desired_caps) 
        self.wait=WebDriverWait(self.driver,300) 
    
    def login(self): 
        print('正在登陸中——————') 
        
    def main(self): 
        self.login() 


M = Moments() 
M.main()

如果出現 urllib.error.URLError: urlopen error [WinError 10061] 由於目標計算機積極拒絕,無法連線。
看看開啟appium沒有,開啟後再執行上面的程式碼就沒有問題,手機會自動轉到微信的登陸介面(注:deviceName要改成自己手機的) 

開啟微信後,我們要模擬點選登陸按鈕,首先要定位到登陸元素,這個跟selenium的用法類似,我們可以開啟appium用前面的方式連線微信,也可以開啟sdk安裝路徑下tools中的uiautomatorviewer.bat,第二種方法比較快

下圖中,要先點選圖中的按鈕,才會出現手機上的畫面,圖中我已經定位到登陸按鈕,可以在右邊看到屬性resource-id,以及clickable=True,確實是可以點選的,為什麼要這樣確認下呢,因為有的你以為可以點選的元素,也許你定位沒有定好,比如說你定位到登陸外更大的一個框,它是不可點選的 

from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait 
from selenium.webdriver.support import expected_conditions as EC 
from selenium.webdriver.common.by import By 


PLATFORM = 'Android' 
deviceName = 'HUAWEI_P7_L09' 
app_package = 'com.tencent.mm' 
app_activity = '.ui.LauncherUI' 
driver_server = 'http://127.0.0.1:4723/wd/hub' 


class Moments(): 
    def __init__(self): 
        self.desired_caps={ 
            'platformName': PLATFORM, 
            'deviceName': deviceName, 
            'appPackage': app_package, 
            'appActivity': app_activity
        } 
        self.driver = webdriver.Remote(driver_server, self.desired_caps) 
        self.wait = WebDriverWait(self.driver, 300) 
        
    def login(self): 
        print('點選登陸按鈕——————') 
        login = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/d75'))) 
        login.click() 
    
    def main(self): 
        self.login() 


M = Moments() 
M.main()

執行後就會跳出登陸介面,接下來就都是定位元素與點選元素的事情:

from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

PLATFORM = 'Android'
deviceName = 'HUAWEI_P7_L09'
app_package = 'com.tencent.mm'
app_activity = '.ui.LauncherUI'
driver_server = 'http://127.0.0.1:4723/wd/hub'


class Moments():
    def __init__(self):
        self.desired_caps = {
            'platformName': PLATFORM,
            'deviceName': deviceName,
            'appPackage': app_package,
            'appActivity': app_activity
        }
        self.driver = webdriver.Remote(driver_server, self.desired_caps)
        self.wait = WebDriverWait(self.driver, 300)

    def login(self):
        print('點選登陸按鈕——————')
        login = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/d75')))
        login.click()
        # 輸入手機號 
        phone = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/hz')))
        phone_num = input('請輸入手機號')
        phone.send_keys(phone_num)
        print('點選下一步中')
        button = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/alr')))
        button.click()
        pass_w = input('請輸入密碼:')
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/hz')))
        password.send_keys(pass_w)
        login = self.driver.find_element_by_id('com.tencent.mm:id/alr')
        login.click()

        # 提示 叉掉 
        tip = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/an2')))
        tip.click()

    def main(self):
        self.login()


M = Moments()
M.main()

現在已經進入到微信了,我們需要先定位到微信下面的 發現->朋友圈

from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time

PLATFORM = 'Android'
deviceName = 'HUAWEI_P7_L09'
app_package = 'com.tencent.mm'
app_activity = '.ui.LauncherUI'
driver_server = 'http://127.0.0.1:4723/wd/hub'


class Moments():
    def __init__(self):
        self.desired_caps = {
            'platformName': PLATFORM,
            'deviceName': deviceName,
            'appPackage': app_package,
            'appActivity': app_activity
        }
        self.driver = webdriver.Remote(driver_server, self.desired_caps)
        self.wait = WebDriverWait(self.driver, 300)

    def login(self):
        print('點選登陸按鈕——————')
        login = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/d75')))
        login.click()
        # 輸入手機號 
        phone = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/hz')))
        phone_num = input('請輸入手機號')
        phone.send_keys(phone_num)
        print('點選下一步中')
        button = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/alr')))
        button.click()
        pass_w = input('請輸入密碼:')
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/hz')))
        password.send_keys(pass_w)
        login = self.driver.find_element_by_id('com.tencent.mm:id/alr')
        login.click()
        # 提示 叉掉 
        tip = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/an2')))
        tip.click()

    def enter(self):
        print('點擊發現——')
        tab = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@resource-id="com.tencent.mm:id/cdh"]/..')))
        print('已經找到發現按鈕')
        time.sleep(6)
        tab.click()
        # self.wait.until(EC.text_to_be_present_in_element((By.ID,'com.tencent.mm:id/cdj'),'發現')) 
        print('點選朋友圈')
        friends = self.wait.until(EC.presence_of_element_located(
            (By.XPATH, '//*[@resource-id="android:id/list"]/*[@class="android.widget.LinearLayout"][1]')))
        friends.click()

    def main(self):
        self.login()
        self.enter()


M = Moments()
M.main()

都是些元素定位,多用幾次就會了。接下來我們可以提取資料了

from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
import pymongo

PLATFORM = 'Android'
deviceName = 'HUAWEI_P7_L09'
app_package = 'com.tencent.mm'
app_activity = '.ui.LauncherUI'
driver_server = 'http://127.0.0.1:4723/wd/hub'


class Moments():
    def __init__(self):
        self.desired_caps = {
            'platformName': PLATFORM,
            'deviceName': deviceName,
            'appPackage': app_package,
            'appActivity': app_activity
        }
        self.driver = webdriver.Remote(driver_server, self.desired_caps)
        self.wait = WebDriverWait(self.driver, 300)
        self.client = pymongo.MongoClient()
        self.db = self.client.weixin
        self.collection = self.db.weixin

    def login(self):
        print('點選登陸按鈕——————')
        login = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/d75')))
        login.click()
        # 輸入手機號
        phone = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/hz')))
        phone_num = input('請輸入手機號:')
        phone.send_keys(phone_num)
        print('點選下一步中')
        button = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/alr')))
        button.click()
        pass_w = input('請輸入密碼:')
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/hz')))
        password.send_keys(pass_w)
        login = self.driver.find_element_by_id('com.tencent.mm:id/alr')
        login.click()

        # 提示 叉掉
        tip = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/an2')))
        tip.click()

    def enter(self):
        print('點擊發現——')
        tab = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@resource-id="com.tencent.mm:id/cdh"]/..')))
        print('已經找到發現按鈕')
        time.sleep(6)
        tab.click()
        # self.wait.until(EC.text_to_be_present_in_element((By.ID,'com.tencent.mm:id/cdj'),'發現'))
        print('點選朋友圈')
        friends = self.wait.until(EC.presence_of_element_located(
            (By.XPATH, '//*[@resource-id="android:id/list"]/*[@class="android.widget.LinearLayout"][1]')))
        friends.click()

    def crawl(self):
        while True:
            items = self.wait.until(EC.presence_of_all_elements_located(
                (By.XPATH, '//*[@resource-id="com.tencent.mm:id/dja"]//*[@class="android.widget.FrameLayout"]')))
            self.driver.swipe(300, 1000, 300, 300)
            for item in items:
                try:
                    nickname = item.find_element_by_id('com.tencent.mm:id/as6').get_attribute('text')
                    print(nickname)
                    content = item.find_element_by_id('com.tencent.mm:id/dkf').get_attribute('text')
                    print(content)
                    data = {'nickname': nickname, 'content': content}
                    self.collection.update({'nickname': nickname, 'content': content}, {'$set': data}, True)
                except BaseException as e:
                    print(e)

    def main(self):
        self.login()
        self.enter()
        self.crawl()


M = Moments()
M.main()

driver.swipe() 是從點 A 滑動到點 B,driver.swipe(300,1000,300,300)是從點(300,1000)滑動到(300,300)
self.collection.update({'nickname':nickname,'content':content},{'$set':data},True)

首先根據暱稱和正文來查詢,如果資訊不存在,則插入資料,否則更新資料,關鍵點是第三個引數True,這可以實現存在即更新,不存在即插入的程式碼,用著感覺很舒服呢

總的來說,感覺學appium挺不容易的,開頭就有個環境配置,後面再加上appium對接python的時候超級慢,除錯要等很久,再加上微信次數登多了會被封一天,所以這篇教程花了很多時間,在這段時間內下一篇mitmdump都已經誕生了………

如有錯誤,歡迎指正~