2009-12-30 16 views
43

Sto scrivendo un crawler e parser specializzato per uso interno e ho bisogno della possibilità di acquisire uno screenshot di una pagina Web per verificare quali colori vengono utilizzati in tutto. Il programma includerà una decina di indirizzi Web e li salverà come immagine bitmap.Ottiene automaticamente uno screenshot di una pagina

Da lì ho intenzione di utilizzare LockBits per creare un elenco dei cinque colori più utilizzati all'interno dell'immagine. Per quanto ne so, è il modo più semplice per ottenere i colori utilizzati all'interno di una pagina Web, ma se c'è un modo più semplice per farlo, si prega di inserire i suggerimenti.

In ogni caso, stavo per utilizzare ACA WebThumb ActiveX Control finché non ho visto il cartellino del prezzo. Sono anche abbastanza nuovo per C#, avendo usato solo per alcuni mesi. C'è una soluzione al mio problema di prendere uno screenshot di una pagina web al fine di estrarre la combinazione di colori?

+0

non l'ho provato (che è il motivo per cui questo è un commento, non una risposta), ma (http://www.dreamincode.net/code/snippet2539.htm) sembra essere una soluzione C# per salvare una pagina Web come una bitmap. –

+0

Quante pagine esegui la scansione al mese? – jjxtra

+0

Non molti, sto solo usando le immagini come mezzo per estrarre i dati, quindi se uno o due falliscono, non è un grosso problema. Finora non ho avuto problemi con questo, a parte il fatto che ha bisogno di usare Application.Run() per andare avanti. –

risposta

19

https://www.url2png.com/docs è un buon compromesso. Hanno un livello gratuito.

È necessario utilizzare HttpWebRequest per scaricare il file binario dell'immagine. Ecco un esempio:

HttpWebRequest request = HttpWebRequest.Create("https://api.url2png.com/v6/[API_KEY]/[API_TOKEN]/png/?url=[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... 

per generare l'URL ...

public static string url2png(string UrlToSite) 
{ 
    string url2pngAPIKey = "PXXX"; 
    string url2pngPrivateKey = "SXXX"; 

    string url = HttpUtility.UrlEncode(UrlToSite); 

    string getstring = "fullpage=true&url=" + url; 

    string SecurityHash_url2png = Md5HashPHPCompliant(url2pngPrivateKey + "+" + getstring).ToLower(); 

    var url2pngLink = "http://api.url2png.com/v6/" + url2pngAPIKey + "/" + SecurityHash_url2png + "/" + "png/?" + getstring; 

    return url2pngLink; 
} 

public static string Md5HashPHPCompliant(string pass) 
{ 
    System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); 

    byte[] dataMd5 = md5.ComputeHash(Encoding.UTF8.GetBytes(pass)); 
    StringBuilder sb = new StringBuilder(); 

    for (int i = 0; i <= dataMd5.Length - 1; i++) 
    { 
     sb.AppendFormat("{0:x2}", dataMd5[i]); 
    } 

    return sb.ToString(); 
} 
+0

Darò a questo metodo una prova e vedrò come influenza l'aspetto dell'estrazione dei dati. –

+0

@MikeB come aggiungere il codice di attivazione ad esso: | – confusedMind

+0

@PsychoDad, il mio stream ha più di 65.535 pixel. Cosa faccio?? – FabianoLothor

25

Un modo rapido e sporco sarebbe utilizzare il controllo WinForms WebBrowser e disegnarlo in una bitmap. Fare questo in un'app console standalone è un po 'complicato perché devi essere consapevole delle implicazioni dell'hosting di un controllo STAThread mentre usi un modello di programmazione fondamentalmente asincrono. Ma qui è una prova di lavoro di concetto che cattura una pagina web in un file BMP 800x600:

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

per compilare questo, creare una nuova applicazione console e assicurarsi di aggiungere i riferimenti di assemblaggio per System.Drawing e System.Windows.Forms.

UPDATE: Ho riscritto il codice per evitare di dover utilizzare il pattern WaitOne/DoEvents di polling degli hacky. Questo codice dovrebbe essere più vicino alle seguenti best practice.

UPDATE 2: Si indica che si desidera utilizzare questo in un'applicazione Windows Form. In tal caso, dimenticarsi di creare dinamicamente il controllo WebBrowser. Quello che vuoi è creare un'istanza nascosta (Visible = false) di uno WebBrowser nel tuo modulo e usarla nello stesso modo in cui mostro sopra. Ecco un altro esempio che mostra la parte del codice utente di un modulo con una casella di testo (webAddressTextBox), un pulsante (generateScreenshotButton) e un browser nascosto (webBrowser). Mentre stavo lavorando su questo, ho scoperto una particolarità che non ho gestito prima: l'evento DocumentCompleted può effettivamente essere generato più volte a seconda della natura della pagina. Questo esempio dovrebbe funzionare in generale, e si può allungare a fare quello che vuoi:

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

Ci scusiamo per l'enorme ritardo, il codice sembra funzionare bene, ma sto lottando con l'utilizzo all'interno di un modulo che ho. Probabilmente sto facendo qualcosa di stupido, ma se potessi darmi una mano con esso sarebbe molto apprezzato. –

+0

DrawToBitmap non è supportato e a volte fallirà, lasciando una bitmap bianca nera o bianca vuota – jjxtra

+0

@bobbymcr - Hai per caso un'idea del perché la pagina visualizzata dal controllo del browser IE abbia alcuni stili applicati in modo errato. –

1

check this fuori. Questo sembra fare ciò che volevi e tecnicamente si avvicina al problema in modo molto simile attraverso il controllo del browser web. Sembra che sia stata approvata una serie di parametri da passare e anche una buona gestione degli errori incorporata. L'unico svantaggio è che si tratta di un processo esterno (exe) che si genera e crea un file fisico che si leggerà in seguito. Dalla tua descrizione, consideri anche i servizi web, quindi non penso che sia un problema.

Nel risolvere il tuo ultimo commento su come elaborare più di essi contemporaneamente, questo sarà perfetto. Puoi generare un parallelo di 3, 4, 5 o più processi alla volta o avere l'analisi del bit di colore in esecuzione come thread mentre avviene un altro processo di acquisizione.

Per l'elaborazione delle immagini, recentemente mi sono imbattuto in Emgu, non l'ho usato da solo ma sembra affascinante. Sostiene di essere veloce e di avere un grande supporto per l'analisi grafica inclusa la lettura del colore dei pixel. Se avessi qualche progetto di elaborazione grafica a portata di mano, farò un tentativo.

1

si può anche avere uno sguardo a QT Jambi http://qt.nokia.com/doc/qtjambi-4.4/html/com/trolltech/qt/qtjambi-index.html

hanno una bella implementazione Java webkit base per un browser dove si può fare uno screenshot semplicemente facendo STH come:

QPixmap pixmap; 
    pixmap = QPixmap.grabWidget(browser); 

    pixmap.save(writeTo, "png"); 

Hanno uno sguardo ai campioni: hanno una bella demo per i webbrowser.

13

Esiste un grande browser basato su Webkit PhantomJS che consente di eseguire qualsiasi codice JavaScript dalla riga di comando.

installarlo dal http://phantomjs.org/download.html ed eseguire il seguente script di esempio da linea di comando:

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

Si creerà uno screenshot di una data pagina in file JPEG. L'aspetto positivo di questo approccio è che non ti affidi a nessun fornitore esterno e puoi facilmente automatizzare lo screenshot in grandi quantità.

+0

+1 essendo anche Webkit si sa che rende bene le moderne pagine Web –

+0

Uno strumento davvero grandioso, ma non rende molto bene la mia pagina che ha un sacco di jquery e slickgrid –

+0

Hmmm. simile a quello che si ottiene con qualsiasi altro browser Webkit –

1

Ho usato WebBrowser e non funziona perfettamente per me, specialmente quando è necessario attendere il rendering di JavaScript completo. Ho provato alcuni Api e trovato Selenium, la cosa più importante di Selenium è che non richiede STAThread e può essere eseguito in app per console semplici e Servizi.

fare un tentativo:

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

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

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

     driver.Quit(); 
    } 
} 
+0

L'estensione WebDriver deve essere installata, quindi funzionerà solo – jkyadav

11

Questa domanda è vecchio ma, in alternativa, è possibile utilizzare il pacchetto NuGet Freezer. È gratuito, utilizza un browser Web Gecko recente (supporta HTML5 e CSS3) e si trova solo in una dll.

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

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

Può funzionare con il servizio Windows per salvare il screenshot in una cartella? – Silver

+0

Questa è la soluzione migliore! –

+0

Ha funzionato benissimo per me! –

Problemi correlati