该库应至少支持 .NET 框架版本 3.5 和 4.0;对 4.5 版和未来版本的支持也会很棒。
如果可能的话,它也应该处理“混乱”的 HTML。
该库应至少支持 .NET 框架版本 3.5 和 4.0;对 4.5 版和未来版本的支持也会很棒。
如果可能的话,它也应该处理“混乱”的 HTML。
锐角:
我使用了Html Agility Pack,虽然它的主页只明确提到了 2.0 版,但它与 .NET 框架的 4.0 版配合得很好。我怀疑它也适用于 4.5 版。
下面是一些使用带有 LINQ 的 Html Agility Pack 的示例代码:
var document = new HtmlDocument();
document.Load(@"C:\Documents and Settings\Kenny\My Documents\project\document.html");
var table = document.GetElementbyId("table5");
var tableRows = table.ChildNodes
.Where(cn => cn.NodeType == HtmlNodeType.Element)
.Skip(2);
不幸的是,它似乎不是一个活跃的项目。根据项目 CodePlex 页面的最新更改是 2012 年 7 月。可能没有太大的改进空间,根据我的初步使用,它看起来稳定且快速。
Fizzler构建在 Html Agility Pack 之上,并支持使用 CSS 选择器访问已解析的 HTML 文档。
不幸的是,与 Html Agility Pack 一样,它似乎也处于非活动状态;根据其 Google 代码源列表,最新的更改是 2013 年 1 月。也有可能它也很稳定,不需要持续的开发或维护。
CsQuery也是非常好的带有 CSS 选择器的 HTML 解析器。它生成与基于 Gecko 的浏览器相同的 DOM。它还具有比 Html Agility Pack (MS-PL) 更好的许可证 (MIT),后者与 GPL 不兼容。
这个库也很容易使用,因为它有类似 jQuery 的 API。
编辑:目前(2016 年 6 月 25 日)它没有得到积极维护。所以有更好的选择,比如 AngleSharp。
为什么?
CefSharp 共有三种:
前两个与Windows.Forms 中基于 IE 的WebBrowser类似。但它是基于 Chromium 的。对于解析,您应该使用 CefSharp.OffScreen。
通过 Nuget 安装并使用它。
Install-Package CefSharp.OffScreen -Version 57.0.0
提供的示例不是尽可能短,但它们将使使用 CefSharp 进行编程更容易。
假设目标站点具有此库,我将使用 jQuery 进行 Javascript 调用以进行演示和示例简单性。您可以执行纯 JS 或选择目标站点上可用的任何 JS。
首先,通过类型JavascriptResponse
的属性返回 javascript 结果。Javascript 数组映射到. 其他结果类型映射很明显:, ,但它们都将存储在属性中。为了使 Javascript 方法通用,我使用以下.Result
object
List<object>
string
int
bool
object
Result
ConvertHelper
public static class ConvertHelper
{
public static T[] GetArrayFromObjectList<T>(object obj)
{
return ((IEnumerable<object>)obj)
.Cast<T>()
.ToArray();
}
public static List<T> GetListFromObjectList<T>(object obj)
{
return ((IEnumerable<object>)obj)
.Cast<T>()
.ToList();
}
public static T ToTypedVariable<T>(object obj)
{
if (obj == null)
{
dynamic dynamicResult = null;
return dynamicResult;
}
Type type = typeof(T);
if (type.IsArray)
{
dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetArrayFromObjectList))
.MakeGenericMethod(type.GetElementType())
.Invoke(null, new[] { obj });
return dynamicResult;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetListFromObjectList))
.MakeGenericMethod(type.GetGenericArguments().Single())
.Invoke(null, new[] { obj });
return dynamicResult;
}
return (T)obj;
}
}
我添加了类来处理 Javascript 错误:
public class JavascriptException : Exception
{
public JavascriptException(string message): base(message) { }
}
然后我们需要创建我们的核心CefSharpWrapper
类来执行浏览器的所有脏活。
public class CefSharpWrapper
{
private ChromiumWebBrowser _browser;
public void InitializeBrowser()
{
CefSettings settings = new CefSettings();
// Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(new CefSettings(), performDependencyCheck: true, browserProcessHandler: null);
_browser = new ChromiumWebBrowser();
// wait till browser initialised
AutoResetEvent waitHandle = new AutoResetEvent(false);
EventHandler onBrowserInitialized = null;
onBrowserInitialized = (sender, e) =>
{
_browser.BrowserInitialized -= onBrowserInitialized;
waitHandle.Set();
};
_browser.BrowserInitialized += onBrowserInitialized;
waitHandle.WaitOne();
}
public void ShutdownBrowser()
{
// Clean up Chromium objects
Cef.Shutdown();
}
public Task<T> GetResultAfterPageLoad<T>(string pageUrl, Func<Task<T>> onLoadCallback)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
EventHandler<LoadingStateChangedEventArgs> onPageLoaded = null;
T t = default(T);
// An event that is fired when the first page is finished loading.
// This returns to us from another thread.
onPageLoaded = async (sender, e) =>
{
// Check to see if loading is complete - this event is called twice, one when loading starts
// second time when it's finished
// (rather than an iframe within the main frame).
if (!e.IsLoading)
{
// Remove the load event handler, because we only want one snapshot of the initial page.
_browser.LoadingStateChanged -= onPageLoaded;
t = await onLoadCallback();
tcs.SetResult(t);
}
};
_browser.LoadingStateChanged += onPageLoaded;
_browser.Load(pageUrl);
return tcs.Task;
}
// Method to get result via Javascript
public async Task<T> EvaluateJavascript<T>(string script)
{
JavascriptResponse javascriptResponse = await browser.GetMainFrame().EvaluateScriptAsync(script);
if (javascriptResponse.Success)
{
object scriptResult = javascriptResponse.Result;
return ConvertHelper.ToTypedVariable<T>(scriptResult);
}
throw new JavascriptException(javascriptResponse.Message);
}
}
然后我们从方法中调用我们的CefSharpWrapper
类以从 stackoverflow 主页Main
获取 all 。a
href
public class Program
{
private static void Main()
{
MainAsync().Wait();
}
private static async Task MainAsync()
{
CefSharpWrapper wrapper = new CefSharpWrapper();
wrapper.InitializeBrowser();
string[] urls = await wrapper.GetResultAfterPageLoad("http://stackoverflow.com/", async () =>
await wrapper.EvaluateJavascript<string[]>("$('a[href]').map((index, element) => $(element).prop('href')).toArray()"));
wrapper.ShutdownBrowser();
}
}
注意:这个库不区分空数组null
和undefined
. 它们都返回为null
. 因此,为了避免NullReferenceException
将相应的代码添加到CefSharpWrapper
(但随后您必须处理区分null
C# 中的意思null
或 Javascript 中的空数组)或将以下代码添加到Main
.
if (urls == null) urls = new string[0];