【爬虫】Airtest + 逍遥模拟器 开发 APP爬虫

Airtest


Airtest 简介:

AirtestIDE 是一个跨平台的UI自动化测试编辑器,适用于游戏和App。

自动化脚本录制、一键回放、报告查看,轻而易举实现自动化测试流程;

支持基于图像识别的 Airtest 框架,适用于所有Android和Windows游戏;

支持基于UI控件搜索的 Poco 框架,适用于Unity3d,Cocos2d与Android App;

能够运行在Windows和MacOS上;

网易内部已成功应用在数十个项目上,利用 手机集群 进行大规模自动化测试。

类似工具:uiautomator2 appium airtest

Airtest 相关链接

项目地址:http://airtest.netease.com/

快速上手教程:http://airtest.netease.com/tutorial/Tutorial.html

IDE快速上手教程:http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/1_quick_start.html

Popc 插件教程:https://www.jianshu.com/p/6bf26d1192b4

爬虫实战


环境

airtest 版本:1.2.6

python 的 airtest 库:1.1.7 (已支持 python3.8 和 python3.9)

pocoui:1.0.80

使用步骤

1、下载并安装模拟器(以逍遥模拟器举例)

逍遥模拟器官网:https://www.xyaz.cn/

2、模拟器开启开发者模式

小技巧,把模拟器的分辨率设置的宽度大一些,一页可以显示更多条目,对于下拉爬取能减少很多下拉次数哦!

比如 长1000 宽2000;

设置 -> 关于手机(关于平板电脑) -> 连续点击7次版本号 -> 打开开发者模式

设置 -> 开发者选项 -> 开启 -> USB 调试开启

3、下载并安装 Airtest

Airtest 官网: http://airtest.netease.com/

下载完解压即可

4、使用

打开 AirtestIDE.exe ,右侧 点击 远程设备连接,把端口号改为 21503(逍遥模拟器对应的是 21503),点击连接,然后在右上方看到 移动设备的状态,如果是 device,则直接点击connect,此时 模拟器的屏幕会投到 Airtest 界面右侧;

touch:点击左上侧区域的 touch,然后在右侧的手机屏幕里拖动选中一个APP,代码区域会显示 touch(“app的图片”);

点击 Airtest 上方的 运行(三角)可以看到,被选中的 APP 会自动打开

5、在 airtest 中 使用 本地 python环境

打开 AirtestIDE.exe,点击工具栏的选项 -> 设置 ,在自定义 Python.exe 路径的地方选择本地 Python 路径,选择后,需要在本地安装 airtest 和 pocoui 两个库。

pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com airtest

pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com pocoui

pip install -U numpy==1.19.3

6、利用 poco 编程

poco 辅助窗 在 Airtest 的左下侧,先将stop模式改成安卓模式,可以看到一个层级的树,这个树表示了当前屏幕的布局信息,同时在python代码编辑区也会插入一段代码:

# -*- encoding=utf8 -*-
__author__ = "Ezrealer"

from airtest.core.api import *

auto_setup(__file__)

# 自动插入的代码
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)

点击 poco 辅助窗的锁头,然后点击 右侧 手机屏幕区域的一个APP,此时APP不会打开,但是 poco辅助窗 显示了 这个 APP 所在的屏幕层级,同时中间下方的 Log 查看窗 可以看到这个app节点的信息:

Path from root node: [0, 0, 0, 0, 1, 0, 0, 0, 5]
Payload details:
	  type :  android.widget.TextView 
	  name :  android.widget.TextView 
	  text :  得物() 
	  enabled :  True 
	  visible :  True 
	  checkable :  False 
	  pos :  [0.1388888888888889, 0.509375] 
	  scrollable :  False 
	  boundsInParent :  [0.18055555555555555, 0.19296875] 
	  selected :  False 
	  anchorPoint :  [0.5, 0.5] 
	  size :  [0.18055555555555555, 0.19296875] 
	  zOrders :  {'global': 0, 'local': 6} 
	  editalbe :  False 
	  checked :  False 
	  focused :  False 
	  touchable :  True 
	  package :  b'com.microvirt.launcher2' 
	  scale :  [1, 1] 
	  dismissable :  False 
	  longClickable :  True 
	  focusable :  False 

可以看到 text 是 得物(毒) ,于是我们可以这样编程来实现打开这个app的目的:

# -*- encoding=utf8 -*-
__author__ = "Ezrealer"

from airtest.core.api import *

auto_setup(__file__)


from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
poco(text="得物(毒)").click()

poco(text=“得物(毒)”) 也可以点击左下的 poco 辅助窗的对应元素来自动生成。

7、一个例子

# -*- encoding=utf8 -*-
__author__ = "Ezrealer"

from airtest.core.api import *
from airtest.cli.parser import cli_setup
import emoji
import time
import pymysql
import re
import json
from urllib import parse

# if not cli_setup():
#     auto_setup(__file__, logdir=False, devices=[
#         "android://127.0.0.1:5037/127.0.0.1:21503?cap_method=JAVACAP&&ori_method=ADBORI&&touch_method=MINITOUCH",
#         #"android://127.0.0.1:5037/1f1293ea0407?cap_method=JAVACAP&&ori_method=ADBORI&&touch_method=MINITOUCH",
#     ])

# script content
print("start...")

# generate html report
# from airtest.report.report import simple_report
# simple_report(__file__, logpath=True)


from poco.drivers.android.uiautomation import AndroidUiautomationPoco


def get_mysql_conn():
    while 1:
        try:
            conn = pymysql.connect(host='120.133.xx.xxx', port=3306, user='root', passwd='123',
                                   db='ezrealer', charset='utf8mb4')
            cur = conn.cursor()
            break
        except Exception as e:
            print(e)
            time.sleep(3)

    return cur, conn


def execute_sql(cur, conn, table, item):
    keys = ', '.join(item.keys())
    values = ', '.join(['%s'] * len(item))
    sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)

    while 1:
        try:
            cur.execute(sql, tuple(item.values()))
            conn.commit()
            print("插入成功...")
            break
        except Exception as e:
            print(e)
            cur, conn = get_mysql_conn()
            time.sleep(3)

            
cur,conn = get_mysql_conn()



exits_titles = []
sql = "select title from du_tags"
cur.execute(sql)
results = cur.fetchall()
for i in results:
    exits_titles.append(i[0])

print("已存在:",len(exits_titles))

# keyevent("BACK")
# time.sleep(1)
# keyevent("BACK")
# time.sleep(1)
# keyevent("BACK")
# time.sleep(1)
# keyevent("BACK")

poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
# time.sleep(3)
# poco(text="得物(毒)").click()

# print("连接成功...")
# time.sleep(10)

# poco("com.shizhuang.duapp:id/rbtn_trend").click()
# time.sleep(2)

#while 1:
authors = []
page_cards_module = poco("android.widget.LinearLayout").offspring("com.shizhuang.duapp:id/trendContainer").offspring("com.shizhuang.duapp:id/viewPager").offspring("android.widget.LinearLayout").children()

suc_num = 0

for page_card_module in page_cards_module:
    page_card = page_card_module.get_text()
    print(page_card)
    if page_card != "全部":
    
        page_card_module.click()
        time.sleep(1)
        
        while 1:
    #         articles = poco("android.widget.LinearLayout").offspring("com.shizhuang.duapp:id/trendContainer").offspring("com.shizhuang.duapp:id/recyclerView").children()
            articles = poco("com.shizhuang.duapp:id/clGridRoot")
            print(len(articles))
            titles = []

            for article in articles:
                if not article.child("com.shizhuang.duapp:id/tvTitle").exists():
                    continue
                article_title = article.child("com.shizhuang.duapp:id/tvTitle")
                author = article.child("com.shizhuang.duapp:id/tvUsername").get_text()
                title = article_title.get_text()
                print("标题:",title)
                    
                if title in titles or title in exits_titles:
                    print("已存在...")
                    continue
    #                 print("下拉加载更多airtile...")
    #                 poco.swipe([0.5, 0.8], [0.5, 0.3])
    #                 time.sleep(2)
    #                 continue
                titles.append(title)
                article_title.click()
                time.sleep(1)
                
                item = {}
                
                if poco("com.shizhuang.duapp:id/tags_container").child("android.widget.LinearLayout").offspring("android.view.ViewGroup").exists():
                    tags = []
                    tags_modules = poco("com.shizhuang.duapp:id/tags_container").child("android.widget.LinearLayout").offspring("android.view.ViewGroup")

                    for tags_module in tags_modules:
                        tag = []
#                         tag_modules = tags_module.child("android.widget.LinearLayout").offspring("android.view.ViewGroup").children()
                        tag_modules = tags_module.children()

                        for tag_module in tag_modules: 
                            print(tag_module.attr("type"))
                            
                            if str(tag_module.attr("type")).strip() == "android.widget.TextView":
                                tag_cnt = tag_module.get_text()
                                tag_cnt = tag_cnt.encode("utf8","ignore").decode('unicode_escape')
                                #tag_cnt = tag_cnt.replace("\\x", "%")
                                tag_cnt = parse.unquote(tag_cnt)
                                tag.append(tag_cnt)
                        tag_str = "|".join(tag)
                        tags.append(tag_str)

                    print("标签:",tags)
                    item['tags'] = json.dumps(tags)
                    
                
                poco.swipe([0.5, 0.8], [0.5, 0.3])
                time.sleep(1)
                

                item['title'] = title
                item['author'] = author
                item['page_card'] = page_card
                if poco("com.shizhuang.duapp:id/tv_circle").exists():
                    circle = poco("com.shizhuang.duapp:id/tv_circle").get_text()
                    print("圈子:",circle)
                    joined_circle = poco("com.shizhuang.duapp:id/tv_circleno").get_text()
                    item['circle'] = circle
                    item['joined_circle'] = joined_circle.replace("人已加入","")

                if poco("com.shizhuang.duapp:id/tv_label").exists():
                    topic_module = poco("com.shizhuang.duapp:id/tv_label")
                    topic = topic_module.get_text()
                    time.sleep(1)
                    topic_module.click()
                    time.sleep(2)
                    if poco("com.shizhuang.duapp:id/numContent").exists():
                        topic_cnt_num = poco("com.shizhuang.duapp:id/numContent").get_text()
                        item['topic_cnt_num'] = topic_cnt_num.replace("条内容","")
                    print("话题:",topic)
                    item['topic'] = topic
                    
                    keyevent("BACK")
                    time.sleep(1)

                
                suc_num += 1
                print(suc_num,item)
                
                table = "du_tags"
                execute_sql(cur,conn,table,item)
                    
                    
                keyevent("BACK")
                print("回到主页面...")
                time.sleep(2)
            print("下拉加载更多airtile...")
            poco.swipe([0.5, 0.8], [0.5, 0.3])
            time.sleep(2)

在 pycharm 中使用

https://mp.weixin.qq.com/s/-gGplycWKAsJ6Os3XQFARA

常见问题


Airtest 中 device 是 offline 状态

win + R
powershell
# 查看占用5037的pid
netstat -aon|findstr "5037" 
# 查看占用该pid的任务
tasklist|findstr "4452"
# 终止任务
taskkill /f /t /im vmware-hostd.exe

报错

卸载 手机上的 PocoService 和 Yosemite,启动 airtest的 程序后会重新安装。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值