Лучший способ сделать снимок экрана веб-страницы - программирование
Подтвердить что ты не робот

Лучший способ сделать снимок экрана веб-страницы

Каков наилучший способ сделать снимок экрана веб-страницы? На данный момент я просто запускаю экземпляр selenium firefox и используя 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.

Я забыл упомянуть. Необходимый скриншот должен быть выполнен, как это видно пользователю. Таким образом, снимок экрана должен иметь окно браузера и сайт внутри границ окна веб-браузера. Я не могу найти способ изменить режим съемки скриншота в WebDriver of selenium. WebDriver просто снимает скриншот страницы без какого-либо окна браузера.

4b9b3361

Ответ 1

Я бы рекомендовал 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
}

}

Отредактировано 8/1/12:

Получить код дескриптора приложения. Я уверен, дублируя код, который находится на 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/ также является отличным ресурсом.

Ответ 2

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

Мне лично нравится этот API. Создайте растровое изображение с шириной и высотой, вычисленное из возвращаемого прямоугольника API GetWindowRect и для использования параметра HDC (например):

thebitmap.GetHdc()

Вы должны быть в порядке.

Изменить: также проверьте этот.

Btw, вы можете сделать снимок экрана из любого окна, которое вам нравится, даже если они отпадут. (обратите внимание, что это не будет работать для свернутых окон. Однако, если вам действительно нужно, для этого тоже есть.)

Ответ 3

Если вы ищете программный способ получить скриншот главного окна данного процесса, вот что это делает:

    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) он не захватит полную прозрачную границу. Вместо этого обычная прозрачная рамка будет черной. Возможно, этого достаточно для того, что вы пытаетесь выполнить.

Ответ 4

Я использую webshotcmd (платная версия также командной строки) в производственном приложении в течение многих лет. Он может быть настроен на ожидание загрузки страницы, ждать через n секунд после загрузки страницы и т.д. Он использует Internet Explorer и работает в Windows. Начинается довольно быстро (по моему опыту, msie activex всегда мгновенно загружался).

Помимо вышеизложенного, я бы рекомендовал что-то на основе Webkit libray, оно было бы намного меньше, чем Firefox, и начнется очень быстро (wkhtmltoimage пока доступно только в Linux, но когда он будет доступен для Windows, Я бы пошел на это - тоже командная строка). Прямо сейчас просто google для скриншотов webkit (огромное количество доступных скриншотов, использующих webkit, заставляет меня полагать, что с помощью этой DLL будет легко переноситься на С#).

Изменить: учитывая второе редактирование, посмотрите Chrome Screen Capture. Чтобы попробовать, расширение доступно в галерее магазина/расширения.

Ответ 5

Я смог выполнить это, скопировав окно (по частям) в растровое изображение, которое установлено на размер ScrollRectangle для моего элемента управления webBrowser. Хотя это, конечно, не самый элегантный способ достижения этой цели, я хотел бы поделиться кодом на тот случай, если кто-то сможет его использовать. Как только у меня было что-то, что в основном работало, я смог добавить некоторые аргументы, и теперь я могу выполнить эту утилиту из командной строки:

Имя исполняемого_файла 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);
    }