我需要在paralel中抓取很多页面,而我的UI线程不能被阻止。我正在为每个页面(url)创建线程,并在该线程中实例化webBrowser控件以执行javascript并在此之后获取html。当webBrowser在UI线程上获取html我引发事件以注册浏览器已完成其工作时,因为我想知道所有浏览器何时获取了html所以我可以合并所有数据并显示它。
1.)第一个探测器是,有些线程从不引发事件,所以我等不及了。
2.)第二个问题是我无法在不导致外部浏览器触发的情况下处理浏览器,总是在浏览器下拉地毯,所以他决定继续在用户默认浏览器中打开页面我想。但如果不进行处理,我就会用尽公羊。
我一直在寻找,发现了很多相关的东西,但我没有为我的用例实现它。这是我的代码:
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public partial class Form1 : Form
{
public delegate void ThreadFinishedEventHandler(object source, EventArgs e);
public event ThreadFinishedEventHandler threadFinishedEvent;
int threadCount = 0;
int threadReturnedCount = 0;
List<string> linksGlobal;
public Form1()
{
InitializeComponent();
threadFinishedEvent += new ThreadFinishedEventHandler(OnThreadFinished);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnGO_Click(object sender, EventArgs e)
{
scrapeLinksWithBrowsersInSeparateThreads();
}
private void scrapeLinksWithBrowsersInSeparateThreads()
{
linksGlobal = getLinks(); //10 urls all the same -> https://sports.betway.com
threadCount = linksGlobal.Count;
Random rand = new Random(123);
int waitTime = 0;//trying not to be registered as DOS attack or smth
foreach (string url in linksGlobal)
{
runBrowserThread(url, waitTime);
waitTime += rand.Next(500, 3000) + 500;//each browser will start navigating withing 1 - 4 seconds interval from each other
}
}
public void runBrowserThread(string url, int waitTime)
{
var th = new Thread(() =>
{
try
{
WebBrowserDocumentCompletedEventHandler completed = null;
WebBrowser wb = new WebBrowser();
completed = (sndr, e) =>
{
if (e.Url.AbsolutePath != (sndr as WebBrowser).Url.AbsolutePath)
{
wb.DocumentCompleted -= completed;
string html = (sndr as WebBrowser).Document.Body.InnerHtml;
threadFinishedEvent.Raise(this, EventArgs.Empty); // I have EventExtension allowing me this
//wb.Dispose(); //whenever and wherever I put this it causes external browser to fire
// Application.ExitThread(); //this sometimes seems to cause event never firing, not shure
}
};
wb.DocumentCompleted += completed;
wb.ScriptErrorsSuppressed = true;
Thread.Sleep(waitTime); //tryin not to get registerd as DOS attck or smth, each browser will start navigating withing 1 - 4 seconds interval from each other
wb.Navigate(url);
Application.Run();
}
catch (Exception ex)
{
throw ex;
}
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
private void OnThreadFinished(object source, EventArgs e)
{
threadReturnedCount++; // i get this for smth like 3 - 5 out od 11 threads, then this event stops being raised, dunno why
if (threadReturnedCount == threadCount)
{
// Do work
//this never happens cos a lot of threads never raise event, some do
}
}
private List<string> getLinks()
{
List<string> links = new List<string>();
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
links.Add("https://sports.betway.com");
return links;
}
}
P.S。来自线程的returnign数据是单独的问题,我还没有实现它,但首先我要解决这个问题。我将使用将从每个线程调用的objectFactory,如Factory.createObject(html),我将不得不在该Factory上使用某种锁定,因为它将位于主线程上。
答案 0 :(得分:0)
我无法找到问题中提出的问题的清晰解决方案。我确实尝试了一些东西,但确实得到了一些结果,但这还不够好。我将回顾我的问题并解释我最终做了什么来解决我的问题。
1.)第一个探测器是,有些线程从不引发事件,所以我等不及了。
答案1:仍然没有回应这里发生的事情,但是在我(有点)解决了第二个问题之后,这变得更好了
2.)第二个问题是我无法在不导致外部浏览器触发的情况下处理浏览器
答案2:现在可以通过使用Web浏览器控件的ActiveXInstance来完成,你需要将SHDocvW dll包含到你的项目中。 在这里查看Frank_FC的答案 Detect WebBrowser complete page loading
webBrowser控件也存在内存泄漏问题。使用谷歌我发现了如何减少这个问题(有很多信息)。
最后,整个事情不是很稳定,内存泄漏仍然发生,我会失去内存异常,不可预测的行为,糟糕的性能(缓慢的页面加载)等等。代码只是丑陋和一切似乎......不是正确的做事方式。 如果要在短时间内抓取大量页面,请不要使用webBrowser控件。不要在她自己的thead中实例化几十个不可见的webBrowser控件,并期望有效地处理所有事件。
我到底做了什么? 和我的朋友喝了啤酒后,他向我展示了他作为大学任务所做的计划。在Eclipse中使用JSoup包开发的Java程序用于抓取web。 Java中的2个函数,每个函数10 - 20行代码,他得到的速度比我快100倍,更简单,更好的解决方案。你只是说getHtml(url)和JSoup为你得到它,如果页面运行javascript或任何东西都没关系,疯了。
所以现在我的.NET应用程序正在启动java应用程序,它将html写入磁盘上的文本文件中,当它完成时,.NET应用程序收集数据,一遍又一遍地循环使用。
花了100多个小时摆弄webBrowser控件,然后在2小时内制作出了无法估量的更好的解决方案。 明智地选择你的工具! Java + Eclipse + JSoup似乎比.NET
更好地进行抓取/爬行