Appium实现爬取oppo应用商店评论

环境配置

可以直接参考知乎大佬的文章
Appium环境搭建超详细教程

具体实现

连接到你想要爬取的APP

AppiumDesktop控制手机和安卓模拟器
专门为伸手党们整理了oppo应用商店的对应配置
{
“platformName”: “Android”,
“platformVersion”: “6.0”,
“deviceName”: “Andriod2”,
“noReset”: true,
“appPackage”: “com.oppo.market”,
“appActivity”: “.activity.MainActivity”
}
连接上的截图

模拟人操作并拿取部分字段

此处写给出我自己写的一个基于python的appium的辅助工具类

from time import sleep
from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from PIL import Image
class App(object):
    def __init__(self,appPackage,appActivity,no_reset=True,platformVersion="6.0",deviceName="android_first",wait_time=None,udid=None):
        self.desired_caps = {
            'platformName': 'Android',
            'platformVersion': platformVersion,
            'deviceName': deviceName,
            "appPackage":appPackage,
            'appActivity':appActivity
        }
        if udid:
            self.desired_caps['udid']=udid
        if no_reset:
            self.desired_caps['noReset']=no_reset
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', self.desired_caps)
        # 当资源未加载出时,最大等待时间20S
        if wait_time:
            self.driver.implicitly_wait(wait_time)

    def click_by_id(self,id):
        self.driver.find_element_by_id(id).click()

    def close_app(self):
        self.driver.close_app()

    def back(self):
        self.driver.back()

    def click_by_xpath(self,xpath):
        self.driver.find_element_by_xpath(xpath).click()

    def click_by_name(self,name):
        self.driver.find_element_by_name(name).click()

    def click_by_class_name(self,class_name):
        self.driver.find_element_by_class_name(class_name).click()

    def send_keys_by_id(self,id,key):
        self.driver.find_element_by_id(id).send_keys(key)

    def send_keys_by_xpath(self,id,key):
        self.driver.find_element_by_xpath(id).send_keys(key)

    def send_keys_by_name(self,name,key):
        self.driver.find_element_by_name(name).send_keys(key)

    def send_keys_by_class_name(self,class_name,key):
        self.driver.find_element_by_class_name(class_name).send_keys(key)

    def tap_by_position(self,x_position,y_position,x_length=10,y_length=10,time=500):
        self.driver.tap([(x_position-x_length/2,y_position-y_length*2),(x_position+x_length/2,y_position+y_length*2)],time)

    def get_page_source(self):
        return self.driver.page_source

    def swipe(self,y_start,y_end,x_start=500,x_end=500,duration=1000):
        self.driver.swipe(x_start,y_start,x_end,y_end,duration)

    #获取方位和坐标,左上角和右下角的列表
    @staticmethod
    def get_bounds(element):
        rect=element.rect
        return [{'x':rect['x'],'y':rect['y']},{'x':rect['x']+rect['width'],'y':rect['y']+rect['height']}]

    def get_text_by_x_path(self,x_path):
        return str(self.driver.find_element_by_xpath(x_path).text)

    #method为enum,xpath或者id或者name或者class_name
    def swipe_by_element(self,method,value):
        if method=='xpath':
            element=self.driver.find_element_by_xpath(value)
        elif method=='id':
            element=self.driver.find_element_by_id(value)
        elif method=='name':
            element=self.driver.find_element_by_name(value)
        elif method=='class_name':
            element=self.driver.find_element_by_class_name(value)
        else:
            raise Exception("输入参数错误")

        bounds=self.get_bounds(element)
        x_start=x_end=(bounds[0]['x']+bounds[1]['x'])/2
        y_start=bounds[1]['y']
        y_end=bounds[0]['y']
        try:
            self.swipe(y_start,y_end,x_start,x_end)
        except Exception:
            self.swipe(500+abs(y_start-y_end),500)

    @staticmethod
    def screenshot_by_element(driver, element, out_image: str):
        """
        对指定元素截图
        :param driver:
        :param element: 元素
        :param out_image: 截图输出路径(验证码.png)
        """
        # step1 全屏图
        global_image = '全屏图.png'
        driver.get_screenshot_as_file(global_image)

        # step2 获取元素的四个坐标
        min_x = element.location['x']
        min_y = element.location['y']
        max_x = min_x + element.size['width']
        max_y = min_y + element.size['height']

        # step3 从全屏图中裁剪出目的元素的图片
        im = Image.open(global_image)
        im.save(global_image)
        im = Image.open(global_image)
        im = im.crop((min_x, min_y, max_x, max_y))  # 裁剪(左上至右下)
        print(min_x, min_y, max_x, max_y)
        im.save(out_image)

下面是程序主代码

from appium_utils.app import App
import time
import pandas as pd

def keywords_search(keyword):
    app=App('com.oppo.market','.activity.MainActivity',wait_time=30)

    list_title=[]
    time.sleep(10)



    #点击搜索框并输入值
    app.tap_by_position(347,145)
    app.send_keys_by_id("com.oppo.market:id/et_search",keyword)
    app.click_by_id("com.oppo.market:id/search_clear")
    app.send_keys_by_id("com.oppo.market:id/et_search",keyword)
    app.click_by_id("com.oppo.market:id/tv_search_text")


    #点击到详情页
    app.click_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.widget.ViewAnimator/android.widget.FrameLayout/android.widget.ViewAnimator/android.view.ViewGroup/android.widget.FrameLayout/android.widget.ListView/android.widget.LinearLayout[1]/android.widget.RelativeLayout/android.widget.LinearLayout")
    time.sleep(10)


    #点击评论按钮
    app.click_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout[1]/android.widget.ScrollView/android.widget.LinearLayout/android.widget.RelativeLayout[2]/android.widget.LinearLayout/android.widget.TextView[2]")
    time.sleep(10)
    app.swipe(1330, 740)
    k=0

    #开始循环拿评论
    while True:
        k=k+1
        for i in range(1,5):
            try:
                element=app.driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout[1]/android.widget.ScrollView/android.widget.LinearLayout/com.heytap.cdo.client.detail.ui.detail.widget.ColorViewPager/android.widget.ListView/android.widget.RelativeLayout[{}]/android.widget.LinearLayout[2]".format(i))
                user_name=app.get_text_by_x_path("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout[1]/android.widget.ScrollView/android.widget.LinearLayout/com.heytap.cdo.client.detail.ui.detail.widget.ColorViewPager/android.widget.ListView/android.widget.RelativeLayout[{}]/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TextView[1]".format(i))
                desc=app.get_text_by_x_path("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout[1]/android.widget.ScrollView/android.widget.LinearLayout/com.heytap.cdo.client.detail.ui.detail.widget.ColorViewPager/android.widget.ListView/android.widget.RelativeLayout[{}]/android.widget.TextView[2]".format(i))
                publish_time=app.get_text_by_x_path("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout[1]/android.widget.ScrollView/android.widget.LinearLayout/com.heytap.cdo.client.detail.ui.detail.widget.ColorViewPager/android.widget.ListView/android.widget.RelativeLayout[{}]/android.widget.LinearLayout[1]/android.widget.TextView".format(i))
                like_num=app.get_text_by_x_path("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.RelativeLayout[1]/android.widget.ScrollView/android.widget.LinearLayout/com.heytap.cdo.client.detail.ui.detail.widget.ColorViewPager/android.widget.ListView/android.widget.RelativeLayout[{}]/android.widget.TextView[1]".format(i))
                print(user_name+'_'+desc)
                key=hash(user_name+'_'+desc)
                data={"user_name":user_name,"desc":desc,"publish_time":publish_time,"like_num":like_num,"key":key}
                df=pd.DataFrame(data,index=[0])
                df.to_excel(r"data/excel/{}_{}.xlsx".format(keyword,key),index=False)
                app.screenshot_by_element(app.driver,element,"data/photo/{}_{}.png".format(keyword,key))
                list_title.append(key)
            except Exception as e:
                print(e)
                continue
        app.swipe(1330, 540)
        if k>20:
            # app.close_app()
            return

if __name__ == '__main__':
    # keywords_search("平安银行")
    # keywords_search("招商银行")
    keywords_search("中国建设银行")
    #keywords_search("浦发手机银行")

点击搜索框并输入搜索内容

此处我直接使用的tap_by_position方法,根据位置点击屏幕
此处需要注意一个点(可能是oppo应用商店自己的bug)在第一次输入值的时候会带上一些奇怪的东西
搜索bug
如图中输入值其实是浦发手机银行,但前面带上了抖音火山般,因此在程序中有两次输入,即第一次输入之后按删除键,再进行第二次输入

点击到详情页

没啥好说的,由于是精确搜索直接点击第一个元素就行了
点击详情页

点击评论

同样没啥好说的,点就完事了
点击评论

开始循环拿评论

由于我的方案是通过XPATH拿的,通常应用商店的评论的格式都是一样的,因此只需要替换XPATH中的某一个或者某几个参数,从1-5循环就可以了
代码中几个变量分别为
element:星级对应的那一块,用来截图的
user_name:评论者
desc:评论内容
publish_time;评论时间
like_num:点赞数
key:我自己定义的一个唯一键

评论详情
再根据自己定义的key值存成单个单个的excel(文件名按照keyword_key命名,后续要用),将星级截图也存下来。
photo截图长这样
在这里插入图片描述

解析并合并结果

之前那一步已经获得了除了评分之外的字段了,这一步要做的就是合并所有的单条数据,并附加上评分

话不多说,直接上代码

import os
from utils import io
import pandas as pd
from appium_utils.score_utils import score


def process(keyword):
    total_df=pd.DataFrame()
    for root,dirs,files in os.walk(r"data\excel"):
        for file in files:
            total_path=os.path.join(root,file)
            if keyword in total_path:
                df=pd.read_excel(total_path,encoding='utf8')
                photo_path=r"data/photo/{}.png".format(total_path.split("\\")[2].split(".xlsx")[0])
                df['score']=score(photo_path,10,10,30,215,215,215,255)# 要识别像素的坐标
                total_df=pd.concat([total_df,df],axis=0)
                io.move_file(total_path, r"data/checked")
    total_df.to_excel(r"data/final/{}.xlsx".format(keyword),index=False)

if __name__ == '__main__':
    # process("浦发手机银行")
    # process("平安银行")
    # process("招商银行")
    process("中国建设银行")

其中io.move_file的作用是把检查完的数据丢到另一个文件夹下,其对应代码为

def move_file(file, folder_tgt, suffix=0):
    if os.path.isdir(folder_tgt) is False:
        os.mkdir(folder_tgt)

    # if os.path.isfile(file):
    file_name = os.path.split(file)[1]

    file_type = file_name.split(".")[-1]

    new_name = os.path.join(folder_tgt, file_name)
    while os.path.isfile(new_name):
        suffix += 1
        new_name = os.path.join(
            folder_tgt,
            "{fn}({sfx}).{ft}".format(
                fn=file_name[:-(len(file_type) + 1)],
                sfx=suffix,
                ft=file_type
            )
        )

    shutil.copy(file, new_name)
    os.remove(file)

score的作用是打分(根据像素点识别)其代码如下

from PIL import Image

def score(path,fist_x,first_y,delta_x,r,g,b,d):
    level=0
    image = Image.open(path)
    for i in range(5):
        x=fist_x+i*delta_x
        y=first_y
        r_i,g_i,b_i,d_i=image.getpixel((x, y))
        if r_i!=r or g_i!=g or b_i!=b or d_i!=d:
            level=level+1
        else:
            break
    return level

path为文件路径,fist_x和first_y分别为第一颗星星中间点的坐标(偏一点没事),delta_x为每颗星星中间点的距离,rgbd分别为暗掉的星星中间点对应的rgbd值(不考虑颜色渐变的情况)

最终结果生成如下

在这里插入图片描述

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
回答: 要使用Appium抓取评论,可以通过定位元素的方式来获取评论内容。根据引用\[2\]中的描述,可以通过获取微信朋友圈页面中所有ID为com.tencent.mm:id/emw的元素,来得到每一条微信朋友圈的内容。这些元素对应的是FrameLayout,每个FrameLayout就是一个朋友圈的Item。通过遍历这些元素,可以获取到每一条朋友圈的评论内容。另外,根据引用\[1\]中的描述,可以通过获取ID为com.tencent.mm:id/d13的TextView控件,来获取朋友圈正文的评论内容。所以,通过定位这些元素,就可以抓取到微信朋友圈的评论内容。 #### 引用[.reference_title] - *1* *2* [Python爬虫编程思想(133):项目实战--利用Appium抓取微信朋友圈信息](https://blog.csdn.net/nokiaguy/article/details/123218604)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Appium实现爬取oppo应用商店评论](https://blog.csdn.net/qq_36532060/article/details/117283638)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值