c# - El control WebBrowser arroja una NullReferenceException aparentemente aleatoria
winforms exception-handling (2)
Durante un par de días estoy trabajando en un webscraper basado en WebBrowser. Después de un par de prototipos trabajando con Threads y eventos DocumentCompleted, decidí probar y ver si podía hacer un Webscraper simple y fácil de entender.
El objetivo es crear un Webscraper que no involucre objetos Thread reales. Quiero que funcione en pasos secuenciales (es decir, ir a la url, realizar una acción, ir a otra URL, etc., etc.).
Esto es lo que obtuve hasta ahora:
public static class Webscraper
{
private static WebBrowser _wb;
public static string URL;
//WebBrowser objects have to run in Single Thread Appartment for some reason.
[STAThread]
public static void Init_Browser()
{
_wb = new WebBrowser();
}
public static void Navigate_And_Wait(string url)
{
//Navigate to a specific url.
_wb.Navigate(url);
//Wait till the url is loaded.
while (_wb.IsBusy) ;
//Loop until current url == target url. (In case a website loads urls in steps)
while (!_wb.Url.ToString().Contains(url))
{
//Wait till next url is loaded
while (_wb.IsBusy) ;
}
//Place URL
URL = _wb.Url.ToString();
}
}
Soy un programador novato, pero creo que este es un código bastante sencillo. Es por eso que detesto el hecho de que por alguna razón el programa arroje una NullReferenceException en este fragmento de código:
_wb.Url.ToString().Contains(url)
Acabo de llamar al método _wb.Navigate () para que NullReference no pueda estar en el objeto _wb. Entonces, lo único que puedo imaginar es que el objeto _wb.Url es nulo. Pero el while _wb.IsBusy () loop debería evitar eso.
Entonces, ¿qué está pasando y cómo puedo solucionarlo?
Lo más probable es que la Url aún no esté configurada, puedes intentar:
//Loop until current url == target url. (In case a website loads urls in steps)
//while (!_wb.Url.ToString().Contains(url))
while (_wb.Url == null || !_wb.Url.ToString().Contains(url))
{
//Wait till next url is loaded
while (_wb.IsBusy) ;
}
Pero sigue siendo una solución escamosa sin sincronización real que podría tener otros problemas.
¿No hay un evento Loaded
o ''navegado'' que puedas manejar?
La espera ocupada ( while (_wb.IsBusy) ;
) en el hilo de UI no es muy aconsejable. Si usa las nuevas características async / await de .Net 4.5, puede obtener un efecto similar ( es decir, ir a la url, realizar una acción, ir a otra url, etc. ) que desee
public static class SOExtensions
{
public static Task NavigateAsync(this WebBrowser wb, string url)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
WebBrowserDocumentCompletedEventHandler completedEvent = null;
completedEvent = (sender, e) =>
{
wb.DocumentCompleted -= completedEvent;
tcs.SetResult(null);
};
wb.DocumentCompleted += completedEvent;
wb.ScriptErrorsSuppressed = true;
wb.Navigate(url);
return tcs.Task;
}
}
async void ProcessButtonClick()
{
await webBrowser1.NavigateAsync("http://www..com");
MessageBox.Show(webBrowser1.DocumentTitle);
await webBrowser1.NavigateAsync("http://www.google.com");
MessageBox.Show(webBrowser1.DocumentTitle);
}