gifts2017

.Net в 1С. На примере использования HTTPClient,AngleSharp. Удобный парсинг сайтов с помощью библиотеки AngleSharp, в том числе с авторизацией аля JQuery с использованием CSS селекторов. Динамическая компиляция

Опубликовал Сергей Смирнов (Serginio) в раздел Программирование - Практика программирования

Часто приходится парсить сайты, в том числе с авторизацией, перескакивая со страницы на страницу по ссылкам. Тот, кто занимался вэб программированием, знает, как удобно использовать JQuery и CSS селекторы. На .Net написана очень удобная библиотека AngleSharp. Я покажу, как с её помощью можно значительно облегчить себе труд.

Это продолжение статьи .Net в 1С. Примеры использования HTTPClient,AngleSharp. Асинхронные HTTP запросы, отправка Post нескольких файлов multipart/form-data,сжатие трафика с использованием gzip, deflate,удобный парсинг сайтов итд

https://github.com/AngleSharp/AngleSharp  это .Net библиотека для разбора HTML файлов в стиле CSS селекторов, аналогично использованию JQuery.  Которая также поддерживает CSS и JavaScript. Но библиотека поддержки JavaScript пока плющевая и давно не обновлялась https://github.com/AngleSharp/AngleSharp.Scripting

На первой же странице есть пример получения названий всех серий сериала "Теория большого взрыва"

// устанавливаем параметры для использования разбора документа
var config = Configuration.Default.WithDefaultLoader();
// Устанавливаем адрес страницы сайта
var address = "https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes";
// загружаем страницу и разбираем её
var document = await BrowsingContext.New(config).OpenAsync(address);
// Используем CSS селектор для получения строк таблицы с классом  и выбрать из этой строки 3 колонку 
var cellSelector = "tr.vevent td:nth-child(3)";
// Получим все ячейки
var cells = document.QuerySelectorAll(cellSelector);
//Выделим из ячеек текстовое содержимое
var titles = cells.Select(m => m.TextContent);

Красиво, не правда ли?

На 1С это будет выглядеть так

	
	config = AngleSharp_Configuration;
	Расширение=Врап.ПолучитьТип("AngleSharp.ConfigurationExtensions");
	config =Расширение.WithDefaultLoader(config);
            // Load the names of all The Big Bang Theory episodes from Wikipedia
          address = "https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes";
            // Asynchronously get the document in a new context using the configuration
          document = Врап.ПолучитьТип("AngleSharp.BrowsingContext").New(config);
          BrowsingContextExtensions=Врап.ПолучитьТип("AngleSharp.BrowsingContextExtensions");
		  
		  document =BrowsingContextExtensions.OpenAsync(document,address).Result;
            // This CSS selector gets the desired content
          cellSelector = "tr.vevent td:nth-child(3)";
            // Perform the query to get all cells with the content
          cells = document.QuerySelectorAll(cellSelector);
            // We are only interested in the text - select it with LINQ

            Для каждого стр Из cells Цикл
			
			Сообщить(стр.TextContent)	
			
	    КонецЦикла; 

В предыдущей статье я уже показывал описание

Но повторю

// Сборку AngleSharp.dll поместить в каталог программы
//Для использования Scripting Api
    КатаогПрограммы=Врап.ПолучитьТип("System.AppDomain").CurrentDomain.BaseDirectory;
	ИмяСборкиAngleSharp=Врап.ПолучитьТип("System.IO.Path").Combine(КатаогПрограммы,"AngleSharp.dll");
	AngleSharp_Configuration=Врап.ПолучитьТипИзСборки("AngleSharp.Configuration",ИмяСборкиAngleSharp);
        parser = Врап.СоздатьОбъект("AngleSharp.Parser.Html.HtmlParser");

AngleSharp.dll можно поместить в любое место, но для динамической компиляции нужно, чтобы библиотека находилась в домене приложения(КаталогПрограммы). Об этом в конце статьи. Про CSS селекторы и их использование можно почитать здесь

http://anton.shevchuk.name/javascript/jquery-for-beginners-selectors

http://htmlbook.ru/css/nth-child

https://learn.javascript.ru/css-selectors

https://habrahabr.ru/post/51717/

http://webknowledge.ru/slozhnye-css-selektory-soderzhaschie-klass-i-id-elementov/

Теперь посмотрим, как можно получить тот же результат, но с использованием HTTPClient, и заполним данные в таблице

	stopWatch = Врап.СоздатьОбъект("System.Diagnostics.Stopwatch");
	
        stopWatch.Start();

	      Client = Врап.СоздатьОбъект(HttpClient);
              Client.BaseAddress = Врап.СоздатьОбъект("System.Uri","https://en.wikipedia.org");
              res = Client.GetStringAsync("wiki/List_of_The_Big_Bang_Theory_episodes").Result;

              parser = Врап.СоздатьОбъект("AngleSharp.Parser.Html.HtmlParser");
             //Just get the DOM representation
             document = parser.Parse(res);
             СелекторСтроки = "tr.vevent";
    Тз=новый ТаблицаЗначений;
    Колонки=Тз.Колонки;
    Колонки.Добавить("НомерСерии");
    Колонки.Добавить("НомерСерииВСезоне");
    Колонки.Добавить("Название");
    Колонки.Добавить("Режиссер");
    Колонки.Добавить("Автор");
    Колонки.Добавить("Дата");
    Колонки.Добавить("Код");
    Колонки.Добавить("Просмотров");

     Строки =document.QuerySelectorAll(СелекторСтроки);

     stopWatch.Stop();
	Сообщить("Скачка и парсинг");
	ВывестиВремя(stopWatch,истина);
	
	
	stopWatch.Restart();


           Для Каждого стр из Строки Цикл
		сч=0;
		стрТз=Тз.Добавить();
		Ячейки=стр.Cells;
				
		Для каждого Ячейка из Ячейки Цикл
				
		 стрТз[сч]=Ячейка.TextContent;
		 сч=сч+1;
		   Если сч=8 Тогда
			прервать
		   КонецЕсли;
		КонецЦикла
	   КонецЦикла;
		
	stopWatch.Stop();
	
	
	Сообщить("Время выполнения =");
	ВывестиВремя(stopWatch,истина);

	Тз.ВыбратьСтроку();	

Все прекрасно. Но меня ждало разочарование. Заполнение составляло 8 секунд. Решение я нашел. Но об этом попозже.

Надо отметить, что  QuerySelectorAll и QuerySelector можно применять к контейнерам поддерживающих интерфейс 

 
    public interface IParentNode
    {
       
        int ChildElementCount { get; }
        IHtmlCollection<IElement> Children { get; }
        IElement FirstElementChild { get; }
        IElement LastElementChild { get; }
        void Append(params INode[] nodes);
        void Prepend(params INode[] nodes);
        IElement QuerySelector(string selectors);
        [DomName("querySelectorAll")]
        IHtmlCollection<IElement> QuerySelectorAll(string selectors);
    }
}

Мы можем например задать селектор для строки. Например получить первый и третий элемент строки

Строки =document.QuerySelectorAll(СелекторСтроки);
    
           	Для Каждого стр из Строки Цикл
				//Ячейки=стр.Cells;
				 Ячейки=стр.QuerySelectorAll("th:nth-child(1),td:nth-child(3)");
				Для каждого Ячейка из Ячейки Цикл
				
				стрТз[сч]=Ячейка.TextContent;
			КонецЦикла
		КонецЦикла;

 Добавлю еще про коллекци.  

 public interface IHtmlCollection<T> : IEnumerable<T>, IEnumerable where T : IElement
    {
     
        T this[int index] { get; }
        T this[string id] { get; }
        int Length { get; }
    }

 К ним можно обращаться по номерe и по ID или атрибуту. Например

Строки =document.QuerySelectorAll(СелекторСтроки);
ВтораяСтрока=Строки.get_Item(1);

Пока покажу, как использовать селекторы. Возьмем ту же страницу, но выделим только ссылки на серии.

Client.BaseAddress = Врап.СоздатьОбъект("System.Uri","https://en.wikipedia.org");
	res = Client.GetStringAsync("wiki/List_of_The_Big_Bang_Theory_episodes").Result;
	
	document = parser.Parse(res);
	СелекторСтроки = "li.toclevel-2>a";
	Строки =document.QuerySelectorAll(СелекторСтроки);
	// Получим селекторы аннотации выделим текст и Хэш ссылку
      Для Каждого стр из Строки Цикл 
        Сообщить(Стр.TextContent + " "+стр.Hash); 
      КонецЦикла;

Теперь попробуем использовать форму поиска

Форма=document.QuerySelector("form#searchform");
	ссылка=	Форма.Action;
	
	структура=новый структура;
	Для каждого Элемент Из Форма.Elements  Цикл
		Элем=Врап.ПолучитьИнтерфейс(Элемент,"IHtmlInputElement");
		//Нужны только Input элементы
		//Выберем все элементы Input
		// и запишем их имена и значения
		Если Элем<> Неопределено Тогда
			структура.Вставить(Элем.Name,Элем.Value);
			Сообщить(Элем.Name+"="+Элем.Value);
			
		КонецЕсли; 
		
		
	КонецЦикла; 		
	структура.search="Кириллица";
	// Если удалить fulltext то произойдет Redirect
	// структура.Удалить("fulltext");
	Врап.ВСтроку(Элемент);
	сб=Врап.СоздатьОбъект(StringBuilder,ссылка+"?");
	//var uri = new Uri(builder.ToString(), dontEscape: true);
	
	// Создадим строку запроса. Для метода Get
	// так как Форма.Method=""
	Для каждого стр Из структура  Цикл
		
	   сб.AppendFormat("{0}={1}&",HttpUtility.UrlPathEncode(стр.Ключ),HttpUtility.UrlPathEncode(стр.Значение),0);
	
	КонецЦикла; 
	
	стр=сб.ToString(0,сб.Length-1); 
	Сообщить(стр);
	//Сделаем запрос по относительному пути
	//Так как основной путь уже прописан в BaseAddress
	res = Client.GetStringAsync(стр).Result;
	
	// Посмотрим результат
	// Можно посмотреть страницу используя например
	//http://filyanin.ru/8-vizualnyy-HTML-onlayn-redaktor.html
	Текст=Новый ТекстовыйДокумент;
	Текст.УстановитьТекст(res);
	Текст.Показать();
		
	document = parser.Parse(res);
	
	// Найдем результаты поиска по пути
	// ul с классом mw-search-results в первом потомке li и в первом потомке div и а
	CSSСелектор = "ul.mw-search-results>li>div>a";
	Строки =document.QuerySelectorAll(CSSСелектор);
	
	Для Каждого стр из Строки Цикл
		Сообщить(Стр.TextContent + " "+Стр.PathName);
		
	КонецЦикла; 

Очень часто приходится искать информацию на сайтах с авторизацией. При этом поля формы содежат дополнительные поля для верификации. 

Вот пример поиска нужного значения.

var configuration = Configuration.Default.WithDefaultLoader().WithCookies();
                var context = BrowsingContext.New(configuration);
                // откромем начальную страницу  
                await context.OpenAsync(WebsiteUrl);
                // Найдем ссылку содержашуюся в элементе a и классом log-in
                await context.Active.QuerySelector<IHtmlAnchorElement>("a.log-in").Navigate();
                // Установим элементам формы нужное значение и вызовем Submit
                await context.Active.QuerySelector<IHtmlFormElement>("form").Submit(new
                {
                    User = "User",
                    Password = "secret"
                });
                // Опять найдем нужную ссылку и перейдем на страницу с искомым значением
                await context.Active.QuerySelector<IHtmlAnchorElement>("a.secret-link").Navigate();
                // Получим это искомое значение 

Теперь перепишем все это с использованием HTTPClient

Клиент = Врап.СоздатьОбъект(HttpClient);
	Клиент.BaseAddress = Врап.СоздатьОбъект("System.Uri",WebsiteUrl);
	// Стоит отметить, что по умолчанию  HttpClient использует Cookie
	//Загрузим основную страницу
	res = Клиент.GetStringAsync("").Result;
    document = parser.Parse(res);
    // Найдем адрес страницы для авторизации
	
	Аннотация= document.QuerySelector("a.log-in");
    //Полный относительный путь Аннотация.PathName+Аннотация.Search
    // Но в данном случае Аннотация.Search просто нет
    // Если использовать BrowsingContext то полный путь содержится в Href

	res= Клиент.GetStringAsync(Аннотация.PathName).Result;
	 document = parser.Parse(res);
    // Найдем  элемент form для авторизации
	//В которой есть поле для верификации __RequestVerificationToken 
     Форма=document.QuerySelector("form");
	 Сообщить(Форма.Method);
	 d = Врап.СоздатьОбъект(Dictionary);
	 
	Для каждого Элемент Из Форма.Elements  Цикл
		Элем=Врап.ПолучитьИнтерфейс(Элемент,"IHtmlInputElement");
		//Нужны только Input элементы
		//Выберем все элементы Input
		// и запишем их имена и значения в словарь
		Если Элем<> Неопределено Тогда
			d.Add(Элем.Name, Элем.Value);
			Сообщить(Элем.Name+"="+Элем.Value);
			 
		КонецЕсли; 
		
		
	КонецЦикла; 	
	
	
            d.set_Item("User", "User");
            d.set_Item("Password", "secret");
			
			Контент=Врап.СоздатьОбъект(FormUrlEncodedContent,d);
		// Отправим пост запрос с данными формы	по адресу находящемся в Action
		резулт=Клиент.PostAsync(Форма.Action,Контент).Result;
		
		// И получим ответ
		res=резулт.Content.ReadAsStringAsync().Result;
		document = parser.Parse(res);

	// Найдем на этой странице ссылку на секретную страницу
     Аннотация= document.QuerySelector("a.secret-link");
	 res= Клиент.GetStringAsync(Аннотация.PathName).Result;
	  document = parser.Parse(res);
	  // Получим из первого параграфа искомое значение
	  Сообщить(document.QuerySelector("p").TextContent)

В C# есть удобный сахар в виде расширений. Например, код

Configuration.Default.WithDefaultLoader().WithCookies();

//На самом деле представляет собой 

 var configuration = AngleSharp.Configuration.Default;

            configuration=AngleSharp.ConfigurationExtensions.WithDefaultLoader(configuration);
            configuration = AngleSharp.ConfigurationExtensions.WithCookies(configuration);

// Так как функция WithDefaultLoader представляет функцию расширения
// так как первый параметр помечен как this 
// Что позволяет использовать этот метод через точку
public static IConfiguration WithDefaultLoader(this IConfiguration configuration, Action<LoaderService> setup = null, IEnumerable<IRequester> requesters = null);

Кроме того, используются дженерик функции. Я покажу, как с этим бороться.

Но, к сожалению, красивый код на C# превращается в монстра на 1С. Но зато есть примеры, как с этим бороться

/Получить типизированную дженерик функцию
//TElement QuerySelector<TElement>(this IParentNode parent, string selectors) 
Функция ПолучитьДжененрикМетодИнфо(тип,ИмяМетода,типПараметра)
	method = Врап.ТипКакОбъект(тип).GetMethod(ИмяМетода);
	generic = method.MakeGenericMethod(типПараметра);
	return generic;
	
КонецФункции

// Когда есть перегрузка методов нужно искать метод по имени и типам параметров
// Task<IDocument> Navigate<TElement>(this TElement element)
// string Text<T>(this T element)
Функция ПолучитьМетодИнфоОдинДженерикТип(тип,ИмяМетода,типПараметра)
	
	Для Каждого m in Врап.ТипКакОбъект(тип).GetMethods() Цикл
		
		параметры = m.GetParameters();
		if  (m.Name = ИмяМетода)   
			И (параметры.Length = 1)
			И (Врап.ТипКакОбъект(параметры.GetValue(0).ParameterType).IsGenericParameter)
			Тогда
			method = m;
			break;
		КонецЕсли
	КонецЦикла;
	generic = method.MakeGenericMethod(типПараметра);
	return generic;
	
КонецФункции

Функция  ПолучитьМетодИнфо(тип,ИмяМетода)
	method = Врап.ТипКакОбъект(тип).GetMethod(ИмяМетода);
	
	return method;
	
КонецФункции

Функция  ПолучитьПропертиИнфо(тип,ИмяСвойства)
	свойство = Врап.ТипКакОбъект(тип).GetProperty(ИмяСвойства);
	
	return свойство;
	
КонецФункции

Функция ПолучитьМетодИнфо2Параметра(тип,ИмяМетода)
	
	Для Каждого m in Врап.ТипКакОбъект(тип).GetMethods() Цикл
		
		параметры = m.GetParameters();
		if  (m.Name = ИмяМетода)   
			И (параметры.Length = 2)
			Тогда
			method = m;
			break;
		КонецЕсли
	КонецЦикла;
	
	return method;
	
КонецФункции

Функция Получить_SBAppend()
	
	
	Для Каждого m in Врап.ТипКакОбъект(StringBuilder).GetMethods() Цикл
		
		параметры = m.GetParameters();
		if ( (m.Name = "Append")   
			И (параметры.Length = 1) 
			И (Врап.ТипКакОбъект(параметры.GetValue(0).ParameterType).Equals(String)))   
			Тогда
			возврат  m
		КонецЕсли
	КонецЦикла;
	
	возврат Неопределено
КонецФункции

Процедура AngleSharpFormНажатие(Элемент)
	// Вставить содержимое обработчика.
	
	ПутьКСборке="d:\Vs2015Programs\TestScriptingAPI\TestScriptingAPI\bin\Debug\AngleSharp.dll";
	WebsiteUrl = "http://localhost:54361";
	// Получим используемые типы
        AngleSharp_ConfigurationExtensions = Врап.ПолучитьТип("AngleSharp.ConfigurationExtensions");

	BrowsingContext = Врап.ПолучитьТип("AngleSharp.BrowsingContext");
	BrowsingContextExtensions = Врап.ПолучитьТип("AngleSharp.BrowsingContextExtensions");
	ApiExtensions = Врап.ПолучитьТип("AngleSharp.Extensions.ApiExtensions");
	
	// Получим типы нужных интерфейсов
	IHtmlAnchorElement = Врап.ПолучитьТип("AngleSharp.Dom.Html.IHtmlAnchorElement");
	IHtmlFormElement = Врап.ПолучитьТип("AngleSharp.Dom.Html.IHtmlFormElement");
	IElement=Врап.ПолучитьТип("AngleSharp.Dom.IElement");
	
	// Получим типизированные функции	
	QuerySelector_AnchorElement = ПолучитьДжененрикМетодИнфо(ApiExtensions, "QuerySelector",IHtmlAnchorElement);
	QuerySelector_FormElement =   ПолучитьДжененрикМетодИнфо(ApiExtensions, "QuerySelector",IHtmlFormElement);
	
	ApiExtensions_Navigate=ПолучитьМетодИнфоОдинДженерикТип(ApiExtensions,"Navigate",IHtmlAnchorElement);
	ApiExtensions_Text= ПолучитьМетодИнфоОдинДженерикТип(ApiExtensions,"Text",IElement);
	
	configuration = Configuration.Default;
	
	configuration = AngleSharp_ConfigurationExtensions.WithDefaultLoader(configuration);
	configuration = AngleSharp_ConfigurationExtensions.WithCookies(configuration);
	
	context = BrowsingContext.New(configuration);
	
	// Загрузим начальную страницу
	BrowsingContextExtensions.OpenAsync(context, WebsiteUrl).Wait();
	doc = context.Active;
	
	// Получим ссылку содержащий адрес страницы для авторизации
	//<a class="log-in" href="/Home/LogIn">log in here</a>
	
	HtmlAnchorElement = Врап.MethodInfo_Invoke(QuerySelector_AnchorElement,Неопределено,doc, "a.log-in");
	//  var HtmlAnchorElement = AngleSharp.Extensions.ApiExtensions.QuerySelector<AngleSharp.Dom.Html.IHtmlAnchorElement>(doc, "a.log-in");
	// И перейдем на страницу авторизации
	//  doc = ApiExtensions.Navigate(HtmlAnchorElement).Result;
	
	doc = Врап.MethodInfo_Invoke(ApiExtensions_Navigate,Неопределено,HtmlAnchorElement).Result;
	
	
	//  doc = context.Active;
	
	// Получим форму
	//скрытый элемент для верификации который нужно отправить
	//<input name="__RequestVerificationToken" type="hidden" value="2Y-sFIY9JBZc6wc7antGFsBPG1GoiYCbVDtS0khv3JRkcG8CuN69pS3tAZrSiTevGkBjzpTF9AnuK8tZEUrjqn4qB_lbF4dVxsQBubYZkck1">
	HtmlFormElement = Врап.MethodInfo_Invoke(QuerySelector_FormElement,Неопределено,doc, "form");
	
	//   var HtmlFormElement = AngleSharp.Extensions.ApiExtensions.QuerySelector<AngleSharp.Dom.Html.IHtmlFormElement>(doc, "form");
	
	d = Врап.СоздатьОбъект(Dictionary);
	d.Add("User", "User");
	d.Add("Password", "secret");
	// Авторизуемся установив нужные поля и отправим Post запрос на сервер
	ApiExtensions.Submit(HtmlFormElement, d).Wait();
	
	
	doc = context.Active;
	
	// получим ссылку на искомую страницу
	//<a class="secret-link" href="/Home/Secret">our secret</a>
	HtmlAnchorElement = Врап.MethodInfo_Invoke(QuerySelector_AnchorElement,null, doc, "a.secret-link");
	// HtmlAnchorElement = AngleSharp.Extensions.ApiExtensions.QuerySelector<AngleSharp.Dom.Html.IHtmlAnchorElement>(doc, "a.secret-link");
	
	// Перейдем по ссылке
	// doc = ApiExtensions.Navigate(HtmlAnchorElement).Result;
	doc = Врап.MethodInfo_Invoke(ApiExtensions_Navigate,null,HtmlAnchorElement).Result;
	// В первом селекторе параграфе лежит искомая строка
	//<p>The answer to everything is <span id="secret">42</span>.</p>
	селектор=doc.QuerySelector("p");
	резулт = Врап.MethodInfo_Invoke(ApiExtensions_Text,null,селектор);
	//  резулт =The answer to everything is 42
	Сообщить(резулт);
КонецПроцедуры

Одним из удобных способов это использовать 

https://github.com/dotnet/roslyn

 https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples

Для использования Scripting-API создал сборку. Проинсталлировал 

Install-Package Microsoft.CodeAnalysis.Scripting

Создал класс
namespace ScriptApiDlls

{ 
   public class КлассДляВычесленияВыражений 
    { 
      public static  Microsoft.CodeAnalysis.Scripting.ScriptOptions Опции { get { return Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default; } } 
       
        public static object Вычислить(string Код, Microsoft.CodeAnalysis.Scripting.ScriptOptions опции ) 
        { 
            return Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(Код, опции).Result;

        }

    } 
}

И теперь можно с удобством использовать код, написанный на C#.

Для использования DLL не из GAC их нужно поместить в домен приложения. В данном случае это каталог программы.

врап=новый COMОбъект("NetObjectToIDispatch45");
	КлассДляВычесленияВыражений=Врап.ПолучитьТипИзСборки("ScriptApiDlls.КлассДляВычесленияВыражений",ПутьКДлл+"ScriptApiDlls.dll");

	Опции=КлассДляВычесленияВыражений.Опции;
	Катаог=Врап.ПолучитьТип("System.AppDomain").CurrentDomain.BaseDirectory;
	ИмяСборкиAngleSharp=Врап.ПолучитьТип("System.IO.Path").Combine(Катаог,"AngleSharp.dll");
	СтрокаКода = "string WebsiteUrl = ""http://localhost:54361"";
	|var configuration = Configuration.Default.WithDefaultLoader().WithCookies();
	|    var context = BrowsingContext.New(configuration);
	|    context.OpenAsync(WebsiteUrl).Wait();
	|   
	|   var elem=context.Active.QuerySelector<IHtmlAnchorElement>(""a.log-in"");
	|   if (elem==null) return context.Active.Body.InnerHtml;
	|   elem.Navigate().Wait();
	|    context.Active.QuerySelector<IHtmlFormElement>(""form"").Submit(new
	|    {
	|        User = ""User"",
	|        Password = ""secret""
	|    }).Wait();
	|context.Active.QuerySelector<IHtmlAnchorElement>(""a.secret-link"").Navigate().Wait();
	|
	|return context.Active.QuerySelector(""p"").Text();
	|";
	
	scr = Опции
	.WithReferences(ИмяСборкиAngleSharp)
	.WithImports("System","AngleSharp","AngleSharp.Dom.Html","AngleSharp.Extensions");
	
	
	резулт = КлассДляВычесленияВыражений.Вычислить(СтрокаКода,scr);
	Сообщить(резулт);

А сейчас я расскажу, как можно значительно ускорить процесс использования AngleSharp. Вообще скорость вызовов методов через обертку составляет порядка 20 000 вызовов методов в секунду. Но дело в том, что все классы в библиотеке являются не публичными, передаются только интерфейсы. И вот здесь при передаче ссылок .Net начинает тормозить.  Но решение было найдено через использование типизированных коллекций и использование PropertyInfo

stopWatch = Врап.СоздатьОбъект("System.Diagnostics.Stopwatch");
	
	stopWatch.Start();
	
	Client = Врап.СоздатьОбъект(HttpClient);
	Client.BaseAddress = Врап.СоздатьОбъект("System.Uri","https://en.wikipedia.org");
	res = Client.GetStringAsync("wiki/List_of_The_Big_Bang_Theory_episodes").Result;
	
	parser = Врап.СоздатьОбъект("AngleSharp.Parser.Html.HtmlParser");
	//Just get the DOM representation
	document = parser.Parse(res);
	СелекторСтроки = "tr.vevent";
	Тз=новый ТаблицаЗначений;
	Колонки=Тз.Колонки;
	Колонки.Добавить("НомерСерии");
	Колонки.Добавить("НомерСерииВСезоне");
	Колонки.Добавить("Название");
	Колонки.Добавить("Режиссер");
	Колонки.Добавить("Автор");
	Колонки.Добавить("Дата");
	Колонки.Добавить("Код");
	Колонки.Добавить("Просмотров");
	
	Строки =document.QuerySelectorAll(СелекторСтроки);
	
	stopWatch.Stop();
	Сообщить("Скачка и парсинг");
	ВывестиВремя(stopWatch,истина);
	Dom_INode=Врап.ПолучитьТип("AngleSharp.Dom.INode");
	TextContentСвойство=ПолучитьПропертиИнфо(Dom_INode,"TextContent");
	
	IHtmlTableRowElement=Врап.ПолучитьТип("AngleSharp.Dom.Html.IHtmlTableRowElement");
	ЯчейкиТаблицыСвойство=ПолучитьПропертиИнфо(IHtmlTableRowElement,"Cells");
	
	IEnumerator=Врап.ПолучитьТип("System.Collections.IEnumerator");
	CurrentСвойство=ПолучитьПропертиИнфо(IEnumerator,"Current");
	stopWatch.Restart();
	
	//  Строки=Врап.МассивИзЭнумератора(Строки,IHtmlTableRowElement);//Врап.ПолучитьПеречислитель(строки);
	Строки=Врап.ПолучитьТипизированныйПеречислитель(Строки,IHtmlTableRowElement);	
	
	Для каждого стр из Строки Цикл
		сч=0;
		стрТз=Тз.Добавить();
		Ячейки=ЯчейкиТаблицыСвойство.GetValue(стр);//Ячейки=стр.Cells;
		
		//Ячейки=Врап.МассивИзЭнумератора(Ячейки,Dom_INode);
		Ячейки=Врап.ПолучитьТипизированныйПеречислитель(Ячейки,Dom_INode);
		
		Для каждого Ячейка из Ячейки Цикл
			стрТз[сч]=TextContentСвойство.GetValue(Ячейка);//Ячейка.TextContent;
			
			сч=сч+1;
			Если сч=8 Тогда
				прервать
			КонецЕсли;
		КонецЦикла
	КонецЦикла;
	
	stopWatch.Stop();
	
	
	Сообщить("Время выполнения =");
	ВывестиВремя(stopWatch,истина);
	
	Тз.ВыбратьСтроку();	

Используя MetodInfo, тоже можно ускорить процесс процентов на 20%. 

stopWatch = Врап.СоздатьОбъект("System.Diagnostics.Stopwatch");
	
	КоличествоИтераций=20000;
	stopWatch.Start();
	
	StringBuilder_Append=Получить_SBAppend();
	StringBuilder_ToString=ПолучитьМетодИнфо2Параметра(StringBuilder,"ToString");
	стр="";
	НачВремя=ТекущаяДата();
	stopWatch.Restart();
	SB =  врап.СоздатьОбъект("System.Text.StringBuilder");
	Для сч=1 по КоличествоИтераций Цикл
		//	SB.Append(Строка(сч));
		Врап.MethodInfo_Invoke(StringBuilder_Append,SB,Строка(сч));
		
	КонецЦикла;
	
	stopWatch.Stop();
	
	стр=SB.ToString();
	Сообщить(СтрДлина(стр));
	
	ВывестиВремя(stopWatch,истина);
	
	stopWatch.Restart();
	
	SB2 =  врап.СоздатьОбъект("System.Text.StringBuilder");
	Для сч=0 по SB.Length-1 Цикл
		
		//SB2.Append(SB.ToString(сч,1));
		Врап.MethodInfo_Invoke(StringBuilder_Append,SB2,Врап.MethodInfo_Invoke(StringBuilder_ToString,SB,сч,1));
		
	КонецЦикла;
	stopWatch.Stop();
	
	стр=SB2.ToString();
	Сообщить(СтрДлина(стр));
	
	
	
	
	Сообщить("Время выполнения =");
	ВывестиВремя(stopWatch,истина);
Для получения атрибута нужно применить следующий код
В 
Картинки = doc.QuerySelectorAll("img[src]");

Для каждого стр из Картинки Цикл
 адрес=Врап.ПолучитьИнтерфейс(стр,"IElement").GetAttribute("src");
  
КонецЦикла

 Так же приеры использования AngleSharp на .Net Core  есть здесь

Кроссплатформенное использование классов .Net в 1С через Native ВК. Или замена COM на Linux II

и здесь

.Net Core, 1C, динамическая компиляция, Scripting API

Благодарю всех, кто дошел до конца. Надеюсь, мои труды вам пригодятся.

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Сергей Смирнов (Serginio) 11.03.16 11:50
Я уже не буду перегружать статью. Но можно использовать скрипты для заполнения данных 1С
str = "public class ВычислительAngleSharp
    |{
     |public  static void ЗаполнитьТз(dynamic ТЗ)
     |   {
	 |
	 |
     |       var config = Configuration.Default.WithDefaultLoader();
     |       // Устанавливаем адрес страницы сайта
     |       var address = ""https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes"";
     |       // загружаем страницу и разбираем её
     |       var document = BrowsingContext.New(config).OpenAsync(address).Result;
     |       // Используем CSS селектор для получения строк таблицы с классом  и выбрать из этой строки 3 колонку 
     |       var rowSelector = ""tr.vevent"";
     |       var Строки = document.QuerySelectorAll<IHtmlTableRowElement>(rowSelector);
     |       foreach (var str in Строки)
     |       {
     |           dynamic стрТз = ТЗ.Добавить();
     |           var ячейки = str.Cells;
     |           for (int i = 0; i < Math.Min(ячейки.Length, 8); i++)
     |           {
     |
     |               стрТз.Установить(i, ячейки[i].TextContent);
     |
     |           }
     |
     |
     |       }
     |
     |   }
    |}
|
|return  new Action<dynamic>(ВычислительAngleSharp.ЗаполнитьТз);";

            Каталог=Врап.ПолучитьТип("System.AppDomain").CurrentDomain.BaseDirectory;
            КаталогGAC = Path.GetDirectoryName(Врап.ТипКакОбъект(string).Assembly.Location);
			
			ПутьКДлл="d:\Vs2015Programs\ScriptApiDlls\ScriptApiDlls\bin\Debug\";
			КлассДляВычесленияВыражений=Врап.ПолучитьТипИзСборки("ScriptApiDlls.КлассДляВычесленияВыражений",ПутьКДлл+"ScriptApiDlls.dll");
	       
	        Опции=КлассДляВычесленияВыражений.Опции;

              scr = Опции
                .WithReferences(Каталог + "\AngleSharp.dll", КаталогGAC + "\Microsoft.CSharp.dll")
                //  .WithReferences(typeof(Configuration).Assembly)
                .WithImports("System", "AngleSharp", "AngleSharp.Dom.Html", "AngleSharp.Extensions");

               result = КлассДляВычесленияВыражений.Вычислить(str, scr);
			  Тз=новый ТаблицаЗначений;
	Колонки=Тз.Колонки;
	Колонки.Добавить("НомерСерии");
	Колонки.Добавить("НомерСерииВСезоне");
	Колонки.Добавить("Название");
	Колонки.Добавить("Режиссер");
	Колонки.Добавить("Автор");
	Колонки.Добавить("Дата");
	Колонки.Добавить("Код");
	Колонки.Добавить("Просмотров");

	врап.ВыполнитьДелегат(result,Тз);
	
	тз.ВыбратьСтроку();
...Показать Скрыть
2. Юрий Пермитин (YPermitin) 11.03.16 13:55
(1) Serginio, классные статьи, тоже интересная.

Но есть парочку НО:
1. Материал подается в неудобочитаемом виде.
2. Мало практических примером, близких к реальности.

ИМХО. Без обид.
3. Сергей Смирнов (Serginio) 11.03.16 14:40
(2) Спасибо. Может подкинешь идею? Могу в свободное время взяться за конкретный пример или кому то помочь с реальной задачей
4. Александр Савченко (invertercant) 11.03.16 16:11
(3) Serginio, я был бы рад увидеть статью про валидаторы данных применительно к 1с, особенно приятно если бы это получилось хорошо. Понимаю что тема большая, но хоть что то. Вот например по этой тематике https://habrahabr.ru/post/246521/
5. Сергей Смирнов (Serginio) 11.03.16 16:21
(4) Да уж. Можно смотреть на валидаторы внутри ASP.Net MVC. Там генерятся валидаторы как на JavaScript так и на уровне HTML5
http://stephenwalther.com/archive/2012/03/13/html5-form-validation
6. Александр Савченко (invertercant) 11.03.16 16:56
(5) Serginio, У тебя по ссылке валидация форм, хотя хотелось бы иметь валидацию произвольных данных. На входе - произвольный набор проверяемых данных и правила которым он должен удовлетворять. На выходе - ошибки, если есть. Если ошибок нет - валидация прошла успешно. Например, ты грузишь что то из какого нибудь excel или внешней базы и надо чтобы данные удовлетворяли определенным условиям. Часто фарш в данных обнаруживается значительно позднее чем мог бы.
8. Александр Савченко (invertercant) 11.03.16 17:15
(7) Serginio, Это была идея для статьи чтобы не придумывать велосипед, а взять готовый на NET, если такой существует.
9. Сергей Смирнов (Serginio) 11.03.16 17:19
(8) Они существуют. Только не универсальные. XML, HTML, C# итд.
10. Сергей Смирнов (Serginio) 11.03.16 17:29
Что касается LIVR то прочтя https://github.com/koorchik/LIVR вроде как на .Net нет библиотеки
11. Сергей Смирнов (Serginio) 11.03.16 20:15
Вот пример парсинга мисты

 handler = врап.СоздатьОбъект(HttpClientHandler);

    

    cookieContainer = Врап.СоздатьОбъект("System.Net.CookieContainer");

  

   handler.AutomaticDecompression=Врап.OR(DecompressionMethods.GZip,DecompressionMethods.Deflate) ;

      handler.CookieContainer=cookieContainer;

      cookieContainer.Add(Врап.СоздатьОбъект("System.Net.Cookie","__DDOS_COOKIE", "6c950441aeaf13f026a3aa8c0fee6df2", "/", "http://www.forum.mista.ru";));

   handler.UseCookies=истина;

//Pragma: no-cache

   Клиент = Врап.СоздатьОбъект(HttpClient,handler);

   DefaultRequestHeaders=Клиент.DefaultRequestHeaders;

   

   DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,*/*");



   WebsiteUrl = "http://www.forum.mista.ru/";

   Клиент.BaseAddress = Врап.СоздатьОбъект("System.Uri",WebsiteUrl);



            

    res = Клиент.GetStringAsync("index.php").Result;

    document = parser.Parse(res);

    

    селектор="table#tm td.ct";

    



    Ячейки= document.QuerySelectorall(селектор);

    

    Для каждого Ячейка Из Ячейки Цикл

        

        Аннотации=Ячейка.QuerySelectorall("a");

    Сообщить("=============================");    

Для каждого Аннотация Из Аннотации Цикл



Сообщить(Аннотация.TextContent+"="+Аннотация.PathName+Аннотация.Search);    



КонецЦикла; 

        

    

    КонецЦикла; 

Врап.ЗакрытьРесурс(HttpClient);
...Показать Скрыть
12. Сергей Смирнов (Serginio) 12.03.16 10:44
Обратите внимание
Аннотация= document.QuerySelector("a.log-in");
    //Полный относительный путь Аннотация.PathName+Аннотация.Search
    // Но в данном случае Аннотация.Search просто нет
    // Если использовать BrowsingContext то полный путь содержится в Href

    res= Клиент.GetStringAsync(Аннотация.PathName).Result;
...Показать Скрыть
13. Сергей Смирнов (Serginio) 13.03.16 14:28
Один из основных интерфейсов

 [DomName("Node")]
    public interface INode : IEventTarget, IMarkupFormattable
    {
        /// <summary>
        /// Gets a string representing the base URL. 
        /// </summary>
        [DomName("baseURI")]
        String BaseUri { get; }

        /// <summary>
        /// Gets the base url.
        /// </summary>
        Url BaseUrl { get; }

        /// <summary>
        /// Gets a string containing the name of the Node. The structure of the
        /// name will differ with the name type. 
        /// </summary>
        [DomName("nodeName")]
        String NodeName { get; }

        /// <summary>
        /// Gets a live NodeList containing all the children of this node.
        /// Being live means that if the children of the node change, the
        /// NodeList object is automatically upd ated.
        /// </summary>
        [DomName("childNodes")]
        INodeList ChildNodes { get; }

        /// <summary>
        /// Clones the node, and optionally, all of its contents.
        /// By default, it clones the content of the node.
        /// </summary>
        /// <param name="deep">
        /// Optionally: Sets if all of the content should be cloned as well.
        /// </param>
        /// <returns>The cloned node.</returns>
        [DomName("cloneNode")]
        INode Clone(Boolean deep = true);

        /// <summary>
        /// Determines if two nodes are equal.
        /// </summary>
        /// <param name="otherNode">
        /// The node to be compared to the node that is executing the method.
        /// </param>
        /// <returns>
        /// True if the node specified in the otherNode parameter is equal to
        /// the current node.
        /// </returns>
        [DomName("isEqualNode")]
        Boolean Equals(INode otherNode);

        /// <summary>
        /// Compares the position of two nodes in a document.
        /// </summary>
        /// <param name="otherNode">
        /// The node to be compared to the reference node, which is the node
        /// executing the method.
        /// </param>
        /// <returns>The relation between the two nodes.</returns>
        [DomName("compareDocumentPosition")]
        DocumentPositions CompareDocumentPosition(INode otherNode);

        /// <summary>
        /// Cleans up all the text nodes under this element, i.e. merges
        /// adjacent and removes empty text nodes.
        /// </summary>
        [DomName("normalize")]
        void Normalize();

        /// <summary>
        /// Gets the Document that this node belongs to. If no document is
        /// associated with it, returns null.
        /// </summary>
        [DomName("ownerDocument")]
        IDocument Owner { get; }

        /// <summary>
        /// Gets an Element that is the parent of this node. If the node has no
        /// parent, or if that parent is not an Element, this property returns
        /// null.
        /// </summary>
        [DomName("parentElement")]
        IElement ParentElement { get; }

        /// <summary>
        /// Gets a node that is the parent of this node. If there is no such
        /// node, like if this node is the top of the tree or if doesn't
        /// participate in a tree, this property returns null.
        /// </summary>
        [DomName("parentNode")]
        INode Parent { get; }

        /// <summary>
        /// Returns true if other is an inclusive descendant of the context
        /// object, and false otherwise (including when other is null).
        /// </summary>
        /// <param name="otherNode">The Node to check the childs for.</param>
        /// <returns>
        /// True if the given node is contained within this Node, otherwise
        /// false.
        /// </returns>
        [DomName("contains")]
        Boolean Contains(INode otherNode);

        /// <summary>
        /// Gets a Node representing the first direct child node of the node,
        /// or null if the node has no child.
        /// </summary>
        [DomName("firstChild")]
        INode FirstChild { get; }

        /// <summary>
        /// Gets a node representing the last direct child node of the node,
        /// or null if the node has no child.
        /// </summary>
        [DomName("lastChild")]
        INode LastChild { get; }

        /// <summary>
        /// Gets a Node representing the next node in the tree, or null if
        /// there isn't such node.
        /// </summary>
        [DomName("nextSibling")]
        INode NextSibling { get; }

        /// <summary>
        /// Gets a Node representing the previous node in the tree, or null if
        /// there isn't such node.
        /// </summary>
        [DomName("previousSibling")]
        INode PreviousSibling { get; }

        /// <summary>
        /// Indicates whether or not a namespace is the default namespace for a
        /// document.
        /// </summary>
        /// <param name="namespaceUri">
        /// The namespace to be compared to the default namespace.
        /// </param>
        /// <returns>
        /// True if the given namespace URI is the default for the current
        /// document.
        /// </returns>
        [DomName("isDefaultNamespace")]
        Boolean IsDefaultNamespace(String namespaceUri);

        /// <summary>
        /// Gets the Uniform Resource Identifier (URI) of the namespace
        /// associated with a namespace prefix, if any.
        /// </summary>
        /// <param name="prefix">The namespace prefix.</param>
        /// <returns>The URI of the namespace.</returns>
        [DomName("lookupNamespaceURI")]
        String LookupNamespaceUri(String prefix);

        /// <summary>
        /// Gets the namespace prefix associated with a Uniform
        /// Resource Identifier (URI), if any.
        /// </summary>
        /// <param name="namespaceUri">The URI.</param>
        /// <returns>The namespace prefix associated with the URI.</returns>
        [DomName("lookupPrefix")]
        String LookupPrefix(String namespaceUri);

        /// <summary>
        /// Gets an unsigned short representing the type of the node. 
        /// </summary>
        [DomName("nodeType")]
        NodeType NodeType { get; }

        /// <summary>
        /// Gets or sets a string representing the value of an object. For most
        /// node types, this returns null and any set operation is ignored.
        /// </summary>
        [DomName("nodeValue")]
        String NodeValue { get; set; }

        /// <summary>
        /// Gets or sets the textual content of an element and all its
        /// descendants.
        /// </summary>
        [DomName("textContent")]
        String TextContent { get; se t; }

        /// <summary>
        /// Gets an indicator if the element has any child nodes, or not.
        /// </summary>
        [DomName("hasChildNodes")]
        Boolean HasChildNodes { get; }

        /// <summary>
        /// Inserts a node as the last child node of this element.
        /// </summary>
        /// <param name="child">The node to be appended.</param>
        /// <returns>The appended Node.</returns>
        [DomName("appendChild")]
        INode AppendChild(INode child);

        /// <summary>
        /// Inserts the newElement immediately before the referenceElement.
        /// </summary>
        /// <param name="newElement">The node to be inserted.</param>
        /// <param name="referenceElement">
        /// The existing child element that will succeed the new element.
        /// </param>
        /// <returns>The inserted node.</returns>
        [DomName("insertBefore")]
        INode InsertBefore(INode newElement, INode referenceElement);

        /// <summary>
        /// Removes a child node from the current element, which must be a
        /// child of the current node.
        /// </summary>
        /// <param name="child">The child to be removed.</param>
        /// <returns>The removed node.</returns>
        [DomName("removeChild")]
        INode RemoveChild(INode child);

        /// <summary>
        /// Replaces one child node of the current one with the second one
        /// given in the parameters.
        /// </summary>
        /// <param name="newChild">The child to be inserted.</param>
        /// <param name="oldChild">The child to be removed.</param>
        /// <returns>The old node, if any.</returns>
        [DomName("replaceChild")]
        INode ReplaceChild(INode newChild, INode oldChild);
    }
...Показать Скрыть
14. Юрий Пермитин (YPermitin) 14.03.16 08:58
(3) Serginio, можно написать обработку, которая сжимает или приводит к одному виду изображения в базе с помощью средств .NET платформы, да еще и Parallel прикрутить для ускорения.
Еще было бы интересно рассказать про стандартные возможности .NET'а слежения за изменением в каталоге, передача файлов по сети с помощью фоновой интеллектуальной службы BITS (Windows).

Может об этом уже писали конечно, не искал специально.
15. Сергей Смирнов (Serginio) 14.03.16 09:51
(14) Большое спасибо за совет.
.NET(C#) для 1С. Динамическая компиляция класса обертки для использования .Net событий в 1С через ДобавитьОбработчик или ОбработкаВнешнегоСобытия
Там есть подписка на события System.IO.FileSystemWatcher

Насчет картинок была статья Elisy Обработка изображений 1С средствами .Net framework
Я на мисте предлагал http://www.forum.mista.ru/topic.php?id=758131&page=3#229
Но как то мало это народ интересует.
Нужны какие то простые, но понятные примеры типа Строка в дату. Универсальное решение. Применение NetObjetToIDispatch45
http://www.forum.mista.ru/topic.php?id=763810&page=1

Например больше всего популярностью пользуется Использование классов .Net в 1С для новичков
Главное заинтересовать, а там дальше пойдет.
16. Сергей Смирнов (Serginio) 15.03.16 10:36
Еще один пример парсинга


Client = Врап.СоздатьОбъект(HttpClient);
	Client.BaseAddress = Врап.СоздатьОбъект("System.Uri","http://www.5lb.ru/catalog/");
	Стр="/cgi-bin/mp/page.pl?id=32&m=docs&producer_id=0&price_min=220&price_max=8520&sele­ct_rating=0&unit_6=1&min_6=114&max_6=1588&unit_4=1&min_4=20&­max_4=25&unit_5=1&min_5=30&max_5=500&unit_7=1&min_7=12&max_7­=40&unit_3=1&min_3=60&max_3=600&unit_1=1&min_1=12&max_1=12&u­nit_2=1&min_2=500&max_2=1000";
		
	
// Тест строки с параметрами из фиддлера	
	res = Client.GetStringAsync(стр).Result;
	
	// Посмотрим результат
	// Можно посмотреть страницу используя например
	//http://filyanin.ru/8-vizualnyy-HTML-onlayn-redaktor.html
	Текст=Новый ТекстовыйДокумент;
	Текст.УстановитьТекст(res);
	Текст.Показать();

	
	res = Client.GetStringAsync("bcaa.html").Result;
	document = parser.Parse(res);

		
	Форма=document.QuerySelectorAll("form").get_Item(1);
	ссылка=	СокрЛП(Форма.Action);
		

	структура=новый структура;
	Для каждого Элемент Из Форма.QuerySelectorAll("input")  Цикл
		Элем=Врап.ПолучитьИнтерфейс(Элемент,"IHtmlInputElement");
		//Нужны только Input элементы
		//Выберем все элементы Input
		// и запишем их имена и значения
		
		Если ПустаяСтрока(Элем.Name) Тогда
		   Продолжить;
	    КонецЕсли;
	
		Если Элем<> Неопределено Тогда
			Сообщить(Элем.Name+"="+Элем.Value);

			структура.Вставить(Элем.Name,Элем.Value);
			
			
		КонецЕсли; 
		
		
	КонецЦикла; 		

	
	// Можно изменить значение
	//структура.price_max="3000";
	структура.Вставить("producer_id","0");
	структура.Вставить("select_rating","0");
	сб=Врап.СоздатьОбъект(StringBuilder,ссылка+"?");
	//var uri = new Uri(builder.ToString(), dontEscape: true);
	
	// Создадим строку запроса. Для метода Get
	// так как Форма.Method=""
	Для каждого стр Из структура  Цикл
		
		
		сб.AppendFormat("{0}={1}&",HttpUtility.UrlPathEncode(стр.Ключ),HttpUtility.UrlPathEncode(стр.Значение),0);
		
		
	КонецЦикла; 

	
	стр=сб.ToString(0,сб.Length-1); 
	Сообщить(стр);
	//Сделаем запрос по относительному пути
	//Так как основной путь уже прописан в BaseAddress
	res = Client.GetStringAsync(стр).Result;
	
	// Посмотрим результат
	// Можно посмотреть страницу используя например
	//http://filyanin.ru/8-vizualnyy-HTML-onlayn-redaktor.html
	Текст=Новый ТекстовыйДокумент;
	Текст.УстановитьТекст(res);
	Текст.Показать();
	
	document = parser.Parse(res);
...Показать Скрыть
17. Гость 18.03.16 15:55
Подскажите, пожалуйста, где взять файл AngleSharp.dll?
18. Сергей Смирнов (Serginio) 18.03.16 15:57
(17) http://files.rsdn.ru/19608/AndleSharpScriptDlls.zip

Но за версиями лучше следить здесь https://www.nuget.org/packages/AngleSharp/
https://anglesharp.github.io/
Кстати новая версия вышла. Проверю. Обновил
19. Гость 21.03.16 12:51
Спасибо! А то скачал сборку с Гитхаба, а как ее использовать не понял..
20. Сергей Смирнов (Serginio) 24.03.16 15:16
В составе AngleSharp.Scripting для парсинга сайтов с использованием JS
https://github.com/AngleSharp/AngleSharp/wiki/Examples

Есть парсер
https://github.com/sebastienros/jint

Вот пример использования

Engine=врап.ПолучитьТипИзСборки("Jint.Engine","d:\Vs2015Programs\WpfApplication1\WpfApplication1\bin\Debug\Jint.dll"); 



    JsValue=врап.ПолучитьТип("Jint.Native.JsValue");
      jint =  Врап.СоздатьОбъект (Engine);

            html = "
            |var o = { };
            |o.Foo = 'bar';
            |o.Baz = 42.0001;
            |o.Blah = o.Foo + o.Baz;
            |o.B64='YWRtaW5AbG9jYWxob3N0OnNlY3JldAo==';
            |o.Int =5;
            |if (o.Blah != 'bar42.0001') throw TypeError;
            |
            |function fib(n){
            |    if (n < 2)
            |    {
            |        return n;
            |    }
            |
            |    return fib(n - 1) + fib(n - 2);
            |}
            |
            |if (fib(3) != 2) throw TypeError;
            |";

            jint.Execute(html);
            o = jint.GetValue("o").ToObject();

            Baz = o.Baz;
            Сообщить(Baz);
            Сообщить(o.Blah);
            Сообщить(o.Int);            
            BitConverter=Врап.ПолучитьТип("System.BitConverter");
            Convert  =Врап.ПолучитьТип("System.Convert");
            data = Convert.FromBase64String(o.B64);
                        Сообщить(BitConverter.ToString(data));
          // int ii = o.Int;


            fib = jint.GetValue("fib");
            
            double5=Врап.ChangeType("System.Double",5);
            double5=Врап.СоздатьОбъект(JsValue,double5);
            Фиб5=врап.ВыполнитьМетод(fib,"Invoke",double5).AsNumber();

        //    Фиб5=fib.Invoke(double5).AsNumber();

            Сообщить(Фиб5);
...Показать Скрыть
21. Сергей Смирнов (Serginio) 24.03.16 15:17
Можно передавать свои объекты и и использовать их при выполнении скриптов . Пример на C#




html = @"
var document = { };
document.cookie = '__DDOS_COOKIE =6c950441aeaf13f026a3aa8c0fee6df2; max-age=604800; path=/';
          ";

            jint.Execute(html);
            dynamic document = jint.GetValue("document").ToObject();
            textBox.AppendText(document.cookie.ToString() + Environment.NewLine);

            html = @"document.cookie = '__DDOS_COOKIE =6c950441aeaf13f026a3aa8c0fee6df2; max-age=604800; path=/';
            var nc = function() {
                return
document.cookie.indexOf('__DDOS_COOKIE=6c950441aeaf13f026a3aa8c0fee6df2') == -1;
            };
            var w = function() { document.body.innerHTML = document.getElementsByTagName('noscript')[0].textContent; };
            if (!window.opera)
            {
                if (!nc()) { window.location.reload(true); }
                var r = function() { if (nc()) w(); };
            }
            else {
                var r = function() {
                    if (!nc()) { window.location.reload(true); }
                    else { w(); }
                }
            }";

            dynamic document1 = new ExpandoObject();
            document1.cookie = "";
            document1.body= new ExpandoObject();
            dynamic window= new ExpandoObject();
            window.opera = false;
            window.location = new ExpandoObject();
            window.location.reload = (Action<bool>)(® => { });

            jint.SetValue("document", document1);
            jint.SetValue("window", window);

            jint.Execute(html);
           
            textBox.AppendText(document1.cookie.ToString() + Environment.NewLine);
...Показать Скрыть
22. Сергей Смирнов (Serginio) 13.04.16 09:57
Проверил работают такие конструкции поиска

Поиск div с id начинающихся на "row"
СелекторСтроки = "div[id^=row]";


Найти аннотацию с классом pagenav и содеращую текст 'Вперёд'
a.pagenav:contains('Вперёд')
23. Сергей Смирнов (Serginio) 26.04.16 10:17
Я бы еще добавил Silenium прежде всего как построитель DOM в заскриптованных таблицах http://www.seleniumhq.org/docs/05_selenium_rc.jsp#c
http://scraping.pro/example-of-scraping-with-selenium-webdriver-in-csharp/

Для того что бы добраться до сформированного DOM можно использовать вместо PageSource
вычисляемый скрипт

http://stackoverflow.com/questions/26584215/selenium-page-source-does-not-return-modified-dom-tree

var pageSource = (string)driver.ExecuteScript("return document.body.outerHTML");
24. Сергей Смирнов (Serginio) 01.06.16 21:22
Для получения атрибута нужно применить следующий код

Картинки = doc.QuerySelectorAll("img[src]");

Для каждого стр из Картинки Цикл
	адрес=Врап.ПолучитьИнтерфейс(стр,"IElement").GetAttribute("src");
	
КонецЦикла
...Показать Скрыть
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа