Есть ли способ просмотреть сгенерированный источник веб-страницы (код после всех вызовов AJAX и манипуляций с DOM-документами JavaScript) из приложения С#, не открывая браузер из кода?
Просмотр начальной страницы с помощью WebRequest или WebClient объект работает нормально, но если на странице широко используется JavaScript для изменения DOM при загрузке страницы, то они не дают точного изображения страницы.
Я попытался использовать Selenium и Watin Framework UI, и они отлично работают, поставляя сгенерированный источник, поскольку он появляется после завершения всех операций JavaScript. К сожалению, они делают это, открывая фактический веб-браузер, который очень медленный. Я реализовал сервер selenium, который разгружает эту работу на другую машину, но есть существенная задержка.
Есть ли библиотека .Net, которая будет загружать и анализировать страницу (например, браузер) и выплевывать сгенерированный код? Очевидно, что Google и Yahoo не открывают браузеры для каждой страницы, которую они хотят использовать (конечно, у них может быть больше ресурсов, чем у меня...).
Есть ли такая библиотека или мне повезло, если я не хочу анализировать исходный код браузера с открытым исходным кодом?
Решение
Хорошо, спасибо вам всем за помощь. У меня есть рабочее решение, которое примерно в 10 раз быстрее, чем Selenium. Woo!
Благодаря этой старой статье из beansoftware я смог использовать элемент управления System.Windows.Forms.WebBrowser для загрузки страницы и ее анализа, затем дать em сгенерированный источник. Несмотря на то, что элемент управления находится в Windows.Forms, вы все равно можете запустить его из Asp.Net(это то, что я делаю), просто не забудьте добавить System.Window.Forms в ваши ссылки на проект.
В коде есть две заметные вещи. Во-первых, элемент управления WebBrowser вызывается в новом потоке. Это связано с тем, что он должен работать на однопоточной квартире.
Во-вторых, переменная GeneratedSource устанавливается в двух местах. Это не из-за разумного дизайнерского решения:) Я все еще работаю над этим и обновляю этот ответ, когда закончите. wb_DocumentCompleted() вызывается несколько раз. Сначала, когда загружается исходный HTML, затем снова, когда заканчивается первый раунд JavaScript. К сожалению, сайт, который я соскабливаю, имеет 3 разных этапа загрузки. 1) Загрузите исходный HTML 2) Сделайте первый раунд манипуляции с DOM JavaScript 3) приостановите на полсекунды, затем выполните второй раунд манипуляций с JS DOM.
По какой-то причине второй раунд не является причиной функции wb_DocumentCompleted(), но он всегда пойман, когда wb.ReadyState == Complete. Так почему бы не удалить его из wb_DocumentCompleted()? Я все еще не уверен, почему он не пойман там и что там, где рекомендована статья в beadsoftware. Я буду продолжать изучать его. Я просто хотел опубликовать этот код, чтобы любой, кто его заинтересовал, мог его использовать. Наслаждайтесь!
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;
}
}