You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

循环爬取约38个URL后WebBrowser脚本卡顿无响应问题排查

哥们,我瞅了你的代码和问题,爬个38个URL就卡崩失去响应,大概率是这几个坑在搞鬼:WebBrowser控件本身是给网页渲染用的,不是爬虫工具,再加上你的等待逻辑写得太粗暴,COM资源没彻底清干净,还有网页里的脚本拖慢速度,积少成多就扛不住了。

问题根源拆解
  • WebBrowser的先天不足:这玩意儿基于IE内核,爬取时会加载并执行网页里的所有脚本、样式、图片,完全是做无用功,既浪费内存CPU,还容易被复杂脚本卡死。
  • 等待逻辑太离谱:你的waitTillLoad里嵌套了好几个死循环,靠Application.DoEvents()和Thread.Sleep()硬等,这种方式很容易搞乱消息队列,不仅没法准确判断页面是否加载完成,还会把CPU占满。
  • COM资源没清干净:虽然你调用了Marshal.ReleaseComObject,但WebBrowser本身的COM资源可能没彻底释放,循环创建控件后内存泄漏越来越严重,到一定数量就炸了。
解决方案

1. 直接换掉WebBrowser(最推荐,一劳永逸)

爬虫根本不需要渲染页面,用HttpClient直接抓HTML源码,再交给HtmlAgilityPack解析就行,速度快N倍,还完全避开脚本问题。代码改成这样:

using System.Net.Http;
using HtmlAgilityPack;
using System.Threading.Tasks;

public async Task<HtmlDocument> GetPageSourceAsync(string url)
{
    var htmlDoc = new HtmlDocument();
    // 复用HttpClient比每次创建更高效,也可以把HttpClient做成单例
    using (var httpClient = new HttpClient())
    {
        try
        {
            // 加个User-Agent模拟浏览器,避免被网站反爬
            httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36");
            // 异步获取网页内容,不阻塞主线程
            var htmlContent = await httpClient.GetStringAsync(url);
            htmlDoc.LoadHtml(htmlContent);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"爬取 {url} 出错: {ex.Message}");
        }
    }
    return htmlDoc;
}

用这个方法,爬几百个URL都不会卡,还能异步执行,效率拉满。

2. 非要用WebBrowser?那得彻底优化

如果因为某些特殊需求必须用WebBrowser,那得把这几个地方改了:

a. 直接禁用网页脚本

脚本是拖慢速度的元凶,在导航前加上这段代码,阻止脚本运行:

webBrowserCtrl.ScriptErrorsSuppressed = true;
// 注册DocumentCompleted事件,加载完成后立即停止脚本
webBrowserCtrl.DocumentCompleted += (sender, e) =>
{
    var doc = webBrowserCtrl.Document as mshtml.IHTMLDocument2;
    if (doc != null)
    {
        // 停止页面所有脚本执行
        doc.parentWindow.execScript("window.stop();", "javascript");
    }
};
webBrowserCtrl.Navigate(url);

b. 用事件代替死循环等待

别再用嵌套死循环硬等了,用DocumentCompleted事件来判断页面加载完成,这才是正确姿势:

private void WaitForPageLoad(WebBrowser browser, string targetUrl, int timeoutSeconds = 10)
{
    var loadCompleted = new TaskCompletionSource<bool>();
    WebBrowserDocumentCompletedEventHandler handler = null;

    handler = (sender, e) =>
    {
        // 确保是目标URL加载完成(避免iframe加载触发事件)
        if (e.Url.AbsoluteUri.Equals(targetUrl, StringComparison.OrdinalIgnoreCase))
        {
            browser.DocumentCompleted -= handler;
            loadCompleted.SetResult(true);
        }
    };

    browser.DocumentCompleted += handler;
    // 设置超时,避免无限等待
    if (!loadCompleted.Task.Wait(TimeSpan.FromSeconds(timeoutSeconds)))
    {
        browser.DocumentCompleted -= handler;
        browser.Stop();
        throw new TimeoutException($"加载 {targetUrl} 超时");
    }
}

然后在GetPageSource里调用这个方法,代替原来的waitTillLoad。

c. 彻底清理资源

除了释放COM对象,还要先停止导航再销毁控件,而且别每次爬取都调用GC,频繁GC反而会拖慢速度,比如每爬20个URL调用一次就行:

finally
{
    if (documentAsIHtmlDocument != null)
    {
        Marshal.ReleaseComObject(documentAsIHtmlDocument);
        documentAsIHtmlDocument = null;
    }
    content?.Dispose();
    // 先停止导航再销毁控件
    webBrowserCtrl.Stop();
    webBrowserCtrl.Dispose();
    // 不要每次都GC,比如每爬20个URL调用一次
    // if (爬取次数 % 20 == 0)
    // {
    //     GC.Collect();
    //     GC.WaitForPendingFinalizers();
    // }
}
总结

最省心的就是直接换成HttpClient+HtmlAgilityPack,完全告别WebBrowser的各种坑。如果非要用WebBrowser,那一定要禁用脚本、用事件等待加载、做好资源清理。

内容的提问来源于stack exchange,提问作者GarRock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:01:46