通过 Selenium 驱动程序执行 Javascript elementFromPoint

Posted

技术标签:

【中文标题】通过 Selenium 驱动程序执行 Javascript elementFromPoint【英文标题】:Executing Javascript elementFromPoint through Selenium driver 【发布时间】:2015-11-01 20:02:11 【问题描述】:

我正在尝试在我的基于 Selenium 的框架中实现一个“对象选择器”,这在大多数商业自动化工具中很常见。为此,我使用 javascript 命令在鼠标位置查找元素,但没有得到我期望的元素。

如果我使用的是 ChromeDriver 或 InternetExplorerDriver,脚本总是返回标头对象。无论我看什么网页或鼠标的位置。虽然听起来脚本使用坐标 0、0 而不是鼠标位置,但我已经确认 Cursor.Position 正在发送正确的值。

如果我使用的是 FirefoxDriver,我会遇到异常:

"Argument 1 of Document.elementFromPoint is not a finite floating-point value. (UnexpectedJavaScriptError)"

谁能看出我做错了什么?

    private void OnHovering()
    
        if (Control.ModifierKeys == System.Windows.Forms.Keys.Control)
        
            IWebElement ele = null;
            try
            
                // Find the element at the mouse position
                if (driver is IJavaScriptExecutor)
                    ele = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript(
                        "return document.elementFromPoint(arguments[0], arguments[1])", 
                        new int[]  Cursor.Position.X, Cursor.Position.Y );

                // Select the element found
                if (ele != null)
                    SelectElement(ele);
            
            catch (Exception)  
        
    

谢谢!

【问题讨论】:

如何将鼠标实际移动到元素上?谢谢。 另外,页面上是否有 iframe 元素? 使用鼠标 ;-) 该功能发生在计时器滴答声中。如果用户按住 ctrl,则鼠标下的元素被选中 我不是网页的开发者,但我尝试了很多不同的网页... www.nab.com.au, www.microsoft.com, www.google.com。无论网站是什么,我都会返回标题。 Cursor.Position.X 和 Cursor.Position.Y 是正确的值,只是似乎我没有正确地将它们传递给“ExecuteScript”方法。 是的,谢谢我刚刚使用 Python 绑定和 SO 站点作为目标重现了该问题 - 无论坐标是什么,都会返回标题元素。 【参考方案1】:

我也遇到了这个问题。我发现它被抛出的原因是因为我试图获取位置的元素被隐藏(具有属性 display:none)。因此没有位置。

【讨论】:

【参考方案2】:

alecxe 的代码在大多数情况下都有效,但如果页面包含框架或 iframe,它将失败。

还需要更多代码来尊重框架/iframe。

/// <summary>
/// Get the element at the viewport coordinates X, Y
/// </summary>
static public RemoteWebElement GetElementFromPoint(RemoteWebDriver i_Driver, int X, int Y)

    while (true)
    
        String s_Script = "return document.elementFromPoint(arguments[0], arguments[1]);";

        RemoteWebElement i_Elem = (RemoteWebElement)i_Driver.ExecuteScript(s_Script, X, Y);
        if (i_Elem == null)
            return null;

        if (i_Elem.TagName != "frame" && i_Elem.TagName != "iframe")
            return i_Elem;

        Point p_Pos = GetElementPosition(i_Elem);
        X -= p_Pos.X;
        Y -= p_Pos.Y;

        i_Driver.SwitchTo().Frame(i_Elem);
    


/// <summary>
/// Get the position of the top/left corner of the Element in the document.
/// NOTE: RemoteWebElement.Location is always measured from the top of the document and ignores the scroll position.
/// </summary>
static public Point GetElementPosition(RemoteWebElement i_Elem)

    String s_Script = "var X, Y; "
                    + "if (window.pageYOffset) " // supported by most browsers 
                    + " "
                    + "  X = window.pageXOffset; "
                    + "  Y = window.pageYOffset; "
                    + " "
                    + "else " // Internet Explorer 6, 7, 8
                    + " "
                    + "  var  Elem = document.documentElement; "         // <html> node (IE with DOCTYPE)
                    + "  if (!Elem.clientHeight) Elem = document.body; " // <body> node (IE in quirks mode)
                    + "  X = Elem.scrollLeft; "
                    + "  Y = Elem.scrollTop; "
                    + " "
                    + "return new Array(X, Y);";

    RemoteWebDriver i_Driver = (RemoteWebDriver)i_Elem.WrappedDriver;
    IList<Object>   i_Coord  = (IList<Object>)  i_Driver.ExecuteScript(s_Script);

    int s32_ScrollX = Convert.ToInt32(i_Coord[0]);
    int s32_ScrollY = Convert.ToInt32(i_Coord[1]);

    return new Point(i_Elem.Location.X - s32_ScrollX,
                     i_Elem.Location.Y - s32_ScrollY);

这应该在 WebDriver 中实现。

【讨论】:

【参考方案3】:

这实际上是关于如何将坐标传递到脚本中。 脚本参数必须单独指定为单独的ExecuteScript() 参数。在您的情况下发生的情况是,您基本上指定了一个 x 参数,这使得它认为 y 应该被视为默认的 0 值。在y=0 通常有一个标题。

代替:

ele = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript(
                        "return document.elementFromPoint(arguments[0], arguments[1])", 
                        new int[]  Cursor.Position.X, Cursor.Position.Y );

你应该这样做:

ele = (IWebElement)((IJavaScriptExecutor)driver).ExecuteScript(
                        "return document.elementFromPoint(arguments[0], arguments[1])", 
                        Cursor.Position.X, Cursor.Position.Y);

【讨论】:

谢谢alecxe,你说得对,这就是问题所在。但是 C# 的文档是 object IJavaScriptExecuter.ExecuteScript(string script, params object[] args)。文档是错误的还是我读错了?对我来说,这意味着第二个参数应该是一个参数数组。 @Scotty 是的,我同意这很令人困惑。

以上是关于通过 Selenium 驱动程序执行 Javascript elementFromPoint的主要内容,如果未能解决你的问题,请参考以下文章

突破网站对selenium的屏蔽

Docker selenium自动化 - 执行程序没反应不执行原因,强制处理之前失败的进程,“... requests waiting for a slot to be free“问题解决

Docker selenium自动化 - 执行程序没反应不执行原因,强制处理之前失败的进程,“... requests waiting for a slot to be free“问题解决

python+selenium环境搭建步骤

selenium+chrome浏览器驱动-爬取百度图片

Selenium模块的使用