您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

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

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

  • 环境配置
  • 具体实现
    • 连接到你想要爬取的APP
    • 模拟人操作并拿取部分字段
      • 点击搜索框并输入搜索内容
      • 点击到详情页
      • 点击评论
      • 开始循环拿评论
    • 解析并合并结果

环境配置

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

具体实现

连接到你想要爬取的APP

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

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

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

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值(不考虑颜色渐变的情况)

最终结果生成如下

在这里插入图片描述


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进