You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Sources/pyOpenRPA/Robot/UIWeb.py

517 lines
23 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from selenium import *
from selenium import webdriver, common
from selenium.webdriver.common.by import By
import os
import sys
from pyOpenRPA.Tools import CrossOS
import time
# XPATH CSS CHEAT CHEET: https://devhints.io/xpath
# XPATH CSS CHEAT CHEET: https://devhints.io/css
UIO_WAIT_SEC_FLOAT = 60
UIO_WAIT_INTERVAL_SEC_FLOAT = 1.0
gBrowser:webdriver.Chrome = None
def BrowserChromeStart(inDriverExePathStr:str = None, inChromeExePathStr:str = None, inExtensionPathList:list = None, inProfilePathStr:str=None) -> webdriver.Chrome:
"""_summary_
:param inDriverExePathStr: _description_, defaults to None
:type inDriverExePathStr: str, optional
:param inChromeExePathStr: _description_, defaults to None
:type inChromeExePathStr: str, optional
:param inExtensionPathList: _description_, defaults to None
:type inExtensionPathList: list, optional
:param inProfilePathStr: _description_, defaults to None
:type inProfilePathStr: str, optional
:return: _description_
:rtype: webdriver.Chrome
"""
global gBrowser
inDriverExePathStr = CrossOS.PathStr(inPathStr=inDriverExePathStr)
inChromeExePathStr = CrossOS.PathStr(inPathStr=inChromeExePathStr)
lExtensionPathList = []
if inExtensionPathList is not None:
for lItemStr in inExtensionPathList:
lExtensionPathList.append(CrossOS.PathStr(inPathStr=lItemStr))
inExtensionPathList = lExtensionPathList
inChromeExePathStr = CrossOS.PathStr(inPathStr=inChromeExePathStr)
inProfilePathStr = CrossOS.PathStr(inPathStr=inProfilePathStr)
lResourcePathStr = os.path.abspath(os.path.join(sys.executable, "..","..", ".."))
# Путь по умолчанию к портативному браузеру и драйверу (если скачивался репозиторий pyOpenRPA
if inDriverExePathStr == None:
if CrossOS.IS_WINDOWS_BOOL: inDriverExePathStr = os.path.join(lResourcePathStr, "SeleniumWebDrivers", "Chrome", "chromedriver_win32 v84.0.4147.30", "chromedriver.exe")
elif CrossOS.IS_LINUX_BOOL: inDriverExePathStr = os.path.join(lResourcePathStr, "SeleniumWebDrivers", "Chrome", "chromedriver_lin64 v103.0.5060.53", "chromedriver")
if inChromeExePathStr == None:
if CrossOS.IS_WINDOWS_BOOL: inChromeExePathStr = os.path.join(lResourcePathStr, "WChrome64-840414730", "App", "Chrome-bin", "chrome.exe")
elif CrossOS.IS_LINUX_BOOL: inChromeExePathStr = os.path.join(lResourcePathStr, "LChrome64-10305060114", "data", "chrome")
if inExtensionPathList == None: inExtensionPathList = []
# Set full path to exe of the chrome
lWebDriverChromeOptionsInstance = webdriver.ChromeOptions()
lWebDriverChromeOptionsInstance.binary_location = inChromeExePathStr
#lWebDriverChromeOptionsInstance2 = webdriver.ChromeOptions()
if inProfilePathStr is not None:
inProfilePathStr = os.path.abspath(inProfilePathStr)
lWebDriverChromeOptionsInstance.add_argument(f"user-data-dir={os.path.abspath(inProfilePathStr)}")
# Add extensions
for lExtensionItemFullPath in inExtensionPathList:
lWebDriverChromeOptionsInstance.add_extension (os.path.abspath(lExtensionItemFullPath))
#if inDriverExePathStr == "built-in":
# Run with specified web driver path
gBrowser = webdriver.Chrome(executable_path = inDriverExePathStr, options=lWebDriverChromeOptionsInstance)
#else:
# lWebDriverInstance = webdriver.Chrome(options = lWebDriverChromeOptionsInstance)
return gBrowser
def BrowserChange(inBrowser):
"""_summary_
:param inBrowser: _description_
:type inBrowser: _type_
"""
global gBrowser
gBrowser = inBrowser
def PageOpen(inURLStr):
"""_summary_
:param inURLStr: _description_
:type inURLStr: _type_
"""
global gBrowser
if gBrowser is not None: gBrowser.get(inURLStr)
def PageScrollTo(inVerticalPxInt=0, inHorizontalPxInt=0):
"""_summary_
:param inVerticalPxInt: _description_, defaults to 0
:type inVerticalPxInt: int, optional
:param inHorizontalPxInt: _description_, defaults to 0
:type inHorizontalPxInt: int, optional
"""
PageJSExecute(inJSStr=f"scroll({inHorizontalPxInt},{inVerticalPxInt})")
def PageJSExecute(inJSStr, *inArgList):
"""_summary_
:param inJSStr: _description_
:type inJSStr: _type_
:return: _description_
:rtype: _type_
"""
# arguments[0], arguments[1] etc
global gBrowser
if gBrowser is not None: return gBrowser.execute_script(inJSStr, *inArgList)
def BrowserClose():
"""_summary_
"""
global gBrowser
if gBrowser is not None: gBrowser.quit()
def UIOSelectorList(inUIOSelectorStr, inUIO=None) -> list:
"""_summary_
:param inUIOSelectorStr: _description_
:type inUIOSelectorStr: _type_
:param inUIO: _description_, defaults to None
:type inUIO: _type_, optional
:return: _description_
:rtype: list
"""
lResultList = []
if inUIO is None:
global gBrowser
if gBrowser is not None:
if UIOSelectorDetect(inUIOSelectorStr=inUIOSelectorStr) == "CSS":
lResultList = gBrowser.find_elements(By.CSS_SELECTOR, inUIOSelectorStr)
else:
lResultList = gBrowser.find_elements(By.XPATH,inUIOSelectorStr)
else:
if UIOSelectorDetect(inUIOSelectorStr=inUIOSelectorStr) == "CSS":
lResultList = inUIO.find_elements(By.CSS_SELECTOR, inUIOSelectorStr)
else:
lResultList = inUIO.find_elements(By.XPATH,inUIOSelectorStr)
return lResultList
def UIOSelectorFirst(inUIOSelectorStr, inUIO=None) -> list:
"""_summary_
:param inUIOSelectorStr: _description_
:type inUIOSelectorStr: _type_
:param inUIO: _description_, defaults to None
:type inUIO: _type_, optional
:return: _description_
:rtype: list
"""
lResult = None
lUIOList = UIOSelectorList(inUIOSelectorStr=inUIOSelectorStr, inUIO=inUIO)
if len(lUIOList) > 0: lResult = lUIOList[0]
return lResult
def UIOTextGet(inUIO) -> str:
"""_summary_
:param inUIO: _description_
:type inUIO: _type_
:return: _description_
:rtype: str
"""
return inUIO.text
def UIOAttributeGet(inUIO, inAttributeStr) -> str:
"""_summary_
:param inUIO: _description_
:type inUIO: _type_
:param inAttributeStr: _description_
:type inAttributeStr: _type_
:return: _description_
:rtype: str
"""
return inUIO.get_attribute(inAttributeStr)
def UIOAttributeStyleGet(inUIO, inAttributeStr) -> str:
"""_summary_
:param inUIO: _description_
:type inUIO: _type_
:param inAttributeStr: _description_
:type inAttributeStr: _type_
:return: _description_
:rtype: str
"""
return inUIO.value_of_css_property(inAttributeStr)
def UIOAttributeSet(inUIO, inAttributeStr, inValue):
"""L+,W+: Установить обычный (нестилевой) атрибут у UI элемента.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeSet(inUIO=lUIO, inAttributeStr = "href", inValue = "https://mail.ru")
UIWeb.BrowserClose()
:param inUIO: UIO элемент. Получить его можно с помощью функций UIOSelectorList или UIOSelectorFirst
:type inUIO: WebElement
:param inAttributeStr: Наименование обычного (нестилевого) атрибута
:type inAttributeStr: str
:param inValue: Устанавливаемое значение обычного (нестилевого) атрибута
:type inValue: str
"""
lJSStr = \
f"arguments[0].setAttribute(arguments[1], arguments[2]);"
gBrowser.execute_script(lJSStr,inUIO, inAttributeStr, inValue)
def UIOAttributeRemove(inUIO, inAttributeStr):
"""L+,W+: Удалить обычный (нестилевой) атрибут у UI элемента.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeRemove(lUIO, "href")
UIWeb.BrowserClose()
:param inUIO: UIO элемент. Получить его можно с помощью функций UIOSelectorList или UIOSelectorFirst
:type inUIO: WebElement
:param inAttributeStr: Наименование обычного (нестилевого) атрибута
:type inAttributeStr: str
"""
lJSStr = \
f"arguments[0].removeAttribute(arguments[1]);"
gBrowser.execute_script(lJSStr,inUIO, inAttributeStr)
def UIOAttributeStyleSet(inUIO, inAttributeStr, inValue):
"""L+,W+: Установить стилевой атрибут у UI элемента.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleSet(inUIO=lUIO, inAttributeStr = "color", inValue = "grey")
UIWeb.BrowserClose()
:param inUIO: UIO элемент. Получить его можно с помощью функций UIOSelectorList или UIOSelectorFirst
:type inUIO: WebElement
:param inAttributeStr: Наименование стилевого атрибута
:type inAttributeStr: str
:param inValue: Устанавливаемое значение стилевого атрибута
:type inValue: str
"""
lJSStr = \
f"arguments[0].style[arguments[1]]=arguments[2];"
gBrowser.execute_script(lJSStr,inUIO, inAttributeStr, inValue)
def UIOAttributeStyleRemove(inUIO, inAttributeStr:str):
"""L+,W+: Удалить стилевой атрибут у UI элемента.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleRemove(lUIO, "color")
UIWeb.BrowserClose()
:param inUIO: UIO элемент. Получить его можно с помощью функций UIOSelectorList или UIOSelectorFirst
:type inUIO: WebElement
:param inAttributeStr: Наименование стилевого атрибута
:type inAttributeStr: str
"""
lJSStr = \
f"arguments[0].style[arguments[1]]=\"\";"
gBrowser.execute_script(lJSStr,inUIO, inAttributeStr)
def UIOClick(inUIO):
"""L+,W+: Выполнить нажатие по элементу inUIO.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIOClick(inUIO = lUIO)
UIWeb.BrowserClose()
:param inUIO: UIO элемент. Получить его можно с помощью функций UIOSelectorList или UIOSelectorFirst
:type inUIO: WebElement
"""
inUIO.click()
def UIOSelectorHighlight(inUIOSelectorStr: str, inIsFirst:bool=False, inDurationSecFloat:float=3.0, inColorStr:str="green"):
"""L+,W+: Выполнить подсвечивание UI элемента с селектором inUIOSelectorStr.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIOSelectorHighlight(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
:param inIsFirst: True - подсветить только первый элемент, который удовлетворяет селектору. По умолчанию False
:type inIsFirst: bool, опционально
:param inDurationSecFloat: Длительность подсвечивания. По умолчанию 3.0 сек.
:type inDurationSecFloat: float, опционально
:param inColorStr: Цвет подсвечания Варианты: "red", "blue", "grey", "yellow". По умолчанию "green" (зеленый)
:type inColorStr: str, опционально
"""
global gBrowser
if inIsFirst == True:
lUIOList = [UIOSelectorFirst(inUIOSelectorStr=inUIOSelectorStr)]
lJSStr = \
f"var lElementList = arguments[0];" \
f"if (lElementList.length>0) {{ lElementList=[lElementList[0]]; }}" \
f"for (var lIndexInt=0; lIndexInt<lElementList.length;lIndexInt++) {{" \
f" lElement=lElementList[lIndexInt];" \
f" lElement.ORPABackupStyleOutline = lElement.style[\"outline\"];" \
f" lElement.style[\"outline\"]=\"2px solid {inColorStr}\";" \
f"}}" \
f"window.ORPAOutlineList = lElementList;"
PageJSExecute(lJSStr, lUIOList)
else:
lUIOList = UIOSelectorList(inUIOSelectorStr=inUIOSelectorStr)
lJSStr = \
f"var lElementList = arguments[0];" \
f"for (var lIndexInt=0; lIndexInt<lElementList.length;lIndexInt++) {{" \
f" lElement=lElementList[lIndexInt];" \
f" lElement.ORPABackupStyleOutline = lElement.style[\"outline\"];" \
f" lElement.style[\"outline\"]=\"2px solid {inColorStr}\";" \
f"}}" \
f"window.ORPAOutlineList = lElementList;"
PageJSExecute(lJSStr, lUIOList)
time.sleep(inDurationSecFloat)
lJSStr = \
f"var lElementList = window.ORPAOutlineList;" \
f"for (var lIndexInt=0; lIndexInt<lElementList.length;lIndexInt++) {{" \
f" lElement=lElementList[lIndexInt];" \
f" lElement.style[\"outline\"]=lElement.ORPABackupStyleOutline;" \
f"}}" \
f"delete window.ORPAOutlineList;"
PageJSExecute(inJSStr=lJSStr)
def UIOSelectorClick(inUIOSelectorStr: str):
"""L+,W+: Выполнить нажатие по элементу с селектором inUIOSelectorStr.
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIOSelectorClick(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
"""
PageJSExecute(inJSStr=f"document.querySelector('{inUIOSelectorStr}').click()")
def UIOSelectorWaitAppear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT_SEC_FLOAT, inWaitIntervalSecFloat:float = UIO_WAIT_INTERVAL_SEC_FLOAT):
"""L+,W+: Ожидать появление UI элемента на веб странице (блокирует выполнение потока), заданного по UIO селектору inUIOSelectorStr. Выполнять ожидание на протяжении inWaitSecFloat (по умолчанию 60 сек.). Проверка производится с интервалом inWaitIntervalSecFloat (по умолчанию 1 сек.)
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lAppearUIOList = UIOSelectorWaitAppear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
:param inWaitSecFloat: Время ожидания на исчезновение UI элемента, по умолчанию UIO_WAIT_SEC_FLOAT (60 сек)
:type inWaitSecFloat: float, опциональный
:param inWaitIntervalSecFloat: Интервал проверки исчезновения, по умолчанию UIO_WAIT_INTERVAL_SEC_FLOAT (1 сек)
:type inWaitIntervalSecFloat: float, опциональный
:raises Exception: Время ожидания превышено
:return: Список UI элементов, которые удовлетворяют селектору и появились на странице
:rtype: list
"""
lStartSecFloat = time.time()
lResultList=[]
while time.time() - lStartSecFloat < inWaitSecFloat:
lResultList = UIOSelectorList(inUIOSelectorStr=inUIOSelectorStr)
if len(lResultList)>0: break
time.sleep(inWaitIntervalSecFloat)
if time.time() - lStartSecFloat > inWaitSecFloat: raise Exception(f"Wait time is over. No element has been appear")
return lResultList
def UIOSelectorWaitDisappear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT_SEC_FLOAT, inWaitIntervalSecFloat:float = UIO_WAIT_INTERVAL_SEC_FLOAT):
"""L+,W+: Ожидать исчезновение UI элемента с веб страницы (блокирует выполнение потока), заданного по UIO селектору inUIOSelectorStr. Выполнять ожидание на протяжении inWaitSecFloat (по умолчанию 60 сек.). Проверка производится с интервалом inWaitIntervalSecFloat (по умолчанию 1 сек.)
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIOSelectorWaitDisappear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
:param inWaitSecFloat: Время ожидания на исчезновение UI элемента, по умолчанию UIO_WAIT_SEC_FLOAT (60 сек)
:type inWaitSecFloat: float, опциональный
:param inWaitIntervalSecFloat: Интервал проверки исчезновения, по умолчанию UIO_WAIT_INTERVAL_SEC_FLOAT (1 сек)
:type inWaitIntervalSecFloat: float, опциональный
:raises Exception: Время ожидания превышено
"""
lStartSecFloat = time.time()
while time.time() - lStartSecFloat < inWaitSecFloat:
lResultList = UIOSelectorList(inUIOSelectorStr=inUIOSelectorStr)
if len(lResultList)==0: break
time.sleep(inWaitIntervalSecFloat)
if time.time() - lStartSecFloat > inWaitSecFloat: raise Exception(f"Wait time is over. No element has been disappear")
from lxml import etree
from io import StringIO
gXML = etree.parse(StringIO('<foo><bar></bar></foo>'))
def UIOSelectorDetect(inUIOSelectorStr:str) -> str:
"""L+,W+: Идентифицировать стиль селектора (CSS или XPATH)
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
lUIOSelectorStr = "#grid > div.grid-middle > div.grid__main-col.svelte-2y66pa > div.grid_newscol.grid_newscol__more-pulse.svelte-1yvqfic > div.grid__ccol.svelte-1yvqfic > ul > li:nth-child(5) > div > a"
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lResultStr = UIOSelectorDetect(inUIOSelectorStr = lUIOSelectorStr)
:param inUIOSelectorStr: XPATH или CSS селектор UI объекта на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
:return: "CSS" или "XPATH"
:rtype: str
"""
global gXML
lResultStr = "CSS"
try:
gXML.xpath(inUIOSelectorStr)
lResultStr = "XPATH"
except etree.XPathEvalError as e:
lResultStr = "CSS"
return lResultStr
def UIOMouseSearchInit():
"""L+,W+: Инициализирует процесс поиска UI элемента с помощью мыши. Для прекращения поиска необходимо использовать функцию: UIOMouseSearchReturn
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
import time
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
UIWeb.UIOMouseSearchInit()
time.sleep(3)
UIWeb.UIOMouseSearchReturn()
UIWeb.BrowserClose()
"""
lJSStr = """
document.ORPASearch = function(e){
document.ORPAMouseXInt = e.clientX;
document.ORPAMouseYInt = e.clientY;
}
document.addEventListener('mousemove', document.ORPASearch, {
passive: true})
"""
PageJSExecute(lJSStr)
def UIOMouseSearchReturn():
"""L+,W+: Возвращает UIO объект, над которым находится указатель мыши. Предварительно должна быть вызвана функция UIWeb.UIOMouseSearchInit
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
import time
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
UIWeb.UIOMouseSearchInit()
time.sleep(3)
UIWeb.UIOMouseSearchReturn()
UIWeb.BrowserClose()
:return: UIO объект
:rtype: webelement
"""
lJSStr = """
document.removeEventListener('mousemove', document.ORPASearch);
return document.elementFromPoint(document.ORPAMouseXInt,document.ORPAMouseYInt);
"""
return PageJSExecute(lJSStr)