2009-08-20 18 views
25

C'è un modo per visualizzare la sorgente generata di una pagina Web (il codice dopo che tutte le chiamate AJAX e le manipolazioni del DOM JavaScript hanno avuto luogo) da un'applicazione C# senza aprire una browser dal codice?Visualizza origine generata (dopo AJAX/JavaScript) in C#

La visualizzazione della pagina iniziale utilizzando un oggetto WebRequest o WebClient funziona correttamente, ma se la pagina utilizza ampiamente JavaScript per modificare il DOM al caricamento della pagina, questi non forniscono un'immagine accurata della pagina.

Ho provato con Selenium e Watin framework di test UI e funzionano perfettamente, fornendo la sorgente generato come appare dopo tutte JavaScript manipolazioni sono completati. Sfortunatamente, lo fanno aprendo un vero browser web, che è molto lento. Ho implementato un server di selenio che scarica questo lavoro su un'altra macchina, ma c'è ancora un notevole ritardo.

C'è una libreria .Net che caricherà e analizzerà una pagina (come un browser) e sputerà il codice generato? Chiaramente, Google e Yahoo non aprono i browser per ogni pagina che vogliono spider (ovviamente potrebbero avere più risorse di me ...).

Esiste una tale libreria o sono sfortunato a meno che non sia disposto a analizzare il codice sorgente di un browser open source?

SOLUZIONE

Bene, grazie a tutti per l'aiuto che sei. Ho una soluzione di lavoro che è circa 10 volte più veloce del selenio. Corteggiare!

Grazie a questo old article from beansoftware sono riuscito a utilizzare il controllo System.Windows.Forms.WebBrowser per scaricare la pagina e analizzarla, quindi fornire la sorgente generata. Anche se il controllo è in Windows.Forms, è ancora possibile eseguirlo da Asp.Net (che è quello che sto facendo), basta ricordare di aggiungere System.Window.Forms ai riferimenti del progetto.

Ci sono due cose notevoli sul codice. Innanzitutto, il controllo WebBrowser viene chiamato in una nuova discussione. Questo perché deve essere eseguito su un single threaded apartment.

In secondo luogo, la variabile GeneratedSource è impostata in due posizioni. Questo non è dovuto a una decisione di progettazione intelligente :) Ci sto ancora lavorando e aggiornerò questa risposta quando ho finito. wb_DocumentCompleted() è chiamato più volte. Innanzitutto quando viene scaricato l'HTML iniziale, quindi di nuovo quando viene completato il primo round di JavaScript. Sfortunatamente, il sito che sto analizzando ha 3 diverse fasi di caricamento. 1) Carica l'HTML iniziale 2) Effettua il primo giro di manipolazione del DOM di JavaScript 3) sospendi per mezzo secondo poi esegui un secondo round di manipolazione del JS DOM.

Per qualche motivo, il secondo round non è causato dalla funzione wb_DocumentCompleted(), ma viene sempre rilevato quando wb.ReadyState == Complete. Quindi, perché non rimuoverlo da wb_DocumentCompleted()? Non sono ancora sicuro del motivo per cui non è stato catturato lì ed è qui che l'articolo del software dei perline ha raccomandato di inserirlo. Continuerò a esaminarlo. Volevo solo pubblicare questo codice in modo che chiunque sia interessato possa usarlo. Godere!

using System.Threading; 
using System.Windows.Forms; 

public class WebProcessor 
{ 
    private string GeneratedSource{ get; set; } 
    private string URL { get; set; } 

    public string GetGeneratedHTML(string url) 
    { 
     URL = url; 

     Thread t = new Thread(new ThreadStart(WebBrowserThread)); 
     t.SetApartmentState(ApartmentState.STA); 
     t.Start(); 
     t.Join(); 

     return GeneratedSource; 
    } 

    private void WebBrowserThread() 
    { 
     WebBrowser wb = new WebBrowser(); 
     wb.Navigate(URL); 

     wb.DocumentCompleted += 
      new WebBrowserDocumentCompletedEventHandler(
       wb_DocumentCompleted); 

     while (wb.ReadyState != WebBrowserReadyState.Complete) 
      Application.DoEvents(); 

     //Added this line, because the final HTML takes a while to show up 
     GeneratedSource= wb.Document.Body.InnerHtml; 

     wb.Dispose(); 
    } 

    private void wb_DocumentCompleted(object sender, 
     WebBrowserDocumentCompletedEventArgs e) 
    { 
     WebBrowser wb = (WebBrowser)sender; 
     GeneratedSource= wb.Document.Body.InnerHtml; 
    } 
} 
+1

Si potrebbe provare a incidere fonti Firebug. –

+0

Il mio tentativo sarebbe stato anche con Watin e amici. Ottima domanda! – orip

+0

Prova a eseguire il tuo codice con "http://www.host.com/path/page.html?ast=3" o "http://gwt.google.com/samples/Showcase/Showcase.html". Noterai che non recupera l'HTML corretto. Qualche idea su come risolverlo? – Cosmo

risposta

4

è possibile utilizzare un'istanza di un browser (nel tuo caso: il controllo cioè). puoi facilmente utilizzare nella tua app e aprire una pagina. il controllo lo caricherà e elaborerà qualsiasi javascript. una volta eseguita questa operazione è possibile accedere all'oggetto dom controls e ottenere il codice "interpretato".

+0

questo è ciò che Watin fa – orip

+0

Non avrebbe ancora gli stessi problemi di velocità dell'apertura del browser? –

+0

poiché vuoi che il tuo codice venga interpretato + analizzato, il "problema" di velocità sarebbe praticamente lo stesso (forse un po 'meno sulla CPU se non visualizzi la finestra + hai un po' meno di overhead). Per quanto mi ricordi, puoi anche evitare che l'ocntrol carichi immagini, riducendo così ulteriormente il tempo di caricamento. Ma questo è l'unico modo in cui puoi realizzare ciò che vuoi ho paura – Niko

1

Teoricamente sì, ma, allo stato attuale, no.

Non penso che al momento esista un prodotto o un progetto OSS. Un tale prodotto avrebbe bisogno di avere il proprio interprete javascript ed essere in grado di emulare accuratamente l'ambiente di runtime e le stranezze di ogni browser che supporta.

Dato che è necessario qualcosa che emula accuratamente l'ambiente server + browser per produrre il codice finale della pagina, a lungo termine, penso che l'utilizzo di un'istanza del browser sia il modo migliore per generare accuratamente la pagina nel suo finale stato. Ciò è particolarmente vero, se si considera che, al termine del caricamento della pagina, le origini della pagina possono comunque cambiare nel tempo nel browser da AJAX/javascript.

+0

Forse hai ragione e grazie per il pensiero. Ho trovato una libreria Java che potrebbe essere ciò di cui ho bisogno, ma spero ancora in una soluzione .net. Sicuramente qualcun altro ha bisogno di questo prima di me: http://stackoverflow.com/questions/857515/screen-scraping-from-a-web-page-with-a-lot-of-javascript/857630#857630 –

2

Il modo migliore è utilizzare PhantomJs. È fantastico. (esempio di questo è Article).

La mia soluzione è simile a questa:

var page = require('webpage').create(); 

page.open("https://sample.com", function(){ 
    page.evaluate(function(){ 
     var i = 0, 
     oJson = jsonData, 
     sKey; 
     localStorage.clear(); 

     for (; sKey = Object.keys(oJson)[i]; i++) { 
      localStorage.setItem(sKey,oJson[sKey]) 
     } 
    }); 

    page.open("https://sample.com", function(){ 
     setTimeout(function(){ 
     page.render("screenshoot.png") 
      // Where you want to save it  
      console.log(page.content); //page source 
      // You can access its content using jQuery 
      var fbcomments = page.evaluate(function(){ 
       return $("body").contents().find(".content") 
      }) 
      phantom.exit(); 
     },10000) 
    });  
}); 
+0

Si dovrebbe in aggiungere almeno una parte del codice e spiegarne di più. –

Problemi correlati