使用CefSharp.Offscreen检索需要Javascript渲染的网页

我希望这是一项简单的任务,但是需要那些精通CefSharp的人来解决它。

我有一个我想从中检索HTML的url。 问题是这个特定的URL实际上并没有在GET上分发页面。 相反,它将一大堆Javascript推送到浏览器,然后浏览器执行并生成实际呈现的页面。 这意味着涉及HttpWebRequestHttpWebResponse的常用方法不起作用。

我看了很多不同的“无头”选项,而我认为最符合我需求的选择有很多原因,那就是CefSharp.Offscreen。 但我不知道这件事是如何起作用的。 我看到有几个可以订阅的事件,以及一些配置选项,但我不需要像嵌入式浏览器那样的东西。

我真正需要的是一种做这样的事情的方式(伪代码):

 string html = CefSharp.Get(url); 

订阅事件时我没有问题,如果需要等待Javascript执行并生成呈现的页面。

如果您无法获得Chromium的无头版本来帮助您,您可以尝试node.js和jsdom( https://github.com/tmpvar/jsdom )。 节点启动并运行后,易于安装和播放。 你可以在Github README上看到简单的例子,他们下拉URL,运行所有的javascript,包括任何自定义的javascript代码(例如:jQuery位来计算某些类型的元素),然后你在内存中有HTML做你想做的事情。 您可以只执行$(’body’)。html()并获取一个字符串,就像在伪代码中一样。 (这甚至适用于生成SVG图形的东西,因为那只是更多的XML树节点。)

如果您需要将此作为需要分发的较大C#应用程序的一部分,那么您使用CefSharp.Offscreen的想法听起来很合理。 一种方法可能是首先使用CefSharp.WinForms或CefSharp.WPF,然后你可以在字面上看到内容,然后在这一切正常时再尝试CefSharp.Offscreen。 您甚至可以在屏幕浏览器中运行一些JavaScript来下载body.innerHTML并将其作为字符串返回到C#端,然后再无头。 如果这样做,其余的应该很容易。

也许从CefSharp.MinimalExample( https://github.com/cefsharp/CefSharp.MinimalExample )开始并进行编译,然后根据您的需要进行调整。 您需要能够在C#代码中设置webBrowser.Address,并且您需要知道页面何时已加载,然后您需要使用JavaScript代码调用webBrowser.EvaluateScriptAsync(“.. JS code ..”)(如一个字符串)将执行所描述的操作(将bodyElement.innerHTML作为字符串返回)。

我知道我正在做一些考古复兴2yo的post,但详细的回答可能对其他人有用。

所以是的,Cefsharp.Offscreen适合这项任务。

这下面是一个将处理所有浏览器活动的类。

 using System; using System.IO; using System.Threading; using CefSharp; using CefSharp.OffScreen; namespace [whatever] { public class Browser { ///  /// The browser page ///  public ChromiumWebBrowser Page { get; private set; } ///  /// The request context ///  public RequestContext RequestContext { get; private set; } // chromium does not manage timeouts, so we'll implement one private ManualResetEvent manualResetEvent = new ManualResetEvent(false); public Browser() { var settings = new CefSettings() { //By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache"), }; //Autoshutdown when closing CefSharpSettings.ShutdownOnExit = true; //Perform dependency check to make sure all relevant resources are in our output directory. Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null); RequestContext = new RequestContext(); Page = new ChromiumWebBrowser("", null, RequestContext); PageInitialize(); } ///  /// Open the given url ///  /// the url ///  public void OpenUrl(string url) { try { Page.LoadingStateChanged += PageLoadingStateChanged; if (Page.IsBrowserInitialized) { Page.Load(url); //create a 60 sec timeout bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(60)); manualResetEvent.Reset(); //As the request may actually get an answer, we'll force stop when the timeout is passed if (!isSignalled) { Page.Stop(); } } } catch (ObjectDisposedException) { //happens on the manualResetEvent.Reset(); when a cancelation token has disposed the context } Page.LoadingStateChanged -= PageLoadingStateChanged; } ///  /// Manage the IsLoading parameter ///  ///  ///  private void PageLoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { // Check to see if loading is complete - this event is called twice, one when loading starts // second time when it's finished if (!e.IsLoading) { manualResetEvent.Set(); } } ///  /// Wait until page initialization ///  private void PageInitialize() { SpinWait.SpinUntil(() => Page.IsBrowserInitialized); } } } 

现在在我的应用程序中,我只需要执行以下操作:

 public MainWindow() { InitializeComponent(); _browser = new Browser(); } private async void GetGoogleSource() { _browser.OpenUrl("http://icanhazip.com/"); string source = await _browser.Page.GetSourceAsync(); } 

这是我得到的字符串

"

NotGonnaGiveYouMyIP:)\n

"