【问题标题】:Cannot get HTML source of browser web page, async to sync problem无法获取浏览器网页的 HTML 源代码,异步同步问题
【发布时间】:2020-12-05 18:21:41
【问题描述】:

Visual Studio 2017、Windows 10、.NET Framework 4.8、CefSharp WinForms 83.4.20、平台目标 x64

创建了新的非常简单的 CefSharp Windows Forms 应用程序。我无法获取网页的 html 源代码。我想我已经查看了 StackOverflow 上的每个 CefSharp 和异步同步问题 - 尝试了很多解决方案 - 我的头变得糊涂了。这是我看到的第一个问题 - 我有同样的问题。

Get HTML source code from CefSharp web browser

'浏览器.ViewSource();' 确实会弹出一个带有网页来源的记事本。但是当我尝试使用源代码获取字符串时 - 任务似乎永远不会运行。运行以获取网页源的任务说 ---> Status = WaitingForActivation ---> 并且永远不会返回源。

我已经尝试过异步到同步的转换——可能有十种不同的方式。没有工作。一个 StackOverflow 解决方案建议使用 Application.DoEvents() - 所以我什至尝试过。

希望有人有一些想法。这个浏览器似乎有很大的潜力 - 但我需要获取网页源 html。

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using System.Diagnostics;
namespace Test1
{
    public partial class Form1 : Form
    {
        public ChromiumWebBrowser browser;
        public Form1()
        {
            InitializeComponent();
            InitBrowser();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            browser.Dispose();
            Cef.Shutdown();
        }
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
        public void InitBrowser()
        {
            Cef.Initialize(new CefSettings());
            browser = new ChromiumWebBrowser("https://google.com/");
            this.Controls.Add(browser);
            browser.Dock = DockStyle.Fill;
            browser.FrameLoadEnd += OnWebBrowserFrameLoadEnded;
        }
        void OnWebBrowserFrameLoadEnded(object sender, FrameLoadEndEventArgs e)
        {
            ChromiumWebBrowser BrowserSender = (ChromiumWebBrowser)sender;
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(() => { WebBrowserFrameLoadEnded(BrowserSender, e); }));
            }
            else
            {
                WebBrowserFrameLoadEnded(BrowserSender, e);
            }
        }
        void WebBrowserFrameLoadEnded(ChromiumWebBrowser BrowserSender, FrameLoadEndEventArgs e)
        {
            string html1 = null;
            Task<String> taskString1;

            if (e.Frame.IsMain)
            {
                //browser.ViewSource();
                taskString1 = Task.Run(() => GetBrowserSource(browser));
                while (taskString1.Status != TaskStatus.RanToCompletion)
                {
                    Application.DoEvents();
                    System.Threading.Thread.Sleep(100);
                }
                html1 = taskString1.Result;
                Debug.WriteLine("");
            }
        }

        async Task<string> GetBrowserSource(ChromiumWebBrowser Browser)
        {
            return await Browser.GetMainFrame().GetSourceAsync();
        }
    }
}

我的 app.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
    </startup>
</configuration>

我的packages.config

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="cef.redist.x64" version="83.4.2" targetFramework="net452" />
  <package id="cef.redist.x86" version="83.4.2" targetFramework="net452" />
  <package id="CefSharp.Common" version="83.4.20" targetFramework="net452" />
  <package id="CefSharp.WinForms" version="83.4.20" targetFramework="net452" />
</packages>

【问题讨论】:

  • 该方法应该异步调用,不支持同步调用。
  • 想补充一点背景资料来帮助别人。我是一名 .NET 程序员,从事屏幕抓取工作多年。过去我们可以使用 Windows 窗体浏览器控件来简单地导航到页面、读取 html 源代码并提取我们需要的内容。 HtmlAgility 是使用 html 源代码的巨大改进。我们甚至可以删除 Web Browser 控件,直接在 HtmlAgility 中加载 html。
  • 但是随着网站中反屏幕抓取工具的增长,在 HtmlAgility 中直接加载 html 变得很困难 - 全部自动。我回到半自动的方式,使用Web Browser控件加载登录页面,手动登录,然后让HtmlAgility在登录后接管。最新的反屏幕抓取工具现在需要在浏览器中运行 cookie 和 javascript - 因此即使使用 Windows 窗体 Web 浏览器控件登录也很难完成。我在使用 CefSharp 时没有发现任何问题。

标签: c# asynchronous async-await cefsharp


【解决方案1】:

看起来像一个僵局。这是一个正确的async/awaitusage 问题。

private async void WebBrowserFrameLoadEnded(ChromiumWebBrowser BrowserSender, FrameLoadEndEventArgs e)
{
    if (e.Frame.IsMain)
    {
        string html1 = await GetBrowserSource(BrowserSender);
        Debug.WriteLine(html1);
    }
}

但为什么不简单地这样做呢?

private async void OnWebBrowserFrameLoadEnded(object sender, FrameLoadEndEventArgs e)
{
    if (e.Frame.IsMain)
    {
        ChromiumWebBrowser browserSender = (ChromiumWebBrowser)sender;
        string html = await browserSender.GetMainFrame().GetSourceAsync();
        Debug.WriteLine(html);
    }
}

注意Application.DoEvents()isn't safe to use

【讨论】:

  • 谢谢。根据你的回答,我得到了它的工作。关于“if (this.InvokeRequired)”的使用,他们说 CefSharp 浏览器在不同的线程上运行,所以我们应该始终使用 InvokeRequired。
  • @Bubba 不客气。一些技巧。简而言之,this.Invoke 在 UI 线程上运行代码,Task.Run(默认情况下)在池化后台线程上运行。首先通常用于安全的 UI 操作,其次 - 用于繁重的 CPU 绑定操作以保持 UI 响应。对于 I/O 绑定操作,await 就足够了。因此this.Invoke 仅在您与 UI 交互时才需要,否则它看起来像是调度到主线程无关的冗余工作,可以在任何线程上完成。
猜你喜欢
  • 2018-05-24
  • 2016-05-11
  • 1970-01-01
  • 2016-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-25
相关资源
最近更新 更多