16

截取网页截图的最佳方法是什么?目前我只是启动了一个 selenium 实例并使用 winapi 将它带到前面并制作屏幕截图。我已经问过类似的问题 了。

有两点:

  • 缓慢。
  • 如果任何窗口偶然高于我们的网络浏览器的窗口,该窗口将印在我们的屏幕截图中。

有什么方法可以更“程序化”地截屏吗?

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

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 只是在没有任何浏览器窗口的情况下截取页面。

4

5 回答 5

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/也是一个很好的资源。

于 2012-07-28T21:42:32.947 回答
0

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

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);
    }
于 2012-11-26T22:02:44.397 回答
0

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

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

编辑:考虑到您的第二次编辑,请查看Chrome 屏幕捕获源。要尝试它,扩展程序可在商店/扩展程序库中找到。

于 2012-08-16T15:16:14.047 回答
0

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

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

thebitmap.GetHdc()

你应该没事。

编辑:也检查这个

顺便说一句,您可以截取您喜欢的任何窗口的屏幕截图,即使它们后退。(请注意,这不适用于最小化的窗口。但是,如果您真的需要,也有一些解决方法。)

于 2012-08-14T00:26:41.983 回答
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)上,它不会捕获完全透明的边框。通常的透明边框将改为黑色。也许这对于你想要完成的事情已经足够了。

于 2012-08-16T13:32:53.457 回答