使用ChromeDriver和FirefoxDriver的Selenium WebDriver C#全网站截图

17

当我使用ChromeDriver截图时,截屏的大小与我的视窗大小相同。
当我使用FirefoxDriver截图时,得到的是我想要的效果——整个网站的全屏截图。

ChromeDriver的声明方式如下:

IWebDriver driver = new ChromeDriver();

FirefoxDriver的声明如下:

IWebDriver driver = new FirefoxDriver();

两个驱动程序执行相同的代码:

driver.Manage().Window.Maximize();
driver.Navigate().GoToUrl(url);//url is a string variable
ITakesScreenshot screenshotDriver = driver as ITakesScreenshot;
Screenshot screenshot = screenshotDriver.GetScreenshot();
screenshot.SaveAsFile("c:/test.png", ImageFormat.Png);

ChromeDriver的test.png分辨率为1920x1099,仅包含浏览器视口。
FirefoxDriver的test.png分辨率为1903x16559,包含整个页面。

我知道GetScreenshot()方法返回的分辨率大小并不相同,因为在IEDriver、FirefoxDriver、OperaDriver和ChromeDriver中实现略有不同。

我的问题如下:

  1. 为什么ChromeDriver和FirefoxDriver的.GetScreenshot()方法存在这样的差异,即使它们使用相同的接口(ITakesScreenshot)?

  2. 是否有办法使ChromeDriver的GetScreenshot()方法返回整个网页屏幕,而不仅是视口?

4个回答

11

使用ChromeDriver2无法获取整个页面的屏幕截图,我们需要进行手动实现。我修改了一个博客中的方法,可以在ChromeDriver上正常工作。

按照以下方式使用此方法:

private IWebDriver _driver = new ChromeDriver(CHROME_DRIVER_PATH);
screenshot.SaveAsFile(saveFileName, ImageFormat.Jpeg);

public Bitmap GetEntereScreenshot()
    {

        Bitmap stitchedImage = null;
        try
        {
            long totalwidth1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.offsetWidth");//documentElement.scrollWidth");

            long totalHeight1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return  document.body.parentNode.scrollHeight");

            int totalWidth = (int)totalwidth1;
            int totalHeight = (int)totalHeight1;

            // Get the Size of the Viewport
            long viewportWidth1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.clientWidth");//documentElement.scrollWidth");
            long viewportHeight1 = (long)((IJavaScriptExecutor)_driver).ExecuteScript("return window.innerHeight");//documentElement.scrollWidth");

            int viewportWidth = (int)viewportWidth1;
            int viewportHeight = (int)viewportHeight1;


        // Split the Screen in multiple Rectangles
        List<Rectangle> rectangles = new List<Rectangle>();
        // Loop until the Total Height is reached
        for (int i = 0; i < totalHeight; i += viewportHeight)
        {
            int newHeight = viewportHeight;
            // Fix if the Height of the Element is too big
            if (i + viewportHeight > totalHeight)
            {
                newHeight = totalHeight - i;
            }
            // Loop until the Total Width is reached
            for (int ii = 0; ii < totalWidth; ii += viewportWidth)
            {
                int newWidth = viewportWidth;
                // Fix if the Width of the Element is too big
                if (ii + viewportWidth > totalWidth)
                {
                    newWidth = totalWidth - ii;
                }

                // Create and add the Rectangle
                Rectangle currRect = new Rectangle(ii, i, newWidth, newHeight);
                rectangles.Add(currRect);
            }
        }

        // Build the Image
        stitchedImage = new Bitmap(totalWidth, totalHeight);
        // Get all Screenshots and stitch them together
        Rectangle previous = Rectangle.Empty;
        foreach (var rectangle in rectangles)
        {
            // Calculate the Scrolling (if needed)
            if (previous != Rectangle.Empty)
            {
                int xDiff = rectangle.Right - previous.Right;
                int yDiff = rectangle.Bottom - previous.Bottom;
                // Scroll
                //selenium.RunScript(String.Format("window.scrollBy({0}, {1})", xDiff, yDiff));
                ((IJavaScriptExecutor)_driver).ExecuteScript(String.Format("window.scrollBy({0}, {1})", xDiff, yDiff));
                System.Threading.Thread.Sleep(200);
            }

            // Take Screenshot
            var screenshot = ((ITakesScreenshot)_driver).GetScreenshot();

            // Build an Image out of the Screenshot
            Image screenshotImage;
            using (MemoryStream memStream = new MemoryStream(screenshot.AsByteArray))
            {
                screenshotImage = Image.FromStream(memStream);
            }

            // Calculate the Source Rectangle
            Rectangle sourceRectangle = new Rectangle(viewportWidth - rectangle.Width, viewportHeight - rectangle.Height, rectangle.Width, rectangle.Height);

            // Copy the Image
            using (Graphics g = Graphics.FromImage(stitchedImage))
            {
                g.DrawImage(screenshotImage, rectangle, sourceRectangle, GraphicsUnit.Pixel);
            }

            // Set the Previous Rectangle
            previous = rectangle;
        }
        }
        catch (Exception ex)
        {
            // handle
        }
        return stitchedImage;
    }

运行此脚本时会出现一个小问题,因为它是JavaScript注入,当通过网页向下滚动时,它会将控制权交给浏览器。不管怎样,感谢Roemer :) - S.Roshanth
1
它还与Facebook和Yahoo存在问题。当您向下滚动时,这些网页会在顶部显示绝对div,并且该div会重复出现。无论如何,大多数网页都可以正常工作。 - sm.abdullah
似乎宽度不对。我准备了另一个解决方案,可以从屏幕截图中获取尺寸。 - Toolkit
1
我只能通过以下方式可靠地固定高度和宽度来使其正常工作: totalwidth1 = document.body.scrollWidth. totalHeight1 = document.body.scrollHeight. viewportWidth1 = document.documentElement.clientWidth. viewportHeight1 = document.documentElement.clientHeight. - Michiel Bugher
1
如果你成功获取了正确的宽度和高度,剩下的代码就能完成任务了。因为不同的人尝试获取它们的方式可能不一样,我认为这取决于浏览器及其版本。 - S.Roshanth
请注意,在 Windows 中缩放必须设置为 100%,否则这将无法正常工作!我通常使用 150%,并且在处理它时一度感到困惑... - Daniel Vygolov

11

我清理了@Selvantharajah Roshanth的答案,并添加了一个检查,以便它不会尝试拼接已经适合视口的屏幕截图。

public Image GetEntireScreenshot()
{
    // Get the total size of the page
    var totalWidth = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return document.body.offsetWidth"); //documentElement.scrollWidth");
    var totalHeight = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return  document.body.parentNode.scrollHeight");
    // Get the size of the viewport
    var viewportWidth = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return document.body.clientWidth"); //documentElement.scrollWidth");
    var viewportHeight = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return window.innerHeight"); //documentElement.scrollWidth");

    // We only care about taking multiple images together if it doesn't already fit
    if (totalWidth <= viewportWidth && totalHeight <= viewportHeight)
    {
        var screenshot = driver.TakeScreenshot();
        return ScreenshotToImage(screenshot);
    }
    // Split the screen in multiple Rectangles
    var rectangles = new List<Rectangle>();
    // Loop until the totalHeight is reached
    for (var y = 0; y < totalHeight; y += viewportHeight)
    {
        var newHeight = viewportHeight;
        // Fix if the height of the element is too big
        if (y + viewportHeight > totalHeight)
        {
            newHeight = totalHeight - y;
        }
        // Loop until the totalWidth is reached
        for (var x = 0; x < totalWidth; x += viewportWidth)
        {
            var newWidth = viewportWidth;
            // Fix if the Width of the Element is too big
            if (x + viewportWidth > totalWidth)
            {
                newWidth = totalWidth - x;
            }
            // Create and add the Rectangle
            var currRect = new Rectangle(x, y, newWidth, newHeight);
            rectangles.Add(currRect);
        }
    }
    // Build the Image
    var stitchedImage = new Bitmap(totalWidth, totalHeight);
    // Get all Screenshots and stitch them together
    var previous = Rectangle.Empty;
    foreach (var rectangle in rectangles)
    {
        // Calculate the scrolling (if needed)
        if (previous != Rectangle.Empty)
        {
            var xDiff = rectangle.Right - previous.Right;
            var yDiff = rectangle.Bottom - previous.Bottom;
            // Scroll
            ((IJavaScriptExecutor) driver).ExecuteScript(String.Format("window.scrollBy({0}, {1})", xDiff, yDiff));
        }
        // Take Screenshot
        var screenshot = driver.TakeScreenshot();
        // Build an Image out of the Screenshot
        var screenshotImage = ScreenshotToImage(screenshot);
        // Calculate the source Rectangle
        var sourceRectangle = new Rectangle(viewportWidth - rectangle.Width, viewportHeight - rectangle.Height, rectangle.Width, rectangle.Height);
        // Copy the Image
        using (var graphics = Graphics.FromImage(stitchedImage))
        {
            graphics.DrawImage(screenshotImage, rectangle, sourceRectangle, GraphicsUnit.Pixel);
        }
        // Set the Previous Rectangle
        previous = rectangle;
    }
    return stitchedImage;
}

private static Image ScreenshotToImage(Screenshot screenshot)
{
    Image screenshotImage;
    using (var memStream = new MemoryStream(screenshot.AsByteArray))
    {
        screenshotImage = Image.FromStream(memStream);
    }
    return screenshotImage;
}

1
很棒的答案!我用了几个月,但现在发现这里有一个小错误。例如,如果总高度比视口大4倍,则会得到由4个图像组成的屏幕截图。假设您在测试失败时进行SS。如果您的测试在页面底部失败,则只会重复底部部分4次。需要添加命令以在开头滚动到顶部,并使整个页面无论测试是否停在底部、顶部或中间:((IJavaScriptExecutor)driver).ExecuteScript("window.scrollTo(0, 0)") - kotoj

4
似乎ChromeDriver尚未实现全屏截图,因为其先前的实现存在一些不准确之处。
来源:https://code.google.com/p/chromedriver/issues/detail?id=294 最近我编写了一个基于Selenium的应用程序来测试Internet Explorer UI,发现以下问题:
  1. 使用Selenium拍摄屏幕截图不如使用.NET快;
  2. 当对话框存在时,Selenium无法拍摄屏幕截图。这是一个主要的缺点,因为我需要在与页面交互期间识别意外的对话框。
调查使用System.Drawing中的Graphics.CopyFromScreen方法作为替代解决方案,直到在Chrome中实现该功能。但是一旦你尝试过.NET方法,我认为你就不会回头了 =]

1
我遇到了同样的问题,但 ChromeDriver2 并不支持它。 因此,我创建了一个小脚本,可以滚动浏览页面、截图并将所有内容拼接在一起。 您可以在我的博客文章中找到这个脚本: http://dev.flauschig.ch/wordpress/?p=341

5
请注意,仅链接答案不受鼓励,SO答案应该是寻找解决方案的终点(而不是另一个引用站点,随着时间的推移可能变得陈旧)。 请考虑在此处添加一个独立的概要,并将链接作为参考。 - kleopatra
@kleopatra 你是对的,这个链接已经失效了。 - undefined
它仍然可以使用,我没有移除它,我仍然可以访问它。不过,不确定这些信息是否仍然有效,因为这是10年前的事情了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接