WinForm 嵌入 WebView2 实战:手撸一个百度新闻排行榜

你搜“C# 爬百度新闻”,出来的教程十有八九是 HttpClient 一发一收,然后告诉你解析 HTML。

我跟你讲,这套路在 2026 年基本等于自欺欺人

不信?你 F12 看一下百度新闻首页的排行榜那块,数据根本不是写在初始 HTML 里的。全是 JS 异步加载、动态渲染出来的。你用 HttpClient 拿回来的源码,大概率只有一堆 div 壳子和 script 标签,排行榜数据?不存在的。

![百度新闻 F12 截图示意]

所以,别跟传统的 HttpWebRequest 死磕了。咱上正经浏览器内核 —— WebView2

踩坑警告:WebView2 不是老掉牙的 IE 内核 WebBrowser,它是基于 Chromium 的微软官方亲儿子。从 .NET Framework 4.6.2 到 .NET 8 都能用。我们项目环境是 .NET Framework 4.8 + Visual Studio 2022。

说白了,这玩意儿就是个“没有皮肤的小 Chrome”。你在里面加载百度新闻,它跟你在外面用 Chrome 打开是一模一样的渲染效果。那排行榜数据自然也就有了。

今天这篇文章,我们不整虚的,直接上代码,从零开始,把 WebView2 塞进 WinForm,再精准地把百度新闻排行榜那几行字给抠出来。

开搞:把 WebView2 请进 WinForm

首先,别想着去微软官网下载什么几十兆的 SDK,直接在 Nuget 里搞定最快。

第一步:建项目 & 装包

建一个 WinForms 项目(.NET Framework 4.8 或者 .NET 6/8 都行)。然后在“管理 NuGet 程序包”里搜 Microsoft.Web.WebView2,认准官方那个,点安装。

![Nuget 安装截图]

装完之后,你的工具箱里就会多出来一个 WebView2 控件。直接拖到 Form 上,然后改个名字,比如 webViewMain

第二步:初始化 & 导航

双击 Form 的 Load 事件,或者直接在构造函数里初始化。注意:必须调用 EnsureCoreWebView2Async 来初始化运行时环境,否则控件是白板。

using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;

private async void Form1_Load(object sender, EventArgs e)
{
    try
    {
        // 这一步会检查系统是否安装了 WebView2 运行时(Edge 浏览器自带)
        // 如果没装,它会弹窗提示下载,或者你也可以指定用户目录
        await webViewMain.EnsureCoreWebView2Async(null);
        
        // 加载百度新闻
        webViewMain.CoreWebView2.Navigate("https://news.baidu.com/");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"初始化 WebView2 跪了: {ex.Message}");
    }
}

跑起来看看,百度新闻首页是不是就在你的 WinForm 里了?到这里,你只是把浏览器搬进来了,还没拿到数据。

找准时机:啥时候动手“偷”数据?

很多人在这里翻车。直接在 NavigationCompleted 事件里取 HTML,取回来的数据往往缺胳膊少腿

为啥?因为 NavigationCompleted 只是代表页面框架加载完了,并不代表里面的 JS 把排行榜数据渲染出来了。那些排行榜 DOM 节点,可能过了 1 秒、2 秒才被 JS 塞进去。

错误示范(别这样写)

private void webViewMain_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
{
    // 这里立马去取 HTML,大概率取不到排行榜数据,或者取到空的 li 标签
    string html = await webViewMain.CoreWebView2.ExecuteScriptAsync("document.documentElement.outerHTML");
}

正确姿势:等它渲染完再动手。

怎么等?无脑 Task.Delay(3000) 太 low 了,网速快的时候浪费 3 秒,网速慢的时候 3 秒还不够。

我们需要精准等待。观察百度新闻首页的排行榜,它有一个特定的容器 ID 或 class,比如 #pane-news 下面的某个 ul。只要这个 ul 里面出现了 li,就说明数据到位了。

写一个智能等待函数

private async Task<bool> WaitForElement(string selector, int timeoutSeconds = 10)
{
    // 注入 JS 脚本,使用 MutationObserver 监听 DOM 变化,比轮询优雅多了
    string script = $@"
        new Promise((resolve) => {{
            const target = document.querySelector('{selector}');
            if (target && target.children.length > 0) {{
                resolve(true);
                return;
            }}
            const observer = new MutationObserver(() => {{
                const el = document.querySelector('{selector}');
                if (el && el.children.length > 0) {{
                    observer.disconnect();
                    resolve(true);
                }}
            }});
            observer.observe(document.body, {{ childList: true, subtree: true }});
            setTimeout(() => {{ observer.disconnect(); resolve(false); }}, {timeoutSeconds * 1000});
        }})
    ";

    string result = await webViewMain.CoreWebView2.ExecuteScriptAsync(script);
    // ExecuteScriptAsync 返回的是 JSON 字符串,比如 "true" 或 "false"
    return result.Trim('"') == "true";
}

然后在导航完成事件里调用它:

private async void webViewMain_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
{
    if (!e.IsSuccess)
    {
        Debug.WriteLine("导航失败");
        return;
    }

    // 等待百度新闻排行榜的列表容器出现(具体选择器你 F12 自己去抠)
    bool loaded = await WaitForElement("#pane-news .hotnews-list", 10);
    
    if (loaded)
    {
        // 元素就绪,开始抓取数据!
        await ExtractRankingData();
    }
}

动手抠数据:注入 JS 收割排行榜

现在页面数据已经齐全了。我们要把排行榜的标题和链接抠出来。这一步不用去解析复杂的 HTML 字符串,我们直接用 C# 调用 ExecuteScriptAsync 执行 JS,让 JS 去提取数据,返回一个 JSON 数组给 C#。

这是最干净、最不容易被反爬的写法,因为所有操作都是在浏览器上下文里执行的。

private async Task ExtractRankingData()
{
    // 这段 JS 会在 WebView2 内部的 DOM 环境运行
    string script = @"
        (function() {
            const items = document.querySelectorAll('#pane-news .hotnews-list li a');
            const result = [];
            items.forEach((el, index) => {
                // 只取前 10 条,防止数据太多
                if (index < 10) {
                    result.push({
                        title: el.innerText.trim(),
                        url: el.href
                    });
                }
            });
            return JSON.stringify(result);
        })();
    ";

    try
    {
        string jsonResult = await webViewMain.CoreWebView2.ExecuteScriptAsync(script);
        // 注意:返回的 jsonResult 外面包了一层双引号,需要去掉
        if (!string.IsNullOrEmpty(jsonResult))
        {
            string rawJson = jsonResult.Trim('"');
            // 这里你可以反序列化成 List,然后绑定到 DataGridView 或者 ListBox
            // 例如:var list = JsonConvert.DeserializeObject<List<RankItem>>(rawJson);
            
            // 简单展示:直接显示在 ListBox 里
            listBoxRanking.Items.Clear();
            dynamic rankList = Newtonsoft.Json.JsonConvert.DeserializeObject(rawJson);
            foreach (var item in rankList)
            {
                listBoxRanking.Items.Add($"{item.title} ({item.url})");
            }
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"提取数据翻车了: {ex.Message}");
    }
}

等等,这个方案有个坑

你可能会问,这样每次运行程序都要加载一次完整的百度首页,内存占用不小吧?

好问题。WebView2 一个实例大概占 100-200MB 内存,这确实比单纯发 HTTP 请求重得多。但是,对于需要 JS 渲染的数据,这是目前最稳、成本最低的方案

如果你非要说可以抓接口,那我告诉你,百度新闻排行榜那个接口参数加密得很恶心,你算签名算到怀疑人生,最终还得上浏览器环境。

我们项目里有个支付网关的监控后台,也是用类似的方式嵌了一个 WebView2 来显示实时数据大屏。上线第一周,运维小哥抱怨内存涨了 150MB。后来我们加了两个优化:

  1. 禁止加载图片:通过 CoreWebView2.Settings.AreImagesEnabled = false,内存直接掉了 50MB。
  2. 用完释放:如果不显示排行榜了,调用 webViewMain.Dispose() 释放资源。

总结一下这套组合拳

  1. 环境.NET Framework 4.8 + Microsoft.Web.WebView2 (v1.0.2592.51)。
  2. 核心痛点:动态网页数据拿不到,用 HttpClient 抓瞎。
  3. 解决思路:用 WebView2 模拟真实浏览器加载,然后通过 ExecuteScriptAsync 注入 JavaScript 操作 DOM 提取数据。
  4. 避坑指南:不要在 NavigationCompleted 里直接取数据,一定要等 JS 渲染完成,利用 MutationObserver 或定时轮询监听目标元素出现。

这个方案的局限性也很明显:比纯 HTTP 请求慢(大概慢 2-3 秒),资源占用高。但它解决了可行性问题 —— 有些数据,你不用浏览器就是拿不到。

别跟我扯什么无头浏览器 Headless,那玩意儿在 Windows 服务器上配置起来能让你吐血。WebView2 依托 Edge 运行时,在 Win 环境下是原生支持,稳得一匹。

照这个思路,你不仅能爬百度新闻,任何 SPA(单页应用)、Vue/React 渲染的页面,只要是你浏览器能看到的,WebView2 都能给你抠出来。

本文链接:https://www.biyeyuanma.cn/post/128.html

猜你喜欢

随机文章
热门标签
图片名称

服务热线

加我微信

加我微信