uiautomator2实现自动化商品发布实践

本文主要介绍了使用Python编写的一个脚本,通过Appium和Uiautomator2库,控制Android设备执行一系列操作,包括设备连接、屏幕截图、应用启动、界面交互等,用于自动化测试或抓取商品详情页面的截图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import os
import re
import time
from appium import webdriver
import uiautomator2 as u2
import logging
import subprocess
from datetime import datetime
import queue
import threading
import random
now = datetime.now()
# logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(funcName)s - %(message)s')
# logger = logging.getLogger(__name__)
# logger.info(d)
log_path = r'./log'
img_path = r'./img_screen'
titles_dir = './device_title'
os.makedirs(titles_dir,exist_ok=True)
os.makedirs(log_path,exist_ok=True)
os.makedirs(img_path,exist_ok=True)

def get_current_time():
    return str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())+' : ')
def append_file(d_sn,content):
    with open(os.path.join(log_path,f'{d_sn}.txt'),"a",encoding='utf-8') as f:
        f.write(f'{get_current_time()} {content}')
        f.write('\n')
def append_title_file(file,content):
    with open(file,"a",encoding='utf-8') as f:
        f.write(content)
        f.write('\n')
def get_devices():
    s_d = ''
    devices_info = os.popen('adb devices').readlines()
    for i in range(len(devices_info)):
        s_d+=devices_info[i]
    device_sn = re.findall('\n(.+?)\t',s_d,re.S)
    return device_sn
devices = get_devices()
def clean_screen_shots(driver,flag=True):
    screen_files = run_adb(f'adb -s {driver.serial} shell ls /sdcard/DCIM/Screenshots').split('\r\n')
    print('screen_files:',screen_files)
    for f in screen_files: 
        if not len(f) > 0 :continue
        run_adb(f'adb -s {driver.serial} shell rm -f /sdcard/DCIM/Screenshots/{f}')
        # run_adb(f'adb -s {device} shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Screenshots/*.png')
        time.sleep(1)
        run_adb(f'adb -s {driver.serial} shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Screenshots/{f}')
        time.sleep(1)
    if flag:
        driver.app_stop('com.smile.gifmaker') 
# 判断屏幕是否锁屏,如果屏幕锁定就解锁
def get_screen_status(device):
    status = device.info['screenOn'] 
    if not status:
        device.screen_on()
        device.swipe_ext('up',scale=0.9)
    return status
def run_adb(cmd):
    process = subprocess.Popen(cmd.split(),stdout=subprocess.PIPE)
    output ,error = process.communicate()
    print(output,error)
    return output.decode('utf-8')
def click_(driver,element='',text_='',resourceId_='', content='',error_path='',pkg='com.smile.gifmaker',sleepTime=3):
    try:
        if str(element).startswith("com"):  # 若开头是com则使用ID定位
            driver(resourceId=element).click()  # 点击定位元素
        elif re.findall("//", str(element)):  # 若//开头则使用正则表达式匹配后用xpath定位
            driver.xpath(element).click()  # 点击定位元素
        elif text_:  # 若以上两种情况都不是,则使用描述定位
            driver(text=text_).click()  # 点击定位元素
        elif resourceId_:
            driver(resourceId=resourceId_).click()
        else:
           print('***********无操作************') 
        if content:
           append_file(driver.serial,content)
        time.sleep(sleepTime)
        driver.implicitly_wait(15)
    except Exception as e:
        append_file(driver.serial,'异常中断本次商品发布' + content)
        if error_path:
            driver.screenshot(f'{error_path}/error_{content}.png')
        time.sleep(sleepTime)
        clean_screen_shots(driver,flag=True)
        driver.app_stop(pkg) 
# 点击详情图片,然后根据图片张数进行截图,并保存至/DCIM
def img_info_screen_shot(driver,s_title,current_index):
    # 获取大图上面的图片index
    img_info = driver(textContains='/').get_text().split('/')
    left_step = int(img_info[1]) - int(img_info[0])
    print(img_info,left_step)
    # 每次截图前先讲Screenshots下的图片清空
    if int(img_info[1]) ==1:
        pass
    else:
        for j in range(int(img_info[0])):#将图片滑到第一张
            driver.swipe_ext('right',scale=0.9)
    time.sleep(2)
    current_time = time.strftime('%Y_%m_%d_%H_%M_%S', time.localtime())
    img_path_tmp =f'{img_path}/{driver.serial}/{current_time}_{current_index}'
    title_file_tmp = f'{img_path}/{driver.serial}/{current_time}_{current_index}/{driver.serial}_title_{current_index}.txt'
    
    os.makedirs(img_path_tmp,exist_ok=True)
    append_title_file(title_file_tmp,s_title)
    img_counts = driver(textContains='/').get_text().split('/')[-1] # 获取商品详情总共图片张数
    append_file(driver.serial,f'Step 10 : 当前商品张数:{img_counts}')
    for i in range(int(img_info[1])):     
        tt = time.time()
        timestamp = int(round(tt * 1000))
        time.sleep(1)
        current_element = driver(textContains='/').get_text().split('/')
        print('timestamp',current_element)
        driver.screenshot(f'{img_path_tmp}/{timestamp}_{i+1}.png')
        # append_file(device,f'{get_current_time()}当前商品截图title:{current_element}')
        time.sleep(1)
        append_file(driver.serial,f'Step 11 : 当前商品截图:{timestamp}_{i+1}.png')
        time.sleep(1)
        run_adb(f'adb -s {driver.serial} push {img_path_tmp}/{timestamp}_{i+1}.png /sdcard/DCIM/Screenshots')
        time.sleep(1)
        driver.swipe_ext('left',scale=0.9)
        time.sleep(2)
        # 截图后发送广播更新相册
        run_adb(f'adb -s {driver.serial} shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file:///sdcard/DCIM/Screenshots/{timestamp}_{i+1}.png')
    # d.xpath('//android.app.Dialog/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[4]').click()
    driver.press("back")
    # 刷新商品列表至最顶部
    # driver.xpath('//*[@text="在售 "]').click()
    click_(driver,element='//*[@text="在售 "]',content='Step 12 : 双击在售',error_path=img_path_tmp)
    click_(driver,element='//*[@text="在售 "]',content='Step 12 : 双击在售',error_path=img_path_tmp)
    # append_file(device,f'双击在售',12)
    time.sleep(8)
    driver.click(0.889, 0.377)
    time.sleep(2)
    # driver(text='拍短视频').click()
    if not driver(text='拍短视频').exists():
        driver.click(0.163, 0.056)#点空白处,隐藏弹框
        time.sleep(1)
        driver.click(0.856, 0.532)# 点击第二个item
        time.sleep(1)
        driver.click(0.494, 0.63)# 点击拍短视频按钮
        append_file(driver.serial,'Step 13-1 : 拍短视频')
        # click_(driver,text='拍短视频',content='Step 13-1 : 拍短视频')
    else:
        click_(driver,text_='拍短视频',content='Step 13 : 拍短视频',error_path=img_path_tmp)
    # append_file(device,f'拍短视频',13)
    time.sleep(2)
    # driver(text='模板').click()
    click_(driver,text_='模板',content='Step 14 : 选择模板',error_path=img_path_tmp)
    # append_file(device,f'选择模板',14)
    time.sleep(2)
    # 收藏的模板
    # driver(text='收藏').click()
    click_(driver,text_='收藏',content='Step 15 : 选择收藏模板',error_path=img_path_tmp)
    # append_file(device,f'选择收藏模板',15)
    # d.xpath('//*[@resource-id="com.smile.gifmaker:id/ks_root_view"]/android.widget.HorizontalScrollView[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]').click(0)
    time.sleep(1)
    r = random.randint(1, 10)
    # 随机选择模板
    for s_r in range(r):
        driver.swipe_ext('up',scale=0.5)
    # driver(text='使用').click()
    click_(driver,text_='使用',content=f'Step 16 : 选择收藏的模板,drag {r} 次',error_path=img_path_tmp)
    # append_file(device,f'选择收藏的模板,drag {r} 次',16)
    # driver.xpath(f'//*[@resource-id="com.smile.gifmaker:id/recycler_view"]/android.widget.RelativeLayout[{r}]/android.widget.RelativeLayout[1]').click()# 第一个收藏的模板
    # d.swipe_ext('down',scale=0.9)
    time.sleep(3)
    driver.implicitly_wait(15)
    # append_file(device,'Step 17 : 开始制作 - 开始选择图片')
    # driver(text='开始制作').click()  
    click_(driver,text_='开始制作',content='Step 17 : 开始制作 - 开始选择图片',error_path=img_path_tmp)
    print('需要截图张数:',img_counts,s_title)
    # 根据截图数量选择图片
    # //*[@resource-id="com.smile.gifmaker:id/album_view_list"]/android.widget.FrameLayout[6]
    for c_i in  range(int(img_counts)):
        # driver.xpath(f'//*[@resource-id="com.smile.gifmaker:id/album_view_list"]/android.widget.FrameLayout[{c_i+1}]').click()
        click_(driver,element=f'//*[@resource-id="com.smile.gifmaker:id/album_view_list"]/android.widget.FrameLayout[{c_i+1}]',content=f'Step 18 : 选择第 {c_i+1} 张图片',error_path=img_path_tmp)
        # append_file(device,f'Step 18 : 选择第 {c_i+1} 张图片')
        time.sleep(1)
    # 选好图片后确认
    time.sleep(2)
    # append_file(device,'选好了',19)
    # driver(resourceId="com.smile.gifmaker:id/right_container").click()# 选好了
    click_(driver,resourceId_="com.smile.gifmaker:id/right_container",content='Step 18 : 选好了',sleepTime=15,error_path=img_path_tmp)
    # time.sleep(10)
    if not driver(text="下一步").exists():
        append_file(driver.serial,'Step 19 : 下一步 控件没有找到,请检查资源是否违规')
        return 
    # append_file(device,'Step 18 : 下一步',20)
    # driver(text='下一步').click()
    click_(driver,text_='下一步',content='Step 20 : 下一步',error_path=img_path_tmp)
    time.sleep(1)
    driver.xpath('//*[@resource-id="com.smile.gifmaker:id/editor_container"]').set_text(f'{s_title}')
    time.sleep(5)
    driver.press("back")
    time.sleep(2)
    # append_file(device,'Step 21 : 发布作品')
    # driver.xpath('//*[@resource-id="com.smile.gifmaker:id/publish_button"]').click()
    click_(driver,element='//*[@resource-id="com.smile.gifmaker:id/publish_button"]',content='发布作品',sleepTime=5,error_path=img_path_tmp)
    sn_title_file = f'{titles_dir}/{driver.serial}_title.txt'
    append_title_file(sn_title_file,s_title)
queue = queue.Queue()
lock = threading.Lock()
# 将每个设备添加至线程池
def add_device_2_queue():
    for d_sn in devices:
        queue.put(d_sn)
# 判断该商品是否发布过
def get_device_title(d_sn):
    sn_title_file = f'{titles_dir}/{d_sn}_title.txt'
    if not os.path.exists(sn_title_file):
        return []
    with open(sn_title_file, 'r', encoding='utf-8') as f:
        data = [d.replace('\n','') for d in f.readlines()]
        print(data)
        return data
def exc_consum(q):
    while not q.empty():
        current_device = q.get()
        # try:
        for c_i in range(50):
            print(current_device,c_i)
            d = u2.connect(current_device)
            s = d.info['screenOn']
            clean_screen_shots(d,flag=False)
            append_file(current_device,f'Step 0 : 当前连接设备:{current_device} {c_i}')
            print('当前屏幕状态:',s)
            append_file(current_device,f'Step 1 : 当前屏幕状态:{s}')
            if not s:
                d.screen_on()
                d.swipe_ext('up',scale=0.9)
            d.app_start('com.smile.gifmaker')
            append_file(current_device,f'Step 2 : 启动快手应用')
            # d.implicitly_wait(15)
            time.sleep(10) 
            try:
                d(resourceId="android:id/text1", text="我").click()
                append_file(current_device,f'Step 3 : 进入我的')

            except Exception as e:
                print('e1',e)
                append_file(current_device,f'Step 3-1 : Exception:点击我\n{e}')
                # d(description="我").click()
                
                continue

            try:
                d(text='我').click()
                d(text='我').click()
                time.sleep(3)
                zuopin = d.xpath('//*[@resource-id="com.smile.gifmaker:id/tab_text"]').all()
                # print(type(zuopin))
                zuopins = [x.text for x in zuopin]
                append_file(current_device,f'Step 4 : 作品详情:{zuopins}')
                print('zuopin:',zuopins)
                d.implicitly_wait(10)
                # 点击进入快手小店
                # d(text='快手小店').click()
                click_(d,element='//*[@resource-id="com.smile.gifmaker:id/im_group_list"]/android.view.ViewGroup[1]')
            except Exception as e2:
                append_file(current_device,f'Step 5-1 : Exception : 进入快手小店')
                print('exception2:',e2)
                continue
            append_file(current_device,f'Step 5 : 进入快手小店')
            time.sleep(5)
            # 点击货架
            try:
                # d.xpath('//*[@resource-id="com.smile.gifmaker:id/root_view"]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[3]').click()
                click_(d,element='//*[@resource-id="com.smile.gifmaker:id/root_view"]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[3]',content='Step 6 : 进入货架')
                # append_file(current_device,f'Step 6 : 进入货架')
            except Exception as e3:
                print('exception3:',e3)
                append_file(current_device,f'Step 6-1 : Exception:点击货架\n{e3}')
                continue
            time.sleep(5) 

            # 获取商品布局宽高尺寸
            info = d.xpath('//*[@resource-id="com.smile.gifmaker:id/root_view"]/android.widget.LinearLayout[1]/android.widget.LinearLayout[1]').info
            wedith,high = info['bounds']['right'],info['bounds']['bottom']
            
            # 商品列表向上滑动一个item,点击第一个item进入
            for hj in range(c_i + 2):
                d.swipe_ext('up',scale=0.5)
            append_file(current_device,f'Step 7 : 进入列表第一个item')
            d.click(0.074, 0.281) 
            time.sleep(5) 
            # 获取商品详情title
            # title = d(className="android.widget.TextView").get_text()
            for elem in d.xpath('//*[@resource-id="root"]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[3]/android.view.View[1]').all():
                children = elem.elem.getchildren()
                if children:
                    title = children[0].get('text')
                    # print(children[0].get('text'),type(children))
            print('商品标题:',title)
            titles_tmp = get_device_title(current_device)
            if not title:continue
            if title in titles_tmp:
                append_file(current_device,f'Step 8-1 : 该商品已经发布过:{title}')
                clean_screen_shots(d,flag=True)
                continue
            append_file(current_device,f'Step 8 : 获取商品标题:{title}')
            
            time.sleep(1) 
            # 点击商品大图
            info = d.xpath('//*[@resource-id="root"]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[2]').click()
            append_file(current_device,f'Step 9 : 获取商品大图:{title}')
            time.sleep(1)
            # 获取当前图片显示的张数,截图
            img_info_screen_shot(d,title,c_i)
            time.sleep(5)
            # d.app_stop('com.smile.gifmaker') 
            lock.acquire()
            lock.release()
            clean_screen_shots(d,flag=True)
        # except Exception as ee:
        #     print('Exception:',ee)
        #     append_file(current_device,'异常状态,请检查设备连接状态')
        #     clean_screen_shots(d,flag=True)
add_device_2_queue()
thrds = []
for _ in range(len(devices)):
    t = threading.Thread(target=exc_consum,args=(queue,))
    t.start()
    thrds.append(t)
for t in thrds:
    t.join()

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值