16

Web ページのスクリーンショットを撮る最良の方法は何ですか? 現時点では、firefox のセレン インスタンスを起動し、winapi を使用して前面に表示し、スクリーンショットを作成します。 私はすでに同様の質問をします。

2 つのポイントがあります。

  • 遅さ。
  • ウィンドウがたまたま Web ブラウザのウィンドウより高くなった場合、このウィンドウがスクリーンショットに表示されます。

より「プログラム的に」スクリーンショットを撮る方法はありますか?

ここに私が今使っているコードがあります:

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 のサイズに設定されたビットマップにウィンドウを (1 つずつ) コピーすることで実現できました。これは確かにこの目標を達成するための最も洗練された方法ではありませんが、誰かが使用できる場合に備えてコードを共有したいと思いました. ほとんどが機能しているものを取得したら、いくつかの引数を追加できるようになり、コマンド ラインからこのユーティリティを実行できるようになりました。

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

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

私は何年もの間、本番アプリでwebshotcmd (有料版もコマンド ラインです) を使用しています。ページがロードされるまで待機する、ページがロードされてから n 秒待機するなどのように構成できます。Internet Explorer を使用し、Windows で動作します。非常に高速に起動します (私の経験では、msie activex は常にすぐに読み込まれます)。

上記以外に、Webkit ライブラリに基づくものをお勧めします。これは Firefox よりもはるかに小さく、非常に高速に起動します (wkhtmltoimage は今のところ Linux でのみ使用できますが、Windows で使用できるようになったら、それを試してみてください-コマンドラインでもあります)。現在、webkit のスクリーンショットを検索するだけです (webkit を使用する利用可能なスクリーンショット作成者は膨大な数にのぼるため、その DLL を使用すると C# に簡単に移植できると思います)。

編集: 2 番目の編集を考慮して、Chrome スクリーン キャプチャソースを見てください。試すには、ストア/拡張機能ギャラリーで拡張機能を利用できます。

于 2012-08-16T15:16:14.047 に答える
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 を搭載した OS (Vista/Win7/Win8) では、完全に透明な境界線をキャプチャすることはできません。代わりに、通常の透明な境界線が黒くなります。たぶん、あなたが達成しようとしているものには十分です。

于 2012-08-16T13:32:53.457 に答える