Capturing webpage as image in c#, ensuring javascript rendered elements are visible

后端 未结 4 1581
南方客
南方客 2020-11-30 04:37

I am trying to capture the following page using standard c# .net code. I\'ve searched around for people\'s various methods, most of which involve instantiating a browser ob

相关标签:
4条回答
  • 2020-11-30 04:45

    Thread.Sleep will simply suspend the thread your web browser is running on - how do you expect it to render anything when it is suspended? :)

    Instead, you need to allow the thread to process work. You can achieve this with a combination of Thread.Sleep(0) and Application.DoEvents(), with something like the following:

    DateTime finish = DateTime.Now.AddSeconds(3);
    while (DateTime.Now < finish) {
        Application.DoEvents();
        Thread.Sleep(0);
    }
    
    0 讨论(0)
  • 2020-11-30 04:56

    @L.B , thank you for the help!

    Just an FYI for anyone wanting to run it in a class library, WebBrowser needs to Single Threaded Apartment, so do something like this:

      var t = new Thread(InitAndDo); //InitAndDo would have your code creating the webbrowser object etc...
      t.SetApartmentState(ApartmentState.STA);
      t.Start();
    

    Then the Gotcha, after the navigate call is done, add this line of code so that you get the completed navigation event:

        web.Navigate(Url);
        Application.Run();
    
    0 讨论(0)
  • 2020-11-30 04:59

    You have possibly tried IECapt. I think it is the right way to go. I created a modified version of it and use a timer instead of Thread.Sleep it captures your site as expected.

    ------EDIT------

    Here is the ugly source. Just Add a reference to Microsoft HTML Object Library.

    And this is the usage:

    HtmlCapture capture = new HtmlCapture(@"c:\temp\myimg.png");
    capture.HtmlImageCapture += new HtmlCapture.HtmlCaptureEvent(capture_HtmlImageCapture);
    capture.Create("http://www.highcharts.com/demo/combo-dual-axes");
    
    void capture_HtmlImageCapture(object sender, Uri url)
    {
        this.Close();
    }
    

    File1

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    
    
    namespace MyIECapt
    {
        public class HtmlCapture
        {
            private WebBrowser web;
            private Timer tready;
            private Rectangle screen;
            private Size? imgsize = null;
    
            //an event that triggers when the html document is captured
            public delegate void HtmlCaptureEvent(object sender, Uri url);
    
            public event HtmlCaptureEvent HtmlImageCapture;
    
            string fileName = "";
    
            //class constructor
            public HtmlCapture(string fileName)
            {
                this.fileName = fileName;
    
                //initialise the webbrowser and the timer
                web = new WebBrowser();
                tready = new Timer();
                tready.Interval = 2000;
                screen = Screen.PrimaryScreen.Bounds;
                //set the webbrowser width and hight
                web.Width = 1024; //screen.Width;
                web.Height = 768; // screen.Height;
                //suppress script errors and hide scroll bars
                web.ScriptErrorsSuppressed = true;
                web.ScrollBarsEnabled = false;
                //attached events
                web.Navigating +=
                  new WebBrowserNavigatingEventHandler(web_Navigating);
                web.DocumentCompleted += new
                  WebBrowserDocumentCompletedEventHandler(web_DocumentCompleted);
                tready.Tick += new EventHandler(tready_Tick);
            }
    
    
            public void Create(string url)
            {
                imgsize = null;
                web.Navigate(url);
            }
    
            public void Create(string url, Size imgsz)
            {
                this.imgsize = imgsz;
                web.Navigate(url);
            }
    
    
    
            void web_DocumentCompleted(object sender,
                     WebBrowserDocumentCompletedEventArgs e)
            {
                //start the timer
                tready.Start();
            }
    
            void web_Navigating(object sender, WebBrowserNavigatingEventArgs e)
            {
                //stop the timer   
                tready.Stop();
            }
    
    
    
            void tready_Tick(object sender, EventArgs e)
            {
                try
                {
                    //stop the timer
                    tready.Stop();
    
                    mshtml.IHTMLDocument2 docs2 = (mshtml.IHTMLDocument2)web.Document.DomDocument;
                    mshtml.IHTMLDocument3 docs3 = (mshtml.IHTMLDocument3)web.Document.DomDocument;
                    mshtml.IHTMLElement2 body2 = (mshtml.IHTMLElement2)docs2.body;
                    mshtml.IHTMLElement2 root2 = (mshtml.IHTMLElement2)docs3.documentElement;
    
                    // Determine dimensions for the image; we could add minWidth here
                    // to ensure that we get closer to the minimal width (the width
                    // computed might be a few pixels less than what we want).
                    int width = Math.Max(body2.scrollWidth, root2.scrollWidth);
                    int height = Math.Max(root2.scrollHeight, body2.scrollHeight);
    
                    //get the size of the document's body
                    Rectangle docRectangle = new Rectangle(0, 0, width, height);
    
                    web.Width = docRectangle.Width;
                    web.Height = docRectangle.Height;
    
                    //if the imgsize is null, the size of the image will 
                    //be the same as the size of webbrowser object
                    //otherwise  set the image size to imgsize
                    Rectangle imgRectangle;
                    if (imgsize == null) imgRectangle = docRectangle;
                    else imgRectangle = new Rectangle() { Location = new Point(0, 0), Size = imgsize.Value };
    
                    //create a bitmap object 
                    Bitmap bitmap = new Bitmap(imgRectangle.Width, imgRectangle.Height);
                    //get the viewobject of the WebBrowser
                    IViewObject ivo = web.Document.DomDocument as IViewObject;
    
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                        //get the handle to the device context and draw
                        IntPtr hdc = g.GetHdc();
                        ivo.Draw(1, -1, IntPtr.Zero, IntPtr.Zero,
                                 IntPtr.Zero, hdc, ref imgRectangle,
                                 ref docRectangle, IntPtr.Zero, 0);
                        g.ReleaseHdc(hdc);
                    }
                    //invoke the HtmlImageCapture event
                    bitmap.Save(fileName);
                    bitmap.Dispose();
                }
                catch 
                {
                    //System.Diagnostics.Process.GetCurrentProcess().Kill();
                }
                if(HtmlImageCapture!=null) HtmlImageCapture(this, web.Url);
            }
        }
    }
    

    and File2

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.Runtime.InteropServices;
    
    namespace MyIECapt
    {
        [ComVisible(true), ComImport()]
        [GuidAttribute("0000010d-0000-0000-C000-000000000046")]
        [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IViewObject
        {
            [return: MarshalAs(UnmanagedType.I4)]
            [PreserveSig]
            int Draw(
                [MarshalAs(UnmanagedType.U4)] UInt32 dwDrawAspect,
                int lindex,
                IntPtr pvAspect,
                [In] IntPtr ptd,
                IntPtr hdcTargetDev,
                IntPtr hdcDraw,
                [MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcBounds,
                [MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcWBounds,
                IntPtr pfnContinue,
                [MarshalAs(UnmanagedType.U4)] UInt32 dwContinue);
            [PreserveSig]
            int GetColorSet([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
               int lindex, IntPtr pvAspect, [In] IntPtr ptd,
                IntPtr hicTargetDev, [Out] IntPtr ppColorSet);
            [PreserveSig]
            int Freeze([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
                            int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze);
            [PreserveSig]
            int Unfreeze([In, MarshalAs(UnmanagedType.U4)] int dwFreeze);
        }
    }
    
    0 讨论(0)
  • 2020-11-30 05:06

    I created a nuget package for this purpose https://github.com/dcumin39/RenderHighCharts/wiki

    0 讨论(0)
提交回复
热议问题