Библиотека должна поддерживать как минимум .NET framework версии 3.5 и 4.0; поддержка версии 4.5 и будущих версий тоже была бы отличной.
Он также должен обрабатывать «грязный» HTML, если это возможно.
АнглШарп :
Я использовал Html Agility Pack , и хотя на его домашней странице явно упоминается только версия 2.0, он отлично работает с версией 4.0 .NET framework. Я подозреваю, что он отлично работает и с версией 4.5.
Вот пример кода, использующего Html Agility Pack с LINQ:
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 года. Возможно, не так много возможностей для улучшения, и, судя по моему предварительному использованию, он кажется стабильным и быстрым.
Fizzler построен на основе Html Agility Pack и обеспечивает поддержку использования селекторов CSS для доступа к проанализированным HTML-документам.
К сожалению, как и Html Agility Pack, он также выглядит неактивным; последнее изменение, согласно исходному списку Google Code, последнее изменение было в январе 2013 года. Также возможно, что оно также стабильно и не нуждается в постоянной разработке или обслуживании.
CsQuery также является очень хорошим парсером HTML с селекторами CSS. Он генерирует тот же DOM, что и браузеры на основе Gecko. У него также гораздо лучшая лицензия (MIT), чем Html Agility Pack (MS-PL), которая несовместима с GPL.
Эта библиотека также очень проста в использовании, потому что у нее есть API, похожий на jQuery.
РЕДАКТИРОВАТЬ: В настоящее время (25 июня 2016 г.) он активно не поддерживается. Так что есть лучшая альтернатива, такая как AngleSharp.
Почему?
Существует три вида CefSharp:
Первые два используются как WebBrowser на базе IE в Windows.Forms. Но это на основе хрома. А для разбора следует использовать CefSharp.OffScreen.
Установите его через Nuget и используйте.
Install-Package CefSharp.OffScreen -Version 57.0.0
Приведенные примеры не должны быть короткими, но они облегчат программирование с использованием CefSharp.
Я буду использовать jQuery для вызовов Javascript для демонстрации и упрощения примера, предполагая, что на целевом сайте есть эта библиотека. Вы можете выполнить простой JS или выбрать любой из доступных на целевом сайте.
Прежде всего, результаты javascript возвращаются через JavascriptResponse
свойство type Result
. object
Массивы 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
класс для выполнения всей грязной работы с браузером.
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
класс из Main
метода, чтобы получить все a
href
с домашней страницы stackoverflow.
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];
ChromiumWebBrowser
, EvaluateScriptAsync
используя Javascript, получаете желаемые результаты. Например, если целевой сайт поддерживает jQuery и вам нужны все href, которые вы можете вызвать _browser.EvaluateScriptAsync("$('a[href]').map((index, element) => $(element).prop('href')).toArray()")
. Я могу показать вам примеры кода, но не уверен, что они будут уместны на этом сайте. Я использовал текущую библиотеку для анализа веб-сайтов.Если вам нужно что-то действительно быстрое, загляните сюда: Majestic-12: Проекты: HTML-парсер C# (.NET)
Это будет не самый простой в использовании, но, вероятно, будет самым быстрым.
Атари