IE 2016

.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");
  
КонецЦикла

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

См. также

Комментарии

1. Serginio 11.03.2016 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) (16)
# Ответить
2. YPermitin 11.03.2016 13:55
(1) Serginio, классные статьи, тоже интересная.

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

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

    res= Клиент.GetStringAsync(Аннотация.PathName).Result;
...Показать Скрыть
# Ответить
13. Serginio 13.03.2016 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.2016 08:58
(3) Serginio, можно написать обработку, которая сжимает или приводит к одному виду изображения в базе с помощью средств .NET платформы, да еще и Parallel прикрутить для ускорения.
Еще было бы интересно рассказать про стандартные возможности .NET'а слежения за изменением в каталоге, передача файлов по сети с помощью фоновой интеллектуальной службы BITS (Windows).

Может об этом уже писали конечно, не искал специально.
Ответили: (15)
# Ответить
15. Serginio 14.03.2016 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.2016 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.2016 15:55
Подскажите, пожалуйста, где взять файл AngleSharp.dll?
Ответили: (18)
# Ответить
18. Serginio 18.03.2016 15:57
(17) http://files.rsdn.ru/19608/AndleSharpScriptDlls.zip

Но за версиями лучше следить здесь https://www.nuget.org/packages/AngleSharp/
https://anglesharp.github.io/
Кстати новая версия вышла. Проверю. Обновил
# Ответить
19. Гость 21.03.2016 12:51
Спасибо! А то скачал сборку с Гитхаба, а как ее использовать не понял..
# Ответить
20. Serginio 24.03.2016 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.2016 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.2016 09:57
Проверил работают такие конструкции поиска

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


Найти аннотацию с классом pagenav и содеращую текст 'Вперёд'
a.pagenav:contains('Вперёд')
# Ответить
23. Serginio 26.04.2016 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.2016 21:22
Для получения атрибута нужно применить следующий код

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

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






IE 2016