Search code examples
pythonseleniumselenium-webdriverfirefoxwebdriver

Is it a bug: Python's Selenium ActionChains, strange delay between with the 'perform' function?


I am writing in Python with Selenium's WebDrivers a function to click a coordinate in a page in a human-like fashion. It uses Firefox, but I also notice the same problem with ChromeDriver.

It takes two arguments: an element (from which my function gets its coordinates) and target point. It will move the mouse from one point to another in a curve to imitate a real human. Then, it will click the target point.

Weirdly, I find that the 'action.perform' function gets slower the biggest the offset gets from the starting point.

Additionally, I found something else which may be a coincidence: the number of time which it gets stuck is about the value of x from which I offset from the element. So when I move (x:0,y:0), it is quick, but when I move with offset (x:14,y:2) in the loop, it waits about 14 seconds. It just might be a coincidence though.

Finally, I heard there was a *kwarg argument in java for the actionchains, which included a delay. I read that most people didn't use it which resulted in a penalty of a default ~250ms. But I couldn't find such a thing for python.

Do anybody have an idea what I could be doing wrong, or if it is a bug?

Thank you so much!

Here is a simplified example I wrote which reproduces this problem.

import selenium.webdriver, re
from selenium.common.exceptions import NoSuchElementException  
from selenium.webdriver import ActionChains  
import numpy as np
from time import sleep as delay

def hanging_line(point1, point2): #calculates the coordinates for none-straight line which gets from point1 to point2
    a = (point2[1] - point1[1])/(np.cosh(point2[0]) - np.cosh(point1[0]))
    b = point1[1] - a*np.cosh(point1[0])
    x = np.linspace(point1[0], point2[0], 100)
    y = a*np.cosh(x) + b
    return (x,y)


def click(element,coordinates):
    delay(3)
    actionmouse = ActionChains(driver)
    actionmouse.move_to_element(element);
    actionmouse.perform();

    targetx = coordinates[0]
    targety = coordinates[1]

    points = hanging_line((0,0),(targetx,targety))

    for i in range(len(points[0])):
        x = int(round(points[0][i],1)) #I tried to round it, and to convert it to an int,
        y = int(round(points[1][i],1)) #but found no differences in performance

        print('moving')
        actionmouse.move_by_offset(x, y)
        print('performing') #get stuck here for seconds=`x` value
        actionmouse.perform()
        print(x,y)


    action = ActionChains(driver)
    action.move_to_element_with_offset(element, targetx, targety).click().perform()
    delay(0.3)


driver = selenium.webdriver.Firefox()
driver.get("https://www.dailychess.com/chess/chess-fen-viewer.php")
element = driver.find_element_by_class_name('sidebar-action-button')
click(element,[37,94])


Solution

  • The default duration is 250ms.

    To override this, use the 'duration' parameter when initializing you action:

    action = ActionChains(driver, duration=50)