爬取思路

爬取思路:模拟登录、抓取动态、保存数据

2. Android SDK的安装与配置

打开Android Studio, 选择"Configure->SDK Manager->Apperance&Behavior->System Settings->Android SDK", 选择对应安卓机版本的SDK,如图:

sdk

此外,还需要将SDK所在路径添加到系统环境变量中,否则报错。

3. Desired Capabilites 参数

分别是:platfornName, deviceName, appPackage, appActivity。前两个可通过如下命令获取, 前提是连接手机、打开USB调试:

adb devices -l

devicename

后两个参数请移步:获取appPackage和appActivity

4. 开启安卓的" 开发者选项、USB调试 "

测试之前,确保打开 开发者选项、USB调试。开发者模式确保调试程序在手机安装辅助软件:Unlock, Appium Settings;USB调试主要是利用Appium内置驱动打开APP。此外,要保持屏幕长亮。

5. 节点ID或XPATH值获取

安卓微信节点获取,相对比较容易获取,比如获取"登录"ID值,启动Session后只需点击屏幕左侧安卓屏的登录按钮,中间就会自动定位到所在节点,最右侧还会显示该节点的所有属性。如图:

element

对于文本输入框,只需要点击最右侧的"send text"即可。

6. "是否匹配通讯录"

这里选择"否", 理由:重新登录进入微信后会自动加载本地数据,耗时较长,如果匹配通讯录好友,增加耗时,可能在TIMEOUT时间内获取不到节点,导致程序终止。我这里选择了"是", 如图:

no

7. 朋友圈信息获取思路

获取当前显示的朋友圈每条状态对应的区块元素,遍历每个区块元素,再获取内部显示的用户名、正文、发布时间,代码如下:

'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''
# items存储当前页面所有发布的朋友圈信息
items = self.wait.until(
    EC.presence_of_all_elements_located(
        # 每个ej9节点对应一条朋友圈数据
        (By.XPATH, '//*[@resource-id="com.tencent.mm:id/ej9"]/android.widget.LinearLayout')))

for item in items:
    try:
        # 昵称
        nickname = item.find_element_by_id('com.tencent.mm:id/b5o').get_attribute('text')
        # 正文
        content = item.find_element_by_id('com.tencent.mm:id/ejc').get_attribute('text')
        # 日期
        date = item.find_element_by_id('com.tencent.mm:id/eec').get_attribute('text')
        # 处理日期
        date = self.processor.date(date)
        data = {
            'nickname': nickname,
            'content': content,
            'date': date,
        }

 8.日期处理

日期处理依旧采用崔大的实现方式,代码如下:

class Processor():
    def date(self, datetime):
        """
        格式化时间
        :param date: 原始时间
        :return: 处理后时间
        """
        if re.match('\d+分钟前', datetime):
            minute = re.match('(\d+)', datetime).group(1)
            datetime = time.strftime('%Y-%m-%d', time.localtime(time.time() - float(minute) * 60))
        if re.match('\d+小时前', datetime):
            hour = re.match('(\d+)', datetime).group(1)
            datetime = time.strftime('%Y-%m-%d', time.localtime(time.time() - float(hour) * 60 * 60))
        if re.match('昨天', datetime):
            datetime = time.strftime('%Y-%m-%d', time.localtime(time.time() - 24 * 60 * 60))
        if re.match('\d+天前', datetime):
            day = re.match('(\d+)', datetime).group(1)
            datetime = time.strftime('%Y-%m-%d', time.localtime((time.time()) - float(day) * 24 * 60 * 60))
        return datetime

完整代码

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
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from time import sleep
from config import *
from pymongo import MongoClient
from processor import Processor
'''
想要学习Python?Python学习交流群:984632579满足你的需求,资料都已经上传群文件,可以自行下载!
'''

class Moments(object):
    def __init__(self):
        """
        初始化
        """
        # 启动APP的参数配置
        self.desired_caps = {
            'platformName': PLANTFORM,
            'deviceName': DEVICE_NAME,
            'appPackage': APP_PACKAGE,
            'appActivity': APP_ACTIVITY,
        }
        self.driver = webdriver.Remote(DRIVER_SERVER, self.desired_caps)
        self.wait = WebDriverWait(self.driver, TIMEOUT)
        self.client = MongoClient(MONGO_URL)
        self.db = self.client[MONGO_DB]
        self.collection = self.db[MONGO_COLLECTION]
        # 处理日期
        self.processor = Processor()

    def login(self):
        """
        登录
        """
        # 点击登录
        login = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/e4g')))
        login.click()
        # 输入手机号
        phone = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/kh')))
        phone.set_text(USERNAME)
        # 点击下一步
        next = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/axt')))
        next.click()
        # 输入密码
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/kh')))
        password.set_text(PASSWORD)
        # 提交
        submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/axt')))
        submit.click()
        # 通讯录匹配提示(这里选"否",加快tab节点的加载速度)
        # yes = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/az_')))
        no = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/az9')))
        no.click()

    def enter(self):
        # 点击"发现"
        # global explore
        # explore = self.driver.find_element_by_android_uiautomator('new UiSelector().text("发现")')
        """
        try:
            explore = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/d7_')))
        except:
            self.driver.refresh()
            explore = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/b7b')))
        """
        explore = self.wait.until(EC.presence_of_element_located((
                                            By.XPATH, '//*[@class="android.widget.RelativeLayout"][3]')))
        # explore = self.wait.until(EC.presence_of_element_located((By.XPATH,'//android.widget.FrameLayout[@content-desc="当前所在页面,与的聊天"]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.view.View/android.widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.RelativeLayout[3]')))
        explore.click()
        # 朋友圈
        # momnets = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/y6')))
        momnets = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/d7v')))
        momnets.click()

    def crawl(self):
        """
        无限拖动
        :return:
        """
        cnt = 0
        while True:
            # items存储当前页面所有发布的朋友圈信息
            items = self.wait.until(
                EC.presence_of_all_elements_located(
                    # 每个ej9节点对应一条朋友圈数据
                    (By.XPATH, '//*[@resource-id="com.tencent.mm:id/ej9"]/android.widget.LinearLayout')))
            # 上滑刷新朋友圈
            self.driver.swipe(FLICK_START_X, FLICK_START_Y + FLICK_DISTANCE, FLICK_START_X, FLICK_START_Y)

            for item in items:
                try:
                    # 昵称
                    nickname = item.find_element_by_id('com.tencent.mm:id/b5o').get_attribute('text')
                    # 正文
                    content = item.find_element_by_id('com.tencent.mm:id/ejc').get_attribute('text')
                    # 日期
                    date = item.find_element_by_id('com.tencent.mm:id/eec').get_attribute('text')
                    # 处理日期
                    date = self.processor.date(date)
                    data = {
                        'nickname': nickname,
                        'content': content,
                        'date': date,
                    }
                    print("昵称:", data['nickname'])
                    print("正文:", data['content'])
                    print("时间:", data['date'])
                    # 根据昵称和正文来查询信息,然后设置第三个参数为True.实现存在就更新,不存在就插入数据
                    self.collection.update({'nickname': nickname, 'content': content}, {'$set': data}, True)
                    sleep(SCROLL_SLEEP_TIME)
                except NoSuchElementException:
                    # print('Error')
                    pass

            # 循环加1
            cnt = cnt + 1
            if cnt == 200:
                break

    def main(self):
        """
        入口
        :return:
        """
        # 登录
        self.login()
        # 进入朋友圈
        self.enter()
        # 爬取
        self.crawl()

if __name__ == '__main__':
    moments_ = Moments()
    moments_.main()


本文转载:CSDN博客