Catturare la pagina web come immagine in c #, assicurando che gli elementi resi javascript siano visibili

Sto cercando di catturare la seguente pagina usando il codice c # .net standard. Ho cercato in giro per vari metodi della gente, la maggior parte dei quali coinvolge l’istanziazione di un object browser e l’utilizzo di un metodo di disegno a bitmap. Tuttavia, nessuno di questi riprende il contenuto del grafico in questa pagina:

http://www.highcharts.com/demo/combo-dual-axes

Forse il javascript non ha tempo di funzionare, ma l’aggiunta di Thread.Sleep (x) non è stata assistita.

Questa componente commerciale la cattura correttamente, ma preferirei evitare di richiedere un’ulteriore dipendenza dal mio progetto e pagare $ 150 quando le altre soluzioni sono talmente vicine!

Qualcuno trova la sua soluzione lo rende correttamente?

Hai probabilmente provato IECapt . Penso che sia la strada giusta da percorrere. Ho creato una versione modificata di esso e uso un timer invece di Thread.Sleep cattura il tuo sito come previsto.

——MODIFICARE——

Ecco la brutta fonte. Basta aggiungere un riferimento alla Microsoft HTML Object Library .

E questo è l’uso:

 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); } } } 

e 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); } } 

Thread.Sleep semplicemente sospenderà il thread sul quale sta girando il tuo web browser – come ti aspetti che esegua il rendering di qualcosa quando viene sospeso? 🙂

Invece, è necessario consentire al thread di elaborare il lavoro. È ansible ottenere ciò con una combinazione di Thread.Sleep(0) e Application.DoEvents() , con qualcosa di simile al seguente:

 DateTime finish = DateTime.Now.AddSeconds(3); while (DateTime.Now < finish) { Application.DoEvents(); Thread.Sleep(0); } 

@ LB, grazie per l’aiuto!

Solo una FYI per chiunque desideri eseguirlo in una libreria di classi, WebBrowser ha bisogno di Single Threaded Apartment, quindi fai qualcosa di simile a questo:

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

Quindi il Gotcha, una volta effettuata la chiamata di navigazione, aggiungi questa riga di codice in modo da ottenere l’evento di navigazione completato:

  web.Navigate(Url); Application.Run(); 

Ho creato un pacchetto nuget per questo scopo https://github.com/dcumin39/RenderHighCharts/wiki