Rob*_*vey 5 javascript c# headless-browser cefsharp cefsharp.offscreen
我有一个希望是简单的任务,但是它将需要精通CefSharp的人员来解决。
我有一个要从中检索HTML的URL。问题在于此特定的url实际上并未在GET上分发页面。相反,它将大量Javascript推送到浏览器,然后浏览器执行并生成实际的呈现页面。这意味着涉及HttpWebRequest并且HttpWebResponse不会起作用的常规方法。
我研究了许多不同的“无头”选项,由于多种原因,我认为最能满足我的需求的选项是CefSharp.Offscreen。但是我对这件事的运作方式一无所知。我看到有几个事件可以订阅,还有一些配置选项,但是我不需要嵌入式浏览器之类的东西。
我真正需要的是一种执行以下操作(伪代码)的方法:
string html = CefSharp.Get(url);
Run Code Online (Sandbox Code Playgroud)
订阅事件没有问题,如果那是等待Javascript执行并生成呈现页面所需的条件。
我知道我正在做一些考古学来恢复 2yo 的帖子,但详细的回答可能对其他人有用。
所以是的, Cefsharp.Offscreen 适合这项任务。
下面是一个将处理所有浏览器活动的类。
using System;
using System.IO;
using System.Threading;
using CefSharp;
using CefSharp.OffScreen;
namespace [whatever]
{
public class Browser
{
/// <summary>
/// The browser page
/// </summary>
public ChromiumWebBrowser Page { get; private set; }
/// <summary>
/// The request context
/// </summary>
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();
}
/// <summary>
/// Open the given url
/// </summary>
/// <param name="url">the url</param>
/// <returns></returns>
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;
}
/// <summary>
/// Manage the IsLoading parameter
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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();
}
}
/// <summary>
/// Wait until page initialization
/// </summary>
private void PageInitialize()
{
SpinWait.SpinUntil(() => Page.IsBrowserInitialized);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在在我的应用程序中,我只需要执行以下操作:
public MainWindow()
{
InitializeComponent();
_browser = new Browser();
}
private async void GetGoogleSource()
{
_browser.OpenUrl("http://icanhazip.com/");
string source = await _browser.Page.GetSourceAsync();
}
Run Code Online (Sandbox Code Playgroud)
这是我得到的字符串
"<html><head></head><body><pre style=\"word-wrap: break-word; white-space: pre-wrap;\">NotGonnaGiveYouMyIP:)\n</pre></body></html>"
如果您无法获得 Chromium 的无头版本来帮助您,您可以尝试 node.js 和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开始并进行编译,然后根据您的需要进行调整。您需要能够在 C# 代码中设置 webBrowser.Address,并且需要知道页面何时加载,然后需要使用 JavaScript 代码调用 webBrowser.EvaluateScriptAsync(".. JS code ..") (如一个字符串),它将执行所描述的操作(以字符串形式返回 bodyElement.innerHTML)。
| 归档时间: |
|
| 查看次数: |
10110 次 |
| 最近记录: |