Python + OpenCV + Selenium: проблема при последовательных действиях "найти и кликнуть"

Всем привет.
Была задача написать небольшой тест для проверки работоспособности canvas элемента на странице. Как-бы смоук тест на то, не отвалилась ли БД или еще какой из элементов. Для мониторинга. Решил использовать Python, Selenium Webdriver, OpenCV. Нашел статью, где популярно разжевано: HTML <canvas> testing with Selenium and OpenCV

Все сделал, настроил. Вроде работает. Но столкнулся с проблемой и не знаю как решить.
Проблема: если после нахождения элемента на странице и клика по нему доабвить поиск другого элемента и клика уже по нему, то второй клик не отрабатывает (метод вроде отрабатывает, то глазами вижу что действия, которое должно быть после клика - нет).

Модуль поисковика элемента:
graphical_locator.py

import cv2 import numpy from io import BytesIO from PIL import Image class GraphicalLocator(object): def __init__(self, img_path): self.locator = img_path # x, y position in pixels counting from left, top corner self.x = None self.y = None self.img = cv2.imread(img_path) self.width = self.img.shape[1] self.height = self.img.shape[0] self.threshold = None @property def center_x(self): if self.x and self.width: return self.x + int(self.width / 2) else: None @property def center_y(self): if self.y and self.height: return self.y + int(self.height / 2) else: None # Clear last found coordinates def find_me(self, drv): self.x = self.y = None # Get current screenshot of a web page scr = drv.get_screenshot_as_png() # Convert img to BytesIO scr = Image.open(BytesIO(scr)) # Convert to format accepted by OpenCV scr = numpy.asarray( scr, dtype=numpy.float32 ).astype(numpy.uint8) # Convert image from BGR to RGB format scr = cv2.cvtColor(scr, cv2.COLOR_BGR2RGB) # Image matching works only on gray images # (color conversion from RGB/BGR to GRAY scale) img_match = cv2.minMaxLoc( cv2.matchTemplate( cv2.cvtColor( scr, cv2.COLOR_RGB2GRAY ), cv2.cvtColor( self.img, cv2.COLOR_BGR2GRAY ), cv2.TM_CCOEFF_NORMED ) ) # Calculate position of found element self.x = img_match[3][0] self.y = img_match[3][1] # From full screenshot crop part that matches template image scr_crop = scr[ self.y: (self.y + self.height), self.x: (self.x + self.width) ] # Calculate colors histogram of both template# and matching images and # compare them scr_hist = cv2.calcHist( [scr_crop], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256] ) img_hist = cv2.calcHist( [self.img], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256] ) comp_hist = cv2.compareHist( img_hist, scr_hist, cv2.HISTCMP_CORREL ) # Save treshold matches of: graphical image and image histogram self.threshold = { 'shape': round(img_match[1], 2), 'histogram': round(comp_hist, 2) } # Return image with blue rectangle around match return cv2.rectangle( scr, (self.x, self.y), (self.x + self.width, self.y + self.height), (0, 0, 255), 2 ) 

Модуль проверки:
check.py

import time import os from selenium.webdriver.common.action_chains import ActionChains from .graphical_locator import GraphicalLocator def check(driver): def click(path, shape, histogram): button = GraphicalLocator( os.path.abspath(path)) button.find_me(driver) is_found = False print("\nSHAPE: ", path, button.threshold['shape']) print("\nHISTOGRAM: ", path, button.threshold['histogram']) if button.threshold['shape'] >= shape and \ button.threshold['histogram'] >= histogram: is_found = True if is_found: action = ActionChains(driver) action.move_by_offset(button.center_x, button.center_y) action.click() action.perform() print("\nALREADY CLICKED") time.sleep(2) return True return False if click( path="checks/elements/button_1.png", shape=0.56, histogram=0.97, ): return click( path="checks/elements/button_2.png", shape=0.56, histogram=0.53, ) 

В итоге, первый клик проходит, это видно, а второй по коду проходит (click() возвращает True), но по факту нет. Кто с таким сталкивался? Куда копать?
Думал проблема в не очистке координат, но нет. Я же использую новый инстанс ActionChains. Как и GraphicalLocator.

python==3.5.2
selenium==3.14.0
chromedriver==2.45
opencv-python==3.4.4.19
numpy==1.15.4
Pillow==5.3.0

chrome==71.0.3578.98

В общем, если кому вдруг интересно, я решил эту проблему. Дело в том, что метод move_by_offset() отсчитывает задаваемые координаты не с (0,0), а с последнего клика. Похоже это даже на уровне webdriver’а, так как я создаю для каждого клика отдельный инстанс.
Вот решение:

import time import os from selenium.webdriver.common.action_chains import ActionChains from .graphical_locator import GraphicalLocator def click( driver, path, shape, histogram, prev_center_x=0, prev_center_y=0, ): button = GraphicalLocator( os.path.abspath(path)) button.find_me(driver) is_found = False print("\nSHAPE: ", path, button.threshold['shape']) print("\nHISTOGRAM: ", path, button.threshold['histogram']) if button.threshold['shape'] >= shape and \ button.threshold['histogram'] >= histogram: is_found = True if is_found: action = ActionChains(driver) # print("\nCENTER X: ", button.center_x) # print("\nCENTER Y: ", button.center_y) action.move_by_offset( button.center_x - prev_center_x, button.center_y - prev_center_y, ) action.click() action.perform() print("\nALREADY CLICKED") time.sleep(2) return True, button.center_x, button.center_y return False, 0, 0 clicked = click( driver=driver, path="checks/elements/button_1.png", shape=0.56, histogram=0.97, ) if clicked[0]: return click( driver=driver, path="checks/elements/button_2.png", prev_center_x=clicked[1], prev_center_y=clicked[2], shape=0.56, histogram=0.53, )[0] 
1 лайк

It is enough to add the action.reset_actions() command before the action.move_by_offset command.

 action.reset_actions() time.sleep(1) action.move_by_offset( button.center_x , button.center_y , ) 
1 лайк