Catturare pagina web come immagine in C #, assicurando javascript reso gli elementi sono visibili

StackOverflow https://stackoverflow.com/questions/7803201

Domanda

Sto cercando di catturare la pagina seguente con codice C # .NET standard. Ho cercato in giro per i vari metodi della gente, la maggior parte delle quali coinvolgono un'istanza di un oggetto browser e utilizzando un pareggio per il metodo bitmap. Tuttavia, nessuno di questi raccogliere il contenuto della tabella in questa pagina:

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

Forse il javascript non ha il tempo di correre, ma aggiungendo Thread.Sleep (x) non ha assistito.

componente commerciale cattura in modo corretto, ma preferirei evitare che richiede una dipendenza aggiuntiva nel mio progetto e pagare $ 150 quando l'altro Le soluzioni sono sooo vicino!.

Chiunque trovano la loro soluzione rende questo correttamente?

È stato utile?

Soluzione

Hai forse provato IECapt . Credo che sia la strada giusta da percorrere. Ho creato una versione modificata di esso e utilizzare un timer invece di Thread.Sleep cattura il vostro sito come previsto.

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

Questa è la brutta fonte. Basta aggiungere un riferimento a Microsoft HTML Object Library.

E questo è l'utilizzo:

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

Altri suggerimenti

Thread.Sleep si limiterà a sospendere la discussione il browser Web è in esecuzione su - come ci si aspetta di rendere nulla quando si è sospeso? :)

Al contrario, è necessario per consentire al thread per processo di lavoro. È possibile raggiungere questo obiettivo con una combinazione di Thread.Sleep(0) e Application.DoEvents(), con qualcosa come il seguente:

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

@ L.B, grazie per l'aiuto!

Solo una cronaca per chi vuole correre in una libreria di classi, Browser web ha bisogno di Single Threaded Apartment, in modo da fare qualcosa di simile:

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

Poi il Gotcha, dopo che la chiamata è fatta navigare, aggiungere 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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top