在我们实际做app自动化的过程中经常会出现需要翻页才能找到元素的情况
appium中已有一个用于翻页找到元素的方法,不过经过实际使用并不好用,如果元素里初始滚动位置较远则会报找不到元素
driver.scroll(origin_el, destination_el, duration=None)
"""
originalEl (`appium.webdriver.webelement.WebElement`): the element from which to being scrolling
destinationEl (`appium.webdriver.webelement.WebElement`): the element to scroll to
duration (int): a duration after pressing originalEl and move the element to destinationEl.
Default is 600 ms for W3C spec. Zero for MJSONWP.
"""
image.png
scroll方法的源码
if self.w3c and duration is None:
duration = 600
action = TouchAction(self)
if duration is None:
action.press(origin_el).move_to(destination_el).release().perform()
else:
action.press(origin_el).wait(duration).move_to(destination_el).release().perform()
return self
源码该方法的实现是使用TouchAction类中的手势操作完成,以原元素到目标元素的方式
又因为目标元素,有时未必已经加载完成,所以会经常出现报元素找不到的情况
针对这个情况,我们使用滚动固定长度,而不是元素到元素的方法进行滚动。
该方法仅滚动查询目标元素,与具体目标元素操作完全解耦
from appium import webdriver
from appium.webdriver.webdriver import WebDriver
from appium.webdriver.common.mobileby import MobileBy as MB
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
class BaseOperation: # 封装的基本方法类
def __init__(self, driver: WebDriver):
self.driver = driver
def up_and_down_screen(self, wait_loc: tuple, target_loc: tuple, keyword, model="down"):
"""operation app page up or down screen,
:param wait_loc: Locators of global wait elements -> tuple # 用于全体等待的定位,例如:列表中通用的class,ID等用于判断滑动时页面是否加载完成 (MB.CLASS, 'android.widget.TextView')
:param target_loc: Locators of target element -> tuple # 目标元素的定位,例如:(MB.ANDROID_UIAUTOMATOR, 'new UiSelector().text("我是目标值")')
:param keyword: Keyword of target element -> str # 用于找到目标定位的值,注意是目标source中特有的值
:param model: screen direction (up or down) # 模式,有up和down两种
:return: None
"""
wait = WebDriverWait(self.driver, 30) # 设定显性等待
wait.until(EC.visibility_of_all_elements_located(wait_loc)) # 等待页面加载完毕
old_source = None
new_source = self.driver.page_source # 获取当前页面的source
while old_source != new_source and keyword not in new_source: # 当新旧页面的source未相同时说明还未滑动到底部且未找到目标关键字时会持续滑动, 如果是滚动到页面底部则新旧source相同则不再滚动
size = self.driver.get_window_size() # 获取当前页面的分辨率
if model == "down": # 页面向下滚动
self.driver.swipe(start_x=size["width"] * 0.5, start_y=size["height"] * 0.8,
end_x=size["width"] * 0.5, end_y=size["height"] * 0.3)# 滑动距离要根据不同的app进行定义,避免出现滑动超过一页否则会出现找不到元素的可能
elif model == "up": # 页面向上滚动
self.driver.swipe(start_x=size["width"] * 0.5, start_y=size["height"] * 0.3,
end_x=size["width"] * 0.5, end_y=size["height"] * 0.8)# 滑动距离要根据不同的app进行定义,避免出现滑动超过一页否则会出现找不到元素的可能
else:
raise TypeError("Only up or down")
wait.until(EC.visibility_of_all_elements_located(wait_loc)) # 等待新滚动的页面加载完成
old_source = new_source # 将原先页面赋值成旧页面
new_source = self.driver.page_source # 重新获取新页面的source
print("翻页中………………………………")
try:
ele = self.driver.find_element(*target_loc)
except NoSuchElementException as e:
print("找不到目标")
raise e
else:
print("已找到目标")
return ele # 返回找到的目标元素对象,具体操作与查询解耦
使用:
start_loc = (MB.ID, 'fragment_category_type')
target_loc = (MB.ANDROID_UIAUTOMATOR, 'new UiSelector().text("我是目标值")')
BaseOperation(driver).up_and_down_screen(start_loc, target_loc, "我是关键字").click() # 对目标元素进行点击操作