如果可能的话,它也应该处理'messy'HTML。
#1 楼
AngleSharp:积极开发/维护的
对CSS选择器的内置支持
#2 楼
Html Agility Pack我使用了Html Agility Pack,尽管其主页仅明确提到了2.0版,但它与.NET框架的4.0版非常兼容。我怀疑它在4.5版中也能正常工作。
以下是使用带有LINQ的Html Agility Pack的示例代码:似乎不是一个活跃的项目。根据项目CodePlex页面的最新更改是2012年7月。可能没有太多的改进空间,根据我的初步使用情况,它似乎稳定且快速。 > Fizzler建立在Html Agility Pack的基础上,并支持使用CSS选择器来访问已解析的HTML文档。根据Google代码源清单的最新更改,最近一次更改是2013年1月。它也可能很稳定,不需要持续的开发或维护。
其他资源
.net-在C#中解析html的最佳方法是什么? -堆栈溢出
用c#.net解析HTML-堆栈溢出
#3 楼
CsQuery还是具有CSS选择器的非常好的HTML解析器。它生成与基于Gecko的浏览器相同的DOM。它的许可证(MIT)比Html Agility Pack(MS-PL)好得多,后者与GPL不兼容。该库也非常易于使用,因为它具有类似API的jQuery。
:
编辑:当前(2016年6月25日),它没有得到积极维护。因此,还有更好的替代品,例如AngleSharp。
#4 楼
CefSharp为什么?
主动维护
您拥有Chromium的强大功能
让我们运行任何JavaScript。用这种方式开发解析要容易得多。您可以转到基于Chromium的浏览器控制台并开发所需的脚本。当您编写了一些C#代码基础后,您的开发方式就是从控制台粘贴Javascript代码,而无需编写C#循环和查询。
让您从JavaScript代码中触发C#事件。当您要触发AJAX成功事件以获取结果时,此功能非常有用。
CefSharp共有三种:
CefSharp.WinForms
CefSharp.Wpf
CefSharp.OffScreen
前两个用法类似于Windows.Forms中基于IE的WebBrowser。但这是基于铬的。对于解析,您应该使用CefSharp.OffScreen。
通过Nuget安装并使用它。
Install-Package CefSharp.OffScreen -Version 57.0.0
代码
提供的示例虽然不太简短,但它们会使使用CefSharp的编程更加容易。
我将使用jQuery假定目标站点具有此库,则Javascript要求进行演示和示例简化。您可以执行普通JS或选择目标站点上可用的任何JavaScript。
首先,通过
JavascriptResponse
的Result
类型的属性object
返回javascript结果。 Javascript数组映射到List<object>
。其他结果类型映射是显而易见的:string
,int
,bool
,但它们都将存储在object
Result
属性中。为了使Javascript方法通用,我使用以下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
类,以使用浏览器的内容执行所有肮脏的工作。从stackoverflow主页获取所有CefSharpWrapper
Main
。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);
}
}
注意:该库不能区分空数组
a
和href
。它们都以null
的形式返回。因此,要避免使用undefined
,请在null
中添加相应的代码(但您必须处理的区别是C#中的NullReferenceException
表示Java中的CefSharpWrapper
或Javascript中的空数组),或者在null
中添加以下代码。 />评论
这个问题与呈现HTML无关。我在GitHub的项目信息中找不到与解析HTML有关的任何内容。
–肯尼·埃维特(Kenny Evitt)
16-11-28在15:09
我知道。您可以使用该库进行解析,并且解析起来非常舒适,这就是我推荐它的原因。您只需调用ChromiumWebBrowser的EvaluateScriptAsync方法,并使用Javascript获得所需的结果。例如,如果目标站点支持jQuery,并且您需要所有href,则可以调用_browser.EvaluateScriptAsync(“ $('a [href]')。map((index,element)=> $(element).prop('href '))。toArray()“)。我可以向您展示代码示例,但不确定它是否与本网站相关。我已经在生产中使用当前库来解析网站。
– Vadim Ovchinnikov
16年11月28日在19:32
代码示例是绝对相关的;如果可以的话,请添加一个简单的示例。例如,C#代码将如何检索JavaScript代码解析的数据?这是一个非常有趣的答案。我隐式地假设已经提供了要解析的HTML,但是能够使用Chromium(或任何浏览器引擎)将允许处理单页Web应用程序之类的事情。
–肯尼·埃维特(Kenny Evitt)
16年11月28日在20:35
非常有趣(而且很hacky)的答案!
–肯尼·埃维特(Kenny Evitt)
16年12月19日在22:29
#5 楼
如果您想要真正快速的东西,请看这里:Majestic-12:项目:C#HTML解析器(.NET)它不是最容易使用,但可能最快。
评论
HAP仍然很流行,但是它是多虫的古怪垃圾,在现代代码中应避免使用。许多年后,仍然存在对带有可选的结束标记的HTML标记进行不正确解析的错误。它仍然返回null而不是空集合,从而使代码变得冗长。 HAP也是Ms-PL,Fizzler是LGPL,而不是MIT / BSD,这在某些情况下可能很重要。只是不要使用它,所以有更好的选择。
– Athari
17年6月11日在5:08