无法使用Selenium(python)执行拖放

问题描述 投票:0回答:3

我正在努力在私人网络应用程序上使用 python 中的 Selenium 执行拖放或单击并按住操作。

我尝试在此处的公开示例中重现我的错误: http://the-internet.herokuapp.com/drag_and_drop

下面是我的拖放/单击并按住的基本代码

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('http://the-internet.herokuapp.com/drag_and_drop')
dragged = driver.find_element(By.ID, "column-a")
dropped = driver.find_element(By.ID, "column-b")

#Drag and drop

actions = ActionChains(driver)
actions.drag_and_drop(dragged, dropped).perform() 
#column A is selected but not dragged

#Click and hold
actions = ActionChains(driver)
actions.move_to_element(dragged).click_and_hold().move_to_element(dropped).release().perform()
#Same result : column a is selected but not dragged

通过 stackoverflow 搜索,我发现了一个使用 javascript 的公共示例的“解决方案”。 如何在Selenium Webdriver中模拟HTML5拖放?

import os
with open(os.path.abspath('C:/Users/Admin/PycharmProjects/AutoTestIntro/drag_and_drop_helper.js'), 'r') as js_file:
    line = js_file.readline()
    script = ''
    while line:
        script += line 
        line = js_file.readline()

driver.execute_script(script+"$('#column-a').simulateDragDrop({ dropTarget: '#column-b'});")

这可以直接在 python 中完美运行,但要求拖放的元素具有 id。我目前正在进行的私人项目并非如此。

我对 Selenium 做错了什么吗? JS 中是否有任何解决方法来指定 xpath 而不是 id ?

python selenium drag-and-drop
3个回答
2
投票

我终于找到答案了!!!

见下文:

https://gist.github.com/florentbr/60ef7cb8d9b1ae690cafc82aad52da73

我使用drag-drop.min.js中的函数

这是 python 中的一个简短示例:

JS_DRAG_AND_DROP = "function h(a,b,c,d){var k=l.createEvent('DragEvent');k.initMouseEvent(b,!0,!0,l.defaultView,0,0,0,m,n,w,x,y,!1,0,null);Object.setPrototypeOf(k,null);k.dataTransfer=g;Object.setPrototypeOf(k,DragEvent.prototype);a.dispatchEvent(k);setTimeout(d,c)}var a=arguments,c=a[0],d=a[1],q=a[2]||0,r=a[3]||0,t=a[4]||1;a=a[5]||'';var x='alt'===a||'\ue00a'===a,w='ctrl'===a||'\ue009'===a,y='shift'===a||'\ue008'===a,l=c.ownerDocument;a=c.getBoundingClientRect();var e=d?d.getBoundingClientRect():a,m=a.left+a.width/2,n=a.top+a.height/2,u=e.left+(q?q:e.width/2),v=e.top+(r?r:e.height/2),p=l.elementFromPoint(m,n),f=l.elementFromPoint(u,v);for(d=p;d&&!d.draggable;)d=d.parentElement;if(!d||!c.contains(p))throw c=Error('source element is not interactable/draggable'),c.code=15,c;if(!f)throw c=Error('target element is not interactable'),c.code=15,c;var g={constructor:DataTransfer,effectAllowed:null,dropEffect:null,types:[],files:Object.setPrototypeOf([],null),_items:Object.setPrototypeOf([],{add:function(a,b){this[this.length]={_data:''+_data,kind:'string',type:b,getAsFile:function(){},getAsString:function(a){a(this._data)}};g.types.push(b)},remove:function(a){Array.prototype.splice.call(this,a&65535,1);g.types.splice(a&65535,1)},clear:function(a,b){this.length=0;g.types.length=0}}),setData:function(a,b){this.clearData(a);this._items.add(b,a)},getData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);return 0<=b?this._items[b]._data:null},clearData:function(a){for(var b=this._items.length;b--&&this._items[b].type!==a;);this._items.remove(b)},setDragImage:function(a){}};'items'in DataTransfer.prototype&&(g.items=g._items);e=f.getBoundingClientRect();h(p,'dragstart',t,function(){var a=f.getBoundingClientRect();m=a.left+u-e.left;n=a.top+v-e.top;h(f,'dragenter',1,function(){h(f,'dragover',t,function(){f=l.elementFromPoint(m,n);h(f,'drop',1,function(){h(p,'dragend',1,function(){})})})})})"

def drag_and_drop(driver, source, target=None, offsetX=0, offsetY=0, delay=25, key=None) :
  driver.execute_script(JS_DRAG_AND_DROP, source, target, offsetX, offsetY, delay, key)
  time.sleep(delay * 2 / 1000)

driver = webdriver.Chrome()
driver.get("http://the-internet.herokuapp.com/drag_and_drop")


# drag and drop Glass
source = driver.find_element_by_xpath("//*[@id='column-a']")
target = driver.find_element_by_xpath("//*[@id='column-b']")
drag_and_drop(driver, source, target)

0
投票

我希望下面的代码可以解决您在 Java 中的问题

public class darganddropTest {

public static void main(String[] args) {

// TODO Auto-generated method stub

System.setProperty("webdriver.chrome.driver","./chromedriver_win32/chromedriver.exe");

WebDriver driver = new ChromeDriver();

driver.get("https://jqueryui.com/droppable/");

System.out.println(driver.findElements(By.tagName("iframe")).size());

driver.switchTo().frame(driver.findElement(By.className("demo-frame")));

driver.findElement(By.id("draggable")).click();

Actions a = new Actions(driver);

WebElement drag = driver.findElement(By.id("draggable"));

WebElement drop = driver.findElement(By.id("droppable"));

a.dragAndDrop(drag, drop).build().perform();

driver.switchTo().defaultContent();

}

}

0
投票

我围绕这个问题做了一些研究,到目前为止发现只有一种方法可以用纯Python解决它,这意味着没有JS脚本或类似的东西通过使用pyautogui。

from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import pyautogui

driver = webdriver.Chrome()
driver.get('https://the-internet.herokuapp.com/drag_and_drop')
# ## driver.maximize_window()   - can work in full screen mode, but it will look a bit off center when drag and
# dropping the items ###
driver.implicitly_wait(10)

# set the win size to keep the same aspect and not be related to any machine you run this on
# set size is measured in pixels
driver.set_window_size(1200, 600)

# use only to check if the win size was correctly set
win_size = driver.get_window_size()

source = driver.find_element(By.CSS_SELECTOR, "#column-a")
target = driver.find_element(By.CSS_SELECTOR, "#column-b")
# get the size of the element to be dragged not actually required but is a personal preference to make it more fancy
# and make sure the drag / drop action starts from the middle of the element
element_size = source.size

# get the location of elements in pixels
source_location = source.location
target_location = target.location

# required to get because the location of elements does not take in consideration the address bar, bookmark bar
# ie"the browser is controled by automated etc..." and the specific window size
inner_width = driver.execute_script("return window.innerWidth;")
inner_height = driver.execute_script("return window.innerHeight;")

# get the offset and middle of the element to be dragged and the target
offset_width = win_size['width'] - inner_width + element_size['width'] / 2
offset_height = win_size['height'] - inner_height + element_size['height'] / 2

# ### Print Statememts used for Debbug ###
# print(f"actual size is {win_size} ,ineer size is {inner_width}, {inner_height}")
# print(source_location, target_location)
# print(element_size, f"the offsets are {offset_width} and {offset_height}")

# use pyautoguy to perform the actions
# make the actual start location by adding element .location value with the offests that are calculated for Chrome
pyautogui.moveTo(source_location['x'] + offset_width, source_location['y'] + offset_height)
pyautogui.mouseDown()

# adjust duration for a faster or slower animation for drag and drop based on preference
pyautogui.moveTo(target_location['x'] + offset_width, target_location['y'] + offset_height, duration=1)
pyautogui.mouseUp()

# ###  Assertion to check in case you don't want to look and just run the test ###
# ###  Find the moved item, get the new location  ###
# ###  Assert that initial location and location after move are not the same => Drag and Drop action is a success  ###
source_moved = driver.find_element(By.XPATH, '//div[@class="column"]/header[text()="A"]')
source_newlocation = source_moved.location
assert source_location != source_newlocation

# add a sleep time to enjoy the fruits of the work
time.sleep(1)

driver.quit()

这是我的带有注释的代码 经过大量测试后,我发现只有一个问题可能会发生,如果您将显示缩放设置为大于或小于 100%,它可能会改变拖放的起点,除此之外,我没有发现此代码有任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.