Ver fuente generada (después de AJAX / JavaScript) en C #

¿Hay alguna manera de ver la fuente generada de una página web (el código después de todas las llamadas AJAX y las manipulaciones de DOM de JavaScript) desde una aplicación C # sin abrir un navegador desde el código?

Ver la página inicial usando un objeto WebRequest o WebClient funciona bien, pero si la página hace un uso extensivo de JavaScript para alterar el DOM en la carga de la página, entonces estos no proporcionan una imagen precisa de la página.

He intentado utilizar los marcos de prueba de la interfaz de usuario de Selenium y Watin y funcionan perfectamente, suministrando la fuente generada tal como aparece después de que se completen todas las manipulaciones de JavaScript. Desafortunadamente, lo hacen abriendo un navegador web real, que es muy lento. Implementé un servidor de selenium que descarga este trabajo a otra máquina, pero todavía hay un retraso considerable.

¿Hay una biblioteca .Net que cargará y analizará una página (como un navegador) y escupirá el código generado? Claramente, Google y Yahoo no están abriendo navegadores para cada página que quieren arañar (por supuesto, pueden tener más recursos que yo …).

¿Existe tal biblioteca o no tengo suerte a menos que esté dispuesto a diseccionar el código fuente de un navegador de código abierto?

SOLUCIÓN

Bueno, gracias a todos por su ayuda. Tengo una solución de trabajo que es aproximadamente 10 veces más rápida que el selenium. ¡Cortejar!

Gracias a este viejo artículo de beansoftware , pude usar el control System.Windows.Forms.WebBrowser para descargar la página y analizarla, y luego darles la fuente generada. Aunque el control está en Windows.Forms, aún puede ejecutarlo desde Asp.Net (que es lo que estoy haciendo), solo recuerde agregar System.Window.Forms a las referencias de su proyecto.

Hay dos cosas notables sobre el código. Primero, se llama al control WebBrowser en un nuevo hilo. Esto se debe a que debe ejecutarse en un único apartamento con rosca .

En segundo lugar, la variable GeneratedSource se establece en dos lugares. Esto no se debe a una decisión inteligente de diseño 🙂 Aún estoy trabajando en ello y actualizaré esta respuesta cuando haya terminado. wb_DocumentCompleted () se llama varias veces. Primero, cuando se descarga el código HTML inicial, cuando se completa la primera ronda de JavaScript. Desafortunadamente, el sitio que estoy raspando tiene 3 etapas de carga diferentes. 1) Cargue HTML inicial 2) Haga la primera ronda de manipulación DOM de JavaScript 3) pausa durante medio segundo y luego realice una segunda ronda de manipulación JS DOM.

Por alguna razón, la segunda ronda no es causada por la función wb_DocumentCompleted (), pero siempre se captura cuando wb.ReadyState == Complete. Entonces, ¿por qué no eliminarlo de wb_DocumentCompleted ()? Todavía no estoy seguro de por qué no está atrapado allí y ahí es donde el artículo de beadsoftware recomienda ponerlo. Voy a seguir investigando. Solo quería publicar este código para que cualquiera que esté interesado pueda usarlo. ¡Disfrutar!

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

posiblemente esté usando una instancia de un navegador (en su caso: el control ie). puedes usarlo fácilmente en tu aplicación y abrir una página. el control luego lo cargará y procesará cualquier javascript. Una vez hecho esto, puede acceder al objeto control dom y obtener el código “interpretado”.

La mejor forma es usar PhantomJs . Eso es genial. (muestra de eso es artículo ).

Mi solución es así:

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

Teóricamente sí, pero, en este momento, no.

No creo que haya actualmente un producto o proyecto de OSS que haga esto. Tal producto necesitaría tener su propio intérprete de JavaScript y ser capaz de emular con precisión el entorno de tiempo de ejecución y las peculiaridades de cada navegador que admita.

Dado que necesita algo que emule con precisión el entorno del servidor + navegador para producir el código de la página final, a la larga, creo que usar una instancia de navegador es la mejor manera de generar con precisión la página en su estado final. Esto es especialmente cierto cuando se considera que, una vez completada la carga de la página, las fonts de la página pueden cambiar con el tiempo en el navegador desde AJAX / javascript.