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的安裝:
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
我們按照崔大的教程把 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都已經誕生了………
如有錯誤,歡迎指正~