I try this code: Human-like mouse movements via Selenium but trying to figure out how to integrate it in a real life scraper to follow with my mouse with different DOM elements:
#!/usr/bin/python
# https://stackoverflow.com/questions/39422453/human-like-mouse-movements-via-selenium
import os
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import numpy as np
import scipy.interpolate as si
#curve base
points = [[-6, 2], [-3, -2],[0, 0], [0, 2], [2, 3], [4, 0], [6, 3], [8, 5], [8, 8], [6, 8], [5, 9], [7, 2]];
points = np.array(points)
x = points[:,0]
y = points[:,1]
t = range(len(points))
ipl_t = np.linspace(0.0, len(points) - 1, 100)
x_tup = si.splrep(t, x, k=3)
y_tup = si.splrep(t, y, k=3)
x_list = list(x_tup)
xl = x.tolist()
x_list[1] = xl + [0.0, 0.0, 0.0, 0.0]
y_list = list(y_tup)
yl = y.tolist()
y_list[1] = yl + [0.0, 0.0, 0.0, 0.0]
x_i = si.splev(ipl_t, x_list)
y_i = si.splev(ipl_t, y_list)
url = "https://codepen.io/falldowngoboone/pen/PwzPYv"
driver = webdriver.Chrome()
driver.get(url)
action = ActionChains(driver);
startElement = driver.find_element(By.ID, 'drawer')
# First, go to your start point or Element:
action.move_to_element(startElement);
action.perform();
# https://stackoverflow.com/a/70796266/465183
for mouse_x, mouse_y in zip(x_i, y_i):
# Here you should reset the ActionChain and the 'jump' wont happen:
action = ActionChains(driver)
action.move_by_offset(mouse_x,mouse_y);
action.perform();
print(mouse_x, mouse_y)
Is there a Python module like NodeJS/pptr Ghost Cursor to facilitate integration?
Or anybody here can show us a way to integrate it in a real life scraper?
Created a feature request: https://github.com/SeleniumHQ/selenium/issues/11824
Using pyautogui+Selenium ChromeDriver
I think there's a better way, using Bezier curves as I do here and Selenium ActionsChains
like your github links suggest, overriding class to do something like driver.move_to_element()
and driver.random_mouse()
, but this is working well for simple requirements:
git clone https://github.com/sputnick-dev/pyautogui-with-selenium.git
cd pyautogui-with-selenium
./run
Code to avoid link only answer:
paw file:
#!/usr/bin/env python3
import random
import bezier
import pyautogui
import numpy as np
from time import sleep
from random import uniform as randfloat
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium import webdriver
def slow_type(element, text: str):
"""Send a text to an element one character at a time with a delay."""
for character in text:
element.send_keys(character)
sleep(randfloat(0.05,0.3))
def resting_mouse():
"""move mouse to right of screen."""
panelWidht = driver.execute_script('return window.outerWidth;')
start = pyautogui.position()
end = random.randint(panelWidht-100, panelWidht), random.randint(400,850)
x2 = (start[0] + end[0])/2 #midpoint x
y2 = (start[1] + end[1]) / 2 ##midpoint y
control1X = (start[0] + x2)/2
control2X = (end[0] + x2) / 2
# Two intermediate control points that may be adjusted to modify the curve.
control1 = control1X, y2 ##combine midpoints to create perfect curve
control2 = control2X, y2 ## using y2 for both to get a more linear curve
# Format points to use with bezier
control_points = np.array([start, control1, control2, end])
points = np.array([control_points[:, 0], control_points[:, 1]]) # Split x and y coordinates
# You can set the degree of the curve here, should be less than # of control points
degree = 3
# Create the bezier curve
curve = bezier.Curve(points, degree)
curve_steps = 70 # How many points the curve should be split into. Each is a separate pyautogui.moveTo() execution
delay = 0.008 # Time between movements. 1/curve_steps = 1 second for entire curve
# Move the mouse
for j in range(1, curve_steps + 1):
# The evaluate method takes a float from [0.0, 1.0] and returns the coordinates at that point in the curve
# Another way of thinking about it is that i/steps gets the coordinates at (100*i/steps) percent into the curve
x, y = curve.evaluate(j / curve_steps)
pyautogui.moveTo(x, y) # Move to point in curve
pyautogui.sleep(delay) # Wait delay
sleep(2)
def bezier_mouse(location, size, panelHeight): ##move mouse to middle of element
x, relY = location["x"], location["y"] ##abs X and relative Y
absY = relY + panelHeight
w, h = size["width"], size["height"]
wCenter = w/2
hCenter = h/2
xCenter = int(wCenter + x)
yCenter = int(hCenter + absY)
start = pyautogui.position()
end = xCenter, yCenter
x2 = (start[0] + end[0]) / 2 #midpoint x
y2 = (start[1] + end[1]) / 2 ##midpoint y
control1X = (start[0] + x2) / 2
control1Y = (end[1] + y2) / 2
control2X = (end[0] + x2) / 2
control2Y = (start[1] + y2) / 2
# Two intermediate control points that may be adjusted to modify the curve.
control1 = control1X, y2 ##combine midpoints to create perfect curve
control2 = control2X, y2
# Format points to use with bezier
control_points = np.array([start, control1, control2, end])
points = np.array([control_points[:, 0], control_points[:, 1]]) # Split x and y coordinates
# You can set the degree of the curve here, should be less than # of control points
degree = 3
# Create the bezier curve
curve = bezier.Curve(points, degree)
curve_steps = 70 # How many points the curve should be split into. Each is a separate pyautogui.moveTo() execution
delay = 0.008 # Time between movements. 1/curve_steps = 1 second for entire curve
# Move the mouse
for j in range(1, curve_steps + 1):
# The evaluate method takes a float from [0.0, 1.0] and returns the coordinates at that point in the curve
# Another way of thinking about it is that i/steps gets the coordinates at (100*i/steps) percent into the curve
x, y = curve.evaluate(j / curve_steps)
pyautogui.moveTo(x, y) # Move to point in curve
pyautogui.sleep(delay) # Wait delay
# Disable pyautogui pauses
pyautogui.MINIMUM_DURATION = 0
pyautogui.MINIMUM_SLEEP = 0
pyautogui.PAUSE = 0
# avoid Exception when mouse if in a top corner
pyautogui.FAILSAFE = False
# instanciate Chrome websriver
driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(0.5)
try:
driver.get("http://localhost:8000")
except TimeoutException:
driver.execute_script("window.stop();")
sleep(2)
e1 = driver.find_element(By.ID, "change-me-paw")
e2 = driver.find_element(By.ID, "textarea")
panelHeight = driver.execute_script('return window.outerHeight - window.innerHeight;')
location1 = e1.location ## coords of element1
location2 = e2.location ## coords of element2
size1 = e1.size ## size of element1
size2 = e2.size ## size of element2
sleep(3)
bezier_mouse(location1, size1, panelHeight)
e1.click()
sleep(2)
bezier_mouse(location2, size2, panelHeight)
slow_type(e2, 'I type like a human being...')
sleep(2)
resting_mouse()
sleep(2)
driver.close()
The rest of artefacts are in repository.
run wrapper script:
#!/bin/bash
trap 'kill $pid $$' 1 2 3 15
[[ ! -e .req ]] && pip3 install -r requirements.txt && touch .req
cd tests/
python3 -m http.server &
pid=$!
cd ..
./paw
kill $pid &>/dev/null