截取网页截图的最佳方式

时间:2012-07-28 04:16:33

标签: c# .net winapi selenium screenshot

截取网页截图的最佳方法是什么? 目前我刚刚开始使用firefox的selenium实例并使用winapi将其带到前面并制作屏幕截图。 我已经问过类似的question了。

有两点:

  • 慢度。
  • 如果任何窗口出现高于我们网页浏览器的窗口,此窗口将在我们的屏幕截图中显示。

是否有任何方法可以更加“编程”截屏?

以下是我现在使用的一些代码:

class FirefoxDriverEx : FirefoxDriver
{
    public Process GetFirefoxProcess()
    {
        var fi = typeof(FirefoxBinary).GetField("process", BindingFlags.NonPublic | BindingFlags.Instance);
        return fi.GetValue(this.Binary) as Process;
    }
}

以下是说明截屏过程的代码:

using (FirefoxDriverEx driver = new FirefoxDriverEx())
{
    driver.Navigate().GoToUrl(url);

    var process = driver.GetFirefoxProcess();

    if (process != null)
    {
        var screenCapture = new ScreenCapture();
        Win.SetForegroundWindow(process.MainWindowHandle.ToInt32());
    }
}

现在,我正在考虑一些控制窗口队列的管理员来截取屏幕截图。

问题编辑。

我不是在寻找一种解决方案,只是在内存中获取屏幕截图并将其返回到HTTP流。因此,任何保存屏幕截图并将其保存到文件然后从那里获取的方法都非常模糊。

问题编辑#2。

我忘了提。需要的截图应该按照用户的看法进行。因此,截图应该有浏览器窗口和Web浏览器窗口边界内的网站。我找不到任何方法来改变在硒的WebDriver中截取屏幕截图的模式。 WebDriver只是在没有任何浏览器窗口的情况下截取页面。

5 个答案:

答案 0 :(得分:6)

我建议使用getScreenshotAs。它甚至可以在屏幕上显示“视线外”部分。

以下是gr0ovy中的一些示例代码。

import java.io.IOException
import java.net.URL
import java.nio.file.Path
import java.nio.file.Paths
import java.text.SimpleDateFormat

import org.openqa.selenium.Capabilities
import org.openqa.selenium.TakesScreenshot
import org.openqa.selenium.WebDriverException
import org.openqa.selenium.remote.CapabilityType
import org.openqa.selenium.remote.DriverCommand
import org.openqa.selenium.remote.RemoteWebDriver
import org.openqa.selenium.OutputType
import org.openqa.selenium.WebDriver



public class Selenium2Screenshot {
private WebDriver driver
private String browserType
private boolean skipScreenshots

public Selenium2Screenshot(WebDriver webDriver, String browserType, boolean skipScreenshots) {
    this.driver = webDriver
    this.browserType = browserType
    this.skipScreenshots = skipScreenshots
}
public void takeScreenshot(String filenameBase) {
    if (!skipScreenshots) {
        Date today
        String formattedDate
        SimpleDateFormat formatter
        Locale currentLocale
        File scrFile
        currentLocale = new Locale("en", "US")
        formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", currentLocale)
        today = new Date()
        formattedDate = formatter.format(today)
        String filename = getUiAutomationDir() + filenameBase + "_" + browserType + formattedDate + ".png"
        Log.logger.info("Screenshot filename = " + filename)

        try {
            scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE)
            JavaIO.copy(scrFile.getAbsolutePath(), filename)
        } catch (Exception e) {
            Log.logger.error(e.message, e)
        }
    } else {
        Log.logger.info("Skipped Screenshot")
    }
}
private String getUiAutomationDir()
{
    String workingDir = System.getProperty("user.dir")
    Path workingDirPath = Paths.get(workingDir)
    String returnString = workingDirPath.toString() + "\\"
    return returnString
}

}

于2012年8月1日编辑:

获取应用程序处理代码。我肯定会多次复制stackoverflow上的代码,但希望这不是与其他帖子完全相同的代码: - )

public static IntPtr FindWindowByPartialCaption(String partialCaption)
    {
        var desktop = User32.GetDesktopWindow();
        var children = EnumerateWindows.GetChildWindows(desktop);
        foreach (var intPtr in children)
        {
            var current = GetText(intPtr);
            if (current.Contains(partialCaption))
                return intPtr;
        }
        return IntPtr.Zero;
    }

    [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
    public static extern IntPtr GetDesktopWindow();

    [DllImport("user32.dll")]
    public static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowProc lpEnumFunc, IntPtr lParam);

    public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
    public static List<IntPtr> GetChildWindows(IntPtr parent)
    {
        return GetChildWindows(parent, false);
    }
    public static List<IntPtr> GetChildWindows(IntPtr parent, bool reverse)
    {
        List<IntPtr> result = new List<IntPtr>();
        GCHandle listHandle = GCHandle.Alloc(result);
        try
        {
            EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
            EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
        }
        finally
        {
            if (listHandle.IsAllocated)
                listHandle.Free();
        }
        if (reverse)
        {
            List<IntPtr> resultList = result.Reverse<IntPtr>().ToList();
            return resultList;
        } 
        else
            return result;
    }

    private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    {
        GCHandle gch = GCHandle.FromIntPtr(pointer);
        List<IntPtr> list = gch.Target as List<IntPtr>;
        if (list == null)
        {
            throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
        }
        list.Add(handle);
        //  You can modify this to check to see if you want to cancel the operation, then return a null here
        return true;
    }
}

http://www.pinvoke.net/也是一个很好的资源。

答案 1 :(得分:0)

http://msdn.microsoft.com/en-us/library/windows/desktop/dd162869(v=vs.85).aspx

我个人喜欢这个API。创建一个宽度和高度的位图,根据返回的GetWindowRect API矩形计算并使用HDC参数(例如):

thebitmap.GetHdc()

你应该没事。

修改:还要检查this

顺便说一句,你可以截取你喜欢的任何一个窗口的截图,即使它们倒退了。(请注意,这对于最小化的窗口不起作用。但是,如果你真的需要,那么也有一些方法。)< / p>

答案 2 :(得分:0)

如果您正在寻找以编程方式获取给定流程主窗口的屏幕截图,那么这是一个执行此功能的函数:

    public static Bitmap TakeScreenshot(Process process)
    {
        // may need a process Refresh before
        return TakeScreenshot(process.MainWindowHandle);
    }

    public static Bitmap TakeScreenshot(IntPtr handle)
    {
        RECT rc = new RECT();
        GetWindowRect(handle, ref rc);
        Bitmap bitmap = new Bitmap(rc.right - rc.left, rc.bottom - rc.top);
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            PrintWindow(handle, graphics.GetHdc(), 0);
        }
        return bitmap;
    }

    [DllImport("user32.dll")]
    private static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect);

    [DllImport("user32.dll")]
    private static extern bool PrintWindow(IntPtr hWnd, IntPtr hDC, int flags);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

不幸的是,在配备Aero的操作系统(Vista / Win7 / Win8)上,它无法捕获完整的透明边框。通常的透明边框将变黑。也许它足以满足您的目标。

答案 3 :(得分:0)

我多年来一直在生产应用中使用webshotcmd(付费版本也是命令行)。它可以配置为等待页面加载,等待页面加载后n秒等。它使用Internet Explorer并在Windows上运行。启动速度非常快(根据我的经验,msie activex一直都是即时加载的。)

除了上述内容之外,我会推荐一些基于Webkit libray的东西,它会比Firefox小得多,并且启动速度非常快(wkhtmltoimage现在只能在Linux上使用,但是当它可用于Windows时,我会去找它 - 也是命令行)。现在只需google for webkit截图(使用webkit的大量可用截图使我相信使用该DLL很容易移植到C#)。

修改:考虑到您的第二次修改,请查看Chrome Screen Capture来源。 要试用它,扩展程序可在商店/扩展程序库中找到。

答案 4 :(得分:0)

我能够通过将窗口(逐片)复制到一个位图来实现这一点,该位图设置为我的webBrowser控件的ScrollRectangle的大小。虽然它当然不是实现这一目标的最优雅方式,但我想分享代码以防任何人都可以使用它。一旦我有了大部分工作的东西,我就可以添加一些args,现在我可以从命令行执行这个实用程序:

Executable_Path URL文件名

    /// <summary>
    /// This method is called to start the process of copying the webpage to the bitmap
    /// this should be called after the page has fully loaded (use DocumentCompleted event to determine
    /// if the page has completed loading if calling from the command line.)
    /// </summary>
    private void copyWebpageToImage()
    {
        //these two vars will house the current position in the bmp file (starting at 0,0)
        int currXPosition = 0;
        int currYPosition = 0;

        //we need to set the height and width of our bitmap to the scrollrectangle of the webbrowser document object
        int width = webBrowser1.Document.Body.ScrollRectangle.Width;
        int height = webBrowser1.Document.Body.ScrollRectangle.Height;
        //instantiate the bitmap
        bm = new Bitmap(wd, ht);

        //Instantiate our graphics object
        Graphics gfx = Graphics.FromImage((Image)bm);

        //this point is used throughout the process, and helps to determine where the form is at on the screen
        Point formPoint = Form1.ActiveForm.Location;
        formPoint.X = formPoint.X + webBrowser1.Location.X;
        formPoint.Y = formPoint.Y + webBrowser1.Location.Y;
        formPoint.X = formPoint.X + 8; //offsets for my form (may be different for yours)
        formPoint.Y = formPoint.Y + 33; //offsets for my form

        //begin our recursive call that will stop when it reaches the end of the page
        copyEverythingToBitmap(bm, currXPosition, currYPosition, formPoint, gfx);

    }

    private void copyEverythingToBitmap(Bitmap bm, int currXPosition, int currYPosition, Point formPoint, Graphics gfx)
    {
        //check to see if currXPosition and currYPosition are both 0, if so we just began, call the zero copy method
        if (currXPosition == 0 && currYPosition == 0)
        {
            performZeroCopy(bm, currXPosition, currYPosition, formPoint, gfx);
        }
        //if the current x position is less than the total width of the scrollrectangle - the width of the webbrowser,
        //then we need to scroll the window, and copy the contents, y stays the same
        else if (currXPosition < bm.Width - webBrowser1.Width)
        {
            AlterXPosition(bm, ref currXPosition, ref currYPosition, ref formPoint, gfx);
        }
        //if we are no longer at the zero, zero, and we cannot increase the x position anymore,
        //then we need to scroll the window down and copy the contents, x is reset back to zero
        else if(currYPosition < bm.Height - webBrowser1.Height)
        {
            currYPosition = currYPosition + webBrowser1.Height - 20;
            currXPosition = 0;
            performZeroCopy(bm, currXPosition, currYPosition, formPoint, gfx);
        }
    }

    /// <summary>
    /// The name of this method is slightly misleading.  It inherently means that X is zero.
    /// </summary>
    private void performZeroCopy(Bitmap bm, int currXPosition, int currYPosition, Point formPoint, Graphics gfx)
    {
        webBrowser1.Document.Window.ScrollTo(currXPosition, currYPosition);
        gfx.CopyFromScreen(formPoint, new Point(currXPosition, currYPosition), new Size(webBrowser1.Width - 20, webBrowser1.Height - 20));

        if (currXPosition < bm.Width - webBrowser1.Width)
        {
            AlterXPosition(bm, ref currXPosition, ref currYPosition, ref formPoint, gfx);
        }
        else if(currYPosition < bm.Height - webBrowser1.Height)
        {
            currYPosition = currYPosition + webBrowser1.Height - 20;
            currXPosition = 0;
            performZeroCopy(bm, currXPosition, currYPosition, formPoint, gfx);
        }
    }

    private void AlterXPosition(Bitmap bm, ref int currXPosition, ref int currYPosition, ref Point formPoint, Graphics gfx)
    {
        currXPosition = currXPosition + webBrowser1.Width - 20;
        webBrowser1.Document.Window.ScrollTo(bm.Width - currXPosition, currYPosition);

        gfx.CopyFromScreen(formPoint, new Point(bm.Width - currXPosition - 3, currYPosition), new Size(webBrowser1.Width - 20, webBrowser1.Height - 20));

        if (currXPosition + webBrowser1.Width < bm.Width)
        {
            //we still have not traversed the full width of the page, call to alterxposition again...
        }
        else
        {
            copyEverythingToBitmap(bm, currXPosition, currYPosition, formPoint, gfx);
        }
    }

    private void saveImageToFile(string p)
    {
        bm.Tag = DateTime.Now;
        bm.Save(p, ImageFormat.Jpeg);
    }