【问题标题】:Programmatically get a screenshot of a page以编程方式获取页面的屏幕截图
【发布时间】:2009-12-30 18:25:53
【问题描述】:

我正在编写一个专门的爬虫和解析器供内部使用,并且我需要能够截取网页的屏幕截图,以便检查整个过程中使用了哪些颜色。该程序将接收大约十个网址并将它们保存为位图图像。

从那里我计划使用 LockBits 来创建图像中最常用的五种颜色的列表。据我所知,这是获取网页中使用的颜色的最简单方法,但如果有更简单的方法,请提出您的建议。

无论如何,在我看到价格标签之前,我打算使用 ACA WebThumb ActiveX Control。我对 C# 也很陌生,只使用了几个月。有没有办法解决我截取网页以提取配色方案的问题?

【问题讨论】:

  • 没有尝试过(这就是为什么这是评论,而不是答案)但是(dreamincode.net/code/snippet2539.htm)似乎是将网页保存为位图的 C# 解决方案。
  • 你每个月爬多少页?
  • 不多,我只是将图像用作提取数据的一种手段,所以如果一两个失败,那也没什么大问题。到目前为止,我没有遇到任何问题,除了它需要使用 Application.Run() 继续前进。
  • 在这种情况下,我添加了一个我认为效果很好的答案,因为 WebBrowser.DrawToBitmap 非常不可靠。
  • 我在下面的答案中添加了另一个代码示例,以展示如何在 Windows 窗体应用程序中执行此操作。

标签: c# screenshot cutycapt iecapt


【解决方案1】:

一种快速而肮脏的方法是使用 WinForms WebBrowser 控件并将其绘制到位图上。在独立的控制台应用程序中执行此操作有点棘手,因为您必须了解在使用基本异步编程模式时托管STAThread 控件的含义。但这里有一个工作概念证明,它将网页捕获为 800x600 BMP 文件:

namespace WebBrowserScreenshotSample
{
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Threading;
    using System.Windows.Forms;

    class Program
    {
        [STAThread]
        static void Main()
        {
            int width = 800;
            int height = 600;

            using (WebBrowser browser = new WebBrowser())
            {
                browser.Width = width;
                browser.Height = height;
                browser.ScrollBarsEnabled = true;

                // This will be called when the page finishes loading
                browser.DocumentCompleted += Program.OnDocumentCompleted;

                browser.Navigate("https://stackoverflow.com/");

                // This prevents the application from exiting until
                // Application.Exit is called
                Application.Run();
            }
        }

        static void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // Now that the page is loaded, save it to a bitmap
            WebBrowser browser = (WebBrowser)sender;

            using (Graphics graphics = browser.CreateGraphics())
            using (Bitmap bitmap = new Bitmap(browser.Width, browser.Height, graphics))
            {
                Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                browser.DrawToBitmap(bitmap, bounds);
                bitmap.Save("screenshot.bmp", ImageFormat.Bmp);
            }

            // Instruct the application to exit
            Application.Exit();
        }
    }
}

要编译它,创建一个新的控制台应用程序并确保为System.DrawingSystem.Windows.Forms 添加程序集引用。

更新:我重写了代码以避免不得不使用 hacky 轮询 WaitOne/DoEvents 模式。此代码应该更接近于遵循最佳实践。

更新 2:您表示要在 Windows 窗体应用程序中使用它。在这种情况下,忘记动态创建WebBrowser 控件。您想要的是在您的表单上创建 WebBrowser 的隐藏 (Visible=false) 实例,并按照我上面显示的方式使用它。这是另一个示例,它显示了带有文本框 (webAddressTextBox)、按钮 (generateScreenshotButton) 和隐藏浏览器 (webBrowser) 的表单的用户代码部分。在我处理这个问题时,我发现了一个我以前没有处理过的特性—— DocumentCompleted 事件实际上可以根据页面的性质多次引发。这个示例应该可以正常工作,你可以扩展它来做任何你想做的事情:

namespace WebBrowserScreenshotFormsSample
{
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Windows.Forms;

    public partial class MainForm : Form
    {
        public MainForm()
        {
            this.InitializeComponent();

            // Register for this event; we'll save the screenshot when it fires
            this.webBrowser.DocumentCompleted += 
                new WebBrowserDocumentCompletedEventHandler(this.OnDocumentCompleted);
        }

        private void OnClickGenerateScreenshot(object sender, EventArgs e)
        {
            // Disable button to prevent multiple concurrent operations
            this.generateScreenshotButton.Enabled = false;

            string webAddressString = this.webAddressTextBox.Text;

            Uri webAddress;
            if (Uri.TryCreate(webAddressString, UriKind.Absolute, out webAddress))
            {
                this.webBrowser.Navigate(webAddress);
            }
            else
            {
                MessageBox.Show(
                    "Please enter a valid URI.",
                    "WebBrowser Screenshot Forms Sample",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);

                // Re-enable button on error before returning
                this.generateScreenshotButton.Enabled = true;
            }
        }

        private void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // This event can be raised multiple times depending on how much of the
            // document has loaded, if there are multiple frames, etc.
            // We only want the final page result, so we do the following check:
            if (this.webBrowser.ReadyState == WebBrowserReadyState.Complete &&
                e.Url == this.webBrowser.Url)
            {
                // Generate the file name here
                string screenshotFileName = Path.GetFullPath(
                    "screenshot_" + DateTime.Now.Ticks + ".png");

                this.SaveScreenshot(screenshotFileName);
                MessageBox.Show(
                    "Screenshot saved to '" + screenshotFileName + "'.",
                    "WebBrowser Screenshot Forms Sample",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);

                // Re-enable button before returning
                this.generateScreenshotButton.Enabled = true;
            }
        }

        private void SaveScreenshot(string fileName)
        {
            int width = this.webBrowser.Width;
            int height = this.webBrowser.Height;
            using (Graphics graphics = this.webBrowser.CreateGraphics())
            using (Bitmap bitmap = new Bitmap(width, height, graphics))
            {
                Rectangle bounds = new Rectangle(0, 0, width, height);
                this.webBrowser.DrawToBitmap(bitmap, bounds);
                bitmap.Save(fileName, ImageFormat.Png);
            }
        }
    }
}

【讨论】:

  • 抱歉,耽搁了这么久,代码似乎运行良好,但我很难在我拥有的表单中使用它。我可能在做一些愚蠢的事情,但如果你能帮我一把,我将不胜感激。
  • 不支持DrawToBitmap,有时会失败,留下空白的黑色或空白的白色位图
  • @bobbymcr - 你有没有想过为什么 IE 浏览器控件呈现的页面的某些样式应用不正确。
  • @Jenea:我不能说没有看到一个具体的例子。这可能取决于许多因素......
  • @bobbymcr 原来是IE控件的安全设置问题。如果我禁用 IE ESC,则页面将正常呈现。如果有办法禁用控件的 IE ESC 设置,但似乎不可能。
【解决方案2】:

https://screenshotlayer.com/documentation是我最近能找到的唯一免费服务...

您需要使用 HttpWebRequest 下载图像的二进制文件。有关详细信息,请参阅上面提供的网址。

HttpWebRequest request = HttpWebRequest.Create("https://[url]") as HttpWebRequest;
Bitmap bitmap;
using (Stream stream = request.GetResponse().GetResponseStream())
{
    bitmap = new Bitmap(stream);
}
// now that you have a bitmap, you can do what you need to do...

【讨论】:

  • 我会试试这个方法,看看它如何影响数据提取方面。
  • @PsychoDad,我的流有更多的 65,535 像素。我该怎么办??
  • @FabianoLothor 65535 像素是什么意思?没什么。您要截屏的网页尺寸是多少?
  • @PsychoDad,该页面包含更多的 100 个 PDF 页面。
  • 仅供参考 - Websnapr 的主页说他们的服务将停止。于 2016 年 17 月 12 日检查。
【解决方案3】:

这个问题很老,但是,或者,您可以使用 nuget 包Freezer。它是免费的,使用最新的 Gecko 网络浏览器(支持 HTML5 和 CSS3)并且仅存在于一个 dll 中。

var screenshotJob = ScreenshotJobBuilder.Create("https://google.com")
              .SetBrowserSize(1366, 768)
              .SetCaptureZone(CaptureZone.FullPage) 
              .SetTrigger(new WindowLoadTrigger()); 

 System.Drawing.Image screenshot = screenshotJob.Freeze();

【讨论】:

  • 能否配合windows服务将截图保存到文件夹中?
  • 这是最好的解决方案!
  • 非常适合我!
  • 如果工作正常,冰箱看起来很棒。但它总是崩溃:RemoteWorker 出现关机或导航错误或选定区域为 0 区域等错误。此外,它似乎不会再更新了。
【解决方案4】:

有一个很棒的基于 Webkit 的浏览器 PhantomJS,它允许从命令行执行任何 JavaScript。

http://phantomjs.org/download.html 安装它并从命令行执行以下示例脚本:

./phantomjs ../examples/rasterize.js http://www.panoramio.com/photo/76188108 test.jpg

它将在 JPEG 文件中创建给定页面的屏幕截图。这种方法的好处是您不依赖任何外部提供商,并且可以轻松地自动进行大量屏幕截图。

【讨论】:

  • +1 也是 Webkit,你知道它可以很好地呈现现代网页
  • 非常棒的工具,但它不能很好地呈现我的页面,它有很多 jquery 和 slickgrid。
  • 嗯。结果应该与您使用任何其他 Webkit 浏览器得到的结果相似
  • 您确定页面在 document.ready 事件之后没有加载任何内容吗?
【解决方案5】:

我使用了 WebBrowser,但它对我来说并不完美,尤其是在需要等待 JavaScript 渲染完成时。 我尝试了一些 Api(s),发现 Selenium,Selenium 最重要​​的是,它不需要 STAThread,可以在简单的控制台应用程序和服务中运行。

试一试:

class Program
{
    static void Main()
    {
        var driver = new FirefoxDriver();

        driver.Navigate()
            .GoToUrl("http://stackoverflow.com/");

        driver.GetScreenshot()
            .SaveAsFile("stackoverflow.jpg", ImageFormat.Jpeg);

        driver.Quit();
    }
}

【讨论】:

  • WebDriver 扩展应该被安装然后才可以工作。
  • 1) 浏览器应该预先安装在服务器上; 2)截图中只有部分页面,不清楚如何获取全页截图;
【解决方案6】:

检查this。这似乎可以满足您的需求,并且从技术上讲,它通过 Web 浏览器控制以非常相似的方式解决了问题。它似乎满足了要传入的一系列参数以及内置的良好错误处理。唯一的缺点是它是您生成的外部进程 (exe),它会创建一个您稍后将读取的物理文件。根据您的描述,您甚至考虑过网络服务,所以我认为这不是问题。

在解决您关于如何同时处理多个问题的最新评论时,这将是完美的。您可以在任何时候生成 3、4、5 或更多进程的并行,或者在另一个捕获进程发生时将颜色位分析作为线程运行。

对于图像处理,我最近遇到了Emgu,我自己没用过,但看起来很有趣。它声称速度很快,并且对图形分析有很多支持,包括读取像素颜色。如果我现在手头有任何图形处理项目,我会试一试。

【讨论】:

    【解决方案7】:

    你也可以看看 QT jambi http://qt.nokia.com/doc/qtjambi-4.4/html/com/trolltech/qt/qtjambi-index.html

    他们为浏览器提供了一个不错的基于 webkit 的 java 实现,您可以通过以下方式进行屏幕截图:

        QPixmap pixmap;
        pixmap = QPixmap.grabWidget(browser);
    
        pixmap.save(writeTo, "png");
    

    看看示例 - 他们有一个不错的网络浏览器演示。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-15
      • 1970-01-01
      • 1970-01-01
      • 2014-04-17
      • 1970-01-01
      相关资源
      最近更新 更多