2009-08-20 9 views
25

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

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

He intentado utilizar Selenium y Watin UI pruebas de marcos y funcionan perfectamente, suministrando la fuente generada 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 selenio 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 la ayuda que eres. Tengo una solución de trabajo que es aproximadamente 10 veces más rápida que el selenio. ¡Cortejar!

Gracias a esto old article from 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 single threaded apartment.

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

Se podría tratar de cortar las fuentes de Firebug. –

+0

Mi intento habría sido con Watin y amigos también. Gran pregunta! – orip

+0

Intenta ejecutar el código en "http://www.host.com/path/page.html?ast=3" o "http://gwt.google.com/samples/Showcase/Showcase.html". Notarás que no busca el HTML correcto. ¿Hay alguna idea de cómo arreglar eso? – Cosmo

Respuesta

4

es posible que esté usando una instancia de un navegador (en este 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".

+0

eso es lo que hace Watin – orip

+0

¿No tendría esto los mismos problemas de velocidad que abrir el navegador? –

+0

dado que desea que su código sea interpretado + analizado, el "problema" de velocidad sería bastante similar (tal vez un poco menos en la CPU si no muestra la ventana + tiene un poco menos de sobrecarga). Por lo que recuerdo, también puedes evitar que el ocntrol cargue imágenes, reduciendo así el tiempo de carga aún más. Pero esa es la única forma en que puede lograr lo que quiere Tengo miedo – Niko

1

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

No creo que actualmente haya un proyecto de producto o OSS que lo haga. 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 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 la página con precisión en su versión final estado. Esto es especialmente cierto cuando se considera que, una vez completada la carga de la página, las fuentes de la página pueden cambiar con el tiempo en el navegador desde AJAX/javascript.

+0

Puede que tengas razón, y gracias por la idea. Encontré una biblioteca Java que puede ser lo que necesito, pero sigo esperando una solución .net. Seguramente alguien más ha necesitado esto antes que yo: http://stackoverflow.com/questions/857515/screen-scraping-from-a-web-page-with-a-lot-of-javascript/857630#857630 –

2

La mejor manera es usando PhantomJs. Eso es genial. (muestra de eso es Article).

Mi solución es tener este aspecto:

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

Deberías en al menos agregue alguna parte del código, y explique más que esto. –

Cuestiones relacionadas