Перед использованием этого плагина рекомендуется ознакомиться с основами XPath. В интернете можно найти множество полезных ресурсов. Вот некоторые из них:
Код не был полностью протестирован и может содержать ошибки. Любые замечания и отзывы приветствуются.
dump_hierarchy
библиотеки uiautomator2, получаем текущий UI-интерфейс (богатый XML).lxml
парсим XML и находим соответствующие xpath, после чего выполняем команду click
.В настоящее время обнаружено, что lxml поддерживает только XPath 1.0. Если кто-то знает, как поддержать XPath 2.0, пожалуйста, дайте знать.
Принцип работы мониторинга уведомлений
Используя hierarchy, можно получить информацию обо всех элементах интерфейса (включая уведомления и кнопки для нажатия).
Предположим, что есть кнопки "Пропустить" и "Знаю". Кнопка, которую нужно нажать, называется "Плеер".1. Получаем XML текущего интерфейса (через функцию dump_hierarchy
).
2. Проверяем наличие кнопок "Пропустить" и "Знаю". Если они есть, нажимаем их и возвращаемся к шагу 1.
3. Проверяем наличие кнопки "Плеер". Если она есть, нажимаем её и завершаем. Если кнопка не найдена, возвращаемся к шагу 1 и продолжаем до тех пор, пока не превысит количество попыток.## Метод установки
pip3 install -U uiautomator2
В настоящее время этот плагин уже встроен в uiautomator2, поэтому не требуется регистрация плагина.
Вот простой пример, чтобы показать, как это работает:
import uiautomator2 as u2
def main():
d = u2.connect()
d.app_start("com.netease.cloudmusic", stop=True)
d.xpath('//*[@text="Персональный FM"]').click()
#
# Продвинутый пример (локация элементов)
#
# @-начальные
d.xpath('@personal-fm') # эквивалентно d.xpath('//*[@resource-id="personal-fm"]')
# Локация элементов с несколькими условиями, аналогично AND
d.xpath('//android.widget.Button').xpath('//*[@text="Персональный FM"]')
d.xpath('//*[@text="Персональный FM"]').parent() # локация родительского элемента
d.xpath('//*[@text="Персональный FM"]').parent("@android:list") # локация родительского элемента, соответствующего условиям
Ниже приведен код без
import
иmain
, предполагается, что переменнаяd
уже существует.
XPathSelector
sl = d.xpath("@com.example:id/home_searchedit") # sl — это объект XPathSelector
# Нажатие
sl.click()
sl.click(timeout=10) # Указание времени ожидания
sl.click_exists() # Нажатие, если элемент существует, возвращает, был ли нажат элемент
sl.click_exists(timeout=10) # Ожидание до 10 секунд
sl.match() # Возвращает None, если не соответствует, иначе возвращает XMLElement
# Ожидание появления соответствующего элемента, возвращает XMLElement
# По умолчанию время ожидания составляет 10 секунд
el = sl.wait()
el = sl.wait(timeout=15) # Ожидание 15 секунд, если элемент не найден, возвращает None
```# Ожидание исчезновения элемента
sl.wait_gone()
sl.wait_gone(timeout=15)
# Аналогично wait, но если элемент не найден, выбрасывает исключение XPathElementNotFoundError
el = sl.get()
el = sl.get(timeout=15)
# Изменение времени ожидания по умолчанию на 15 секунд
d.xpath.global_set("timeout", 15)
d.xpath.implicitly_wait(15) # Эквивалент предыдущей строке
print(sl.exists) # Возвращает, существует ли элемент (bool)
sl.get_last_match() # Возвращает последний найденный XMLElement
sl.get_text() # Получение текста элемента
sl.set_text("") # Очистка текстового поля
sl.set_text("hello world") # Ввод текста "hello world" в текстовое поле
# Перебор всех соответствующих элементов
for el in d.xpath('//android.widget.EditText').all():
print("rect:", el.rect) # Выводит кортеж: (x, y, width, height)
print("center:", el.center())
el.click() # Операция нажатия
print(el.elem) # Выводит узел, распарсенный lxml
print(el.text)
# Не протестированные методы
# Нажатие на элемент, находящийся в координатах (50%, 50%) внутри элемента
d.xpath("//*").position(0.5, 0.5).click()
XMLElement
# Объект, возвращаемый XPathSelector.get(), называется XMLElement
el = d.xpath("@com.example:id/home_searchedit").get()
lx, ly, width, height = el.rect # Получение координат верхнего левого угла и размеров
lx, ly, rx, ry = el.bounds # Координаты верхнего левого и нижнего правого углов
x, y = el.center() # Получение координат центра элемента
x, y = el.offset(0.5, 0.5) # Аналогично center()
# Отправка нажатия
el.click()
# Вывод текстового содержимого
print(el.text) # Получение атрибутов элемента, возвращает словарь
print(el.attrib)
# Скриншот элемента (работает путем сначала получения полного скриншота, а затем вырезания)
el.screenshot()
# Перемещение элемента
el.swipe("right") # left, right, up, down
el.swipe("right", scale=0.9) # scale по умолчанию 0.9, что означает, что расстояние перемещения составляет 90% ширины элемента, а для перемещения вверх — 90% высоты
Функция
scroll_to
является нововведением и может быть не полностью завершена (например, не может определить, достигло ли перемещение нижней границы экрана). Сначала посмотрим на пример
from uiautomator2 import connect_usb, Direction
d = connect_usb()
d.scroll_to("Заказать")
d.scroll_to("Заказать", Direction.FORWARD) # По умолчанию это скролл вниз, но можно также использовать BACKWARD, HORIZ_FORWARD (горизонтально), HORIZ_BACKWARD (горизонтально в обратную сторону)
d.scroll_to("Заказать", Direction.HORIZ_FORWARD, max_swipes=5)
# Также можно скроллить внутри определенного элемента
d.xpath('@com.taobao.taobao:id/dx_root').scroll(Direction.HORIZ_FORWARD)
d.xpath('@com.taobao.taobao:id/dx_root').scroll_to("Заказать", Direction.HORIZ_FORWARD)
Полный пример
import uiautomator2 as u2
from uiautomator2 import Direction
def main():
d = u2.connect()
d.app_start("com.netease.cloudmusic", stop=True)
# шаги
d.xpath("//*[@text='Личный FM']/../android.widget.ImageView").click()
d.xpath("Следующий трек").click()
``` # Мониторим появление окна 2 секунды, время может быть больше 2 секунд
d.xpath.sleep_watch(2)
d.xpath("Перейти на уровень выше").click()
d.xpath("Перейти на уровень выше").click(watch=False) # клик без активации мониторинга
d.xpath("Перейти на уровень выше").click(timeout=5.0) # ждать 5 секунд d.xpath.watch_background() # включить мониторинг в фоновом режиме, по умолчанию проверка каждые 4 секунды
d.xpath.watch_background(interval=2.0) # проверка каждые 2 секунды
d.xpath.watch_stop() # остановить мониторинг
for el in d.xpath('//android.widget.EditText').all():
print("rect:", el.rect) # выводит кортеж: (левая_x, верхняя_y, ширина, высота)
print("bounds:", el.bounds) # выводит кортеж: (левая, верхняя, правая, нижняя)
print("center:", el.center())
el.click() # операция клика
print(el.elem) # выводит узел lxml
# скролл
el = d.xpath('@com.taobao.taobao:id/fl_banner_container').get()
# с правой стороны на левую
el.swipe(Direction.HORIZ_FORWARD)
el.swipe(Direction.LEFT) # с правой стороны на левую
# с нижней стороны на верхнюю
el.swipe(Direction.FORWARD)
el.swipe(Direction.UP)
el.swipe("право", scale=0.9) # масштаб по умолчанию 0.9, расстояние скролла составляет 80% ширины элемента, центр скролла совпадает с центром элемента
el.swipe("вверх", scale=0.5) # расстояние скролла составляет 50% высоты элемента
# scroll отличается от swipe тем, что возвращает булево значение, указывающее, появился ли новый элемент
el.scroll(Direction.FORWARD) # вниз
el.scroll(Direction.BACKWARD) # вверх
el.scroll(Direction.HORIZ_FORWARD) # горизонтально вперед
el.scroll(Direction.HORIZ_BACKWARD) # горизонтально назад
если el.scroll("forward"):
print("Можно продолжить прокрутку")
## Правила XPath
Чтобы быстрее писать скрипты, мы определили несколько упрощённых правил для XPath.**Правило 1**
`//` в начале означает стандартный XPath.
**Правило 2**
`@` в начале означает локализацию по `resourceId`.
`@smartisanos:id/right_container` эквивалентно
`//*[@resource-id="smartisanos:id/right_container"]`
**Правило 3**
`^` в начале означает регулярное выражение.
`^.*道了` эквивалентно `//*[re:match(text(), '^.*道了')]`
**Правило 4**
> Вдохновлен SQL like
`知道%` соответствует тексту, начинающемуся с `知道`, эквивалентно `//*[starts-with(text(), '知道')]`
`%知道` соответствует тексту, заканчивающемуся на `知道`, эквивалентно `//*[ends-with(text(), '知道')]`
`%知道%` соответствует тексту, содержащему `知道`, эквивалентно `//*[contains(text(), '知道')]`
**~~Правило 5~~ (эта функция удалена)**
> Дополнительно от Selenium PageObjects
`$知道` соответствует тексту, определенному через `d.xpath.global_set("alias", dict)` словарём, если не существует, будет использоваться `知道` для соответствия
**Последнее правило**
соответствует тексту и описанию элемента
например `搜索` эквивалентно XPath `//*[@text="搜索" or @content-desc="搜索" or @resource-id="搜索"]`
## Особые примечания
- Иногда `className` содержит символ `$`, который является недопустимым в XML, поэтому все такие символы заменены на `-`
## Некоторые продвинутые методы использования XPath
//*
//*[contains(@resource-id, 'login')]
//android.widget.Button[contains(@text, '账号') or contains(@text, '帐号')]
(//android.widget.ImageView)[2]
(//android.widget.ImageView)[last()]
# Элемент содержит имя "ImageView"
//*[contains(name(), "ImageView")]
## Некоторые полезные сайты
- [XPath playground](https://scrapinghub.github.io/xpath-playground/)
- [Некоторые продвинутые методы использования XPath-Жаншу](https://www.jianshu.com/p/4fef4142b33f)
- [XPath Quicksheet](https://devhints.io/xpath)
Если у вас есть другие материалы, пожалуйста, добавьте их через [Issues](https://github.com/openatx/uiautomator2/issues/new)
## Устаревшие функции
**Определение псевдонимов** больше не поддерживается с версии `1.3.4`
Этот подход похож на [PageObjects](https://selenium-python.readthedocs.io/page-objects.html) в Selenium
```python
# Здесь используется Python 3. Для Python 2 строковые литералы нужно определить как u"Меню", обратите внимание на u перед строкой
d.xpath.global_set("alias", {
"Меню": "@com.netease.cloudmusic:id/qh", # TODO(ssx): возможно, мы сможем поддерживать P("@com.netease.cloudmusic:id/qh", wait_timeout=2) в будущем
"Настройки": "//android.widget.TextView[@text='Настройки']",
})
# Здесь нужно $开头
d.xpath("$меню").click() # эквивалентно d.xpath()
d.xpath("$настройки").click()
d.xpath("$меню").click()
# эквивалентно d.xpath("@com.netease.cloudmusic:id/qh").click()
d.xpath("$小吃").click() # здесь будет выброшено исключение XPathError, так как нет такого alias как小吃
# параметр alias_strict
d.xpath.global_set("alias_strict", False) # по умолчанию True
d.xpath("$小吃").click() # здесь будет выполнено корректно
# эквивалентно
d.xpath('//*[@text="小吃" or @content-desc="小吃"]').click()
по умолчанию logging.INFOметод изменения
import logging
d.xpath.logger.setLevel(logging.DEBUG)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )