使用Selenium WebDriver获取网页的屏幕位置

时间:2013-12-24 13:04:05

标签: selenium selenium-webdriver

有没有办法通过Selenium WebDriver获取HTML窗口(页面主体)的屏幕坐标?

10 个答案:

答案 0 :(得分:4)

看了几次并且还没有从WebDriver找到一个优雅的解决方案(他们有一个看起来在他们的ILocatable设置中支持的参数,但该方法尚未实现)。

我所做的是使用UIAutomation获取windows AutomationElement并使用树木行者来查找窗口的实际对象 - 缺点是我注意到浏览器偶尔会更新它们的窗口,因此条件必须每隔一段时间更改一次容纳。

这是一些示例代码(我在这里删除了一些公司代码,所以它在我的结尾更优雅,但这应该适用于C#)

    public static Rectangle GetAbsCoordinates(this IWebElement element)
    {
        var driver = GetDriver(element);
        var handle = GetIntPtrHandle(driver);
        var ae = AutomationElement.FromHandle(handle);
        AutomationElement doc = null;
        var caps = ((RemoteWebDriver) driver).Capabilities;
        var browserName = caps.BrowserName;
        switch (browserName)
        {
            case "safari":
                var conditions = (new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane),
                    new PropertyCondition(AutomationElement.ClassNameProperty, "SearchableWebView")));
                doc = ae.FindFirst(TreeScope.Descendants, conditions);
                break;
            case "firefox":
                doc = ae.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document));
                break;
            case "chrome":
                doc = ae.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Chrome Legacy Window"));
                if (doc == null)
                {
                    doc = ae.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
                    if (doc == null)
                        throw new Exception("unable to find element containing browser window");
                    doc = doc.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document));
                }
                break;
            case "internet explorer":
                doc = ae.FindFirst(TreeScope.Descendants, new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane),
                    new PropertyCondition(AutomationElement.ClassNameProperty, "TabWindowClass")));
                break;
        }

        if (doc == null)
            throw new Exception("unable to find element containing browser window");

        var iWinLeft = (int) doc.Current.BoundingRectangle.Left;
        var iWinTop = (int)doc.Current.BoundingRectangle.Top;

        var coords = ((ILocatable) element).Coordinates;
        var rect = new Rectangle(iWinLeft + coords.LocationInDom.X, iWinTop + coords.LocationInDom.Y, element.Size.Width, element.Size.Height);
        return rect;
    }

    public static IWebDriver GetDriver(this IWebElement e)
    {
        return ((IWrapsDriver)e).WrappedDriver;
    }

    public static IntPtr GetIntPtrHandle(this IWebDriver driver, int timeoutSeconds = Timeout)
    {
        var end = DateTime.Now.AddSeconds(timeoutSeconds);
        while(DateTime.Now < end)
        {
            // Searching by AutomationElement is a bit faster (can filter by children only)
            var ele = AutomationElement.RootElement;
            foreach (AutomationElement child in ele.FindAll(TreeScope.Children, Condition.TrueCondition))
            {
                if (!child.Current.Name.Contains(driver.Title)) continue;
                return new IntPtr(child.Current.NativeWindowHandle);;
            }
        }
        return IntPtr.Zero;
    }

答案 1 :(得分:1)

嗯,我不能直接评论一个询问chrome的用户,所以我必须在这里添加另一条评论。

基本上,对于UIAutomation,您需要掌握一个名为inspect的工具(在8.1 SDK中免费提供)。像uispy这样的旧工具可能也会起作用。

基本上你会启动chrome然后启动检查器工具 - 你将看到树状结构,然后向下导航到包含DOM的文档。打开工具中的突出显示以使其更容易。

Chrome在树形控件的布局中看起来非常动态 - 不得不修改它几次以适应我正在看的控件。如果您使用的版本与我不同 - 基本上在树中找到文档窗口并查看与其关联的所有控件模式 - 这就是我传递给PropertyCondition以了解如何搜索控件。 Intellisense应该为您提供不同的东西来查询,如AutomationElement.NameProperty。这是我的例子 - 我注意到在winXP机器和win8机器上运行chrome时有区别...因此检查为null。

就像我之前说过的那样 - 这不是很优雅,如果将它内置到Selenium(我想他们有更好的方法来确定DOM区域的坐标)会很棒......我认为这也会有问题对于那些搬到Selenium Grid的人来说(就像我正在做的那样) - 据我所知,使用它我不知道你是否可以通过一堆支持dll将硒转移到远程机器上...至少没有很多黑客。

如果它仍然不适合你 - 给我一个关于操作系统,Chrome版本的具体想法,我会尝试看看并给出确切的属性匹配。可能是最好的,但如果你弄乱自己,不幸的是这些东西不是静止的。

答案 2 :(得分:1)

Zechtitus发布的代码非常棒,我在IE11和Chrome版本39.0.2171.95米下尝试了它,它就像一个魅力。虽然我不得不传递IWebDriver的真实对象而不是使用WrappedDriver,因为它不能与Chrome一起使用。仅为了您的信息,我有Win 7终极x64和使用Selenium WebDriver 2.44。这是我从Zechtitus中获取并修改它的代码:

    public static Rectangle GetAbsCoordinates(IWebDriver driver, IWebElement element)
    {
        var handle = GetIntPtrHandle(driver);
        var ae = AutomationElement.FromHandle(handle);
        AutomationElement doc = null;
        var caps = ((RemoteWebDriver)driver).Capabilities;
        var browserName = caps.BrowserName;
        switch (browserName)
        {
            case "safari":
                var conditions = (new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane),
                    new PropertyCondition(AutomationElement.ClassNameProperty, "SearchableWebView")));
                doc = ae.FindFirst(TreeScope.Descendants, conditions);
                break;
            case "firefox":
                doc = ae.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document));
                break;
            case "chrome":
                doc = ae.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Chrome Legacy Window"));
                if (doc == null)
                {
                    doc = ae.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
                    if (doc == null)
                        throw new Exception("unable to find element containing browser window");
                    doc = doc.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document));
                }
                break;
            case "internet explorer":
                doc = ae.FindFirst(TreeScope.Descendants, new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane),
                    new PropertyCondition(AutomationElement.ClassNameProperty, "TabWindowClass")));
                break;
        }

        if (doc == null)
            throw new Exception("unable to find element containing browser window");

        var iWinLeft = (int)doc.Current.BoundingRectangle.Left;
        var iWinTop = (int)doc.Current.BoundingRectangle.Top;

        var coords = ((ILocatable)element).Coordinates;
        var rect = new Rectangle(iWinLeft + coords.LocationInDom.X, iWinTop + coords.LocationInDom.Y, element.Size.Width, element.Size.Height);
        return rect;
    }

    public static IntPtr GetIntPtrHandle(this IWebDriver driver, int timeoutSeconds = 20)
    {
        var end = DateTime.Now.AddSeconds(timeoutSeconds);
        while (DateTime.Now < end)
        {
            // Searching by AutomationElement is a bit faster (can filter by children only)
            var ele = AutomationElement.RootElement;
            foreach (AutomationElement child in ele.FindAll(TreeScope.Children, Condition.TrueCondition))
            {
                if (!child.Current.Name.Contains(driver.Title)) continue;
                return new IntPtr(child.Current.NativeWindowHandle); ;
            }
        }
        return IntPtr.Zero;
    }

我用过这样的话:

Rectangle recView = GetAbsCoordinates(MyWebDriverObj,myIWebElementObj);

然后将正确的X,Y存储在recView.X和recView.Y中 正如我所说,它为IE11和Chrome都在为我工作。 祝你好运

答案 3 :(得分:1)

  

是。这是可能的。随便一招。在下面找到我的代码以便继续   屏幕顶部的网页元素。

  long scrollPosition = getScollPosition();
  long elemYPositionOnScreen = (long) elem.getLocation().getY() - scrollPosition;

  public static long getScrollYPosition() {
    WebDriver driver = DriverFactory.getCurrentDriver();

    JavascriptExecutor jse = (JavascriptExecutor) driver;
    Long scrollYPos = (Long) jse.executeScript("return window.scrollY;");

    return scrollYPos;
  }

答案 4 :(得分:0)

我快速浏览了一下chrome,你可能会有更好的运气。

doc = win.Find.ByConditions(new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_RenderWidgetHostHWND"));

我认为类名称对于chrome来说是一致的...似乎适用于我的旧版本和更新版本的操作系统 - chrome版本34.0.1847.116m。希望有所帮助。

答案 5 :(得分:0)

一旦得到支持,这应该有效:

  WebElement htmlElement = driver.findElement(By.tagName("html"));
  Point viewPortLocation = ((Locatable) htmlElement).getCoordinates().onScreen();
  int x = viewPortLocation.getX();
  int y = viewPortLocation.getY();

但是现在它引发了以下错误:

java.lang.UnsupportedOperationException: Not supported yet.
at org.openqa.selenium.remote.RemoteWebElement$1.onScreen(RemoteWebElement.java:342)

(on org.seleniumhq.selenium:selenium-java:2.46.0)

答案 6 :(得分:0)

我在Robot Framework中需要一个this,而我受到Jeyabal解决方案的启发,所以下面的改编对我有用:

${verticalWindow}=     Execute Javascript          return window.scrollY;
${verticalElement} =   Get Vertical Position       /xpath
${hasScrolled} =       Evaluate                    (${verticalElement} - ${verticalWindow}) == 0

答案 7 :(得分:0)

没有什么对我有用。一种解决方法是使用window.innerHeightwindow.innerWidth并从左下角向上移动。假设浏览器的底部边框几乎为0(没有水平滚动条或厚窗口装饰)。

win_pos = selenium.get_window_position()
win_size = selenium.get_window_size()
win_bottom_y = win_pos['y'] + win_size['height']

# We assume viewport x == window x. For y coordinate we take the bottom
# of the browser and subtract the viewport height 
viewport_height = selenium.execute_script('return window.innerHeight')
viewport_width = selenium.execute_script('return window.innerWidth')
viewport_y = win_bottom_y - viewport_height

这不是100%准确,但可以针对您的情况进行调整。

答案 8 :(得分:-1)

你可以这样试试:

   WebDriver driver=new FirefoxDriver();
   driver.get("http://www.google.com");
   JavascriptExecutor js=(JavascriptExecutor) driver;
   Double i= (Double) js.executeScript("var element = document.getElementById('hplogo');var position = element.getBoundingClientRect();return position.left");
   System.out.print(i);

答案 9 :(得分:-1)

试试这个,我希望它会对你有所帮助:

Rectangle rec = new Rectangle(element.getLocation(), element.getSize());