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

Часто приходится парсить сайты, в том числе с авторизацией, перескакивая со страницы на страницу по ссылкам.
Тот, кто занимался вэб программированием, знает, как удобно использовать 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:Vs2024ProgramsTestScriptingAPITestScriptingAPIinDebugAngleSharp.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

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

33 Comments

  1. Serginio

    Я уже не буду перегружать статью. Но можно использовать скрипты для заполнения данных 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:Vs2015ProgramsScriptApiDllsScriptApiDllsinDebug»;
    КлассДляВычесленияВыражений=Врап.ПолучитьТипИзСборки(«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,Тз);
    
    тз.ВыбратьСтроку();
    

    Показать

    Reply
  2. YPermitin

    (1) классные статьи, тоже интересная.

    Но есть парочку НО:

    1. Материал подается в неудобочитаемом виде.

    2. Мало практических примером, близких к реальности.

    ИМХО. Без обид.

    Reply
  3. Serginio

    (2) Спасибо. Может подкинешь идею? Могу в свободное время взяться за конкретный пример или кому то помочь с реальной задачей

    Reply
  4. invertercant

    (3) я был бы рад увидеть статью про валидаторы данных применительно к 1с, особенно приятно если бы это получилось хорошо. Понимаю что тема большая, но хоть что то. Вот например по этой тематике https://habrahabr.ru/post/246521/

    Reply
  5. Serginio

    (4) Да уж. Можно смотреть на валидаторы внутри ASP.Net MVC. Там генерятся валидаторы как на JavaScript так и на уровне HTML5

    http://stephenwalther.com/archive/2012/03/13/html5-form-validation

    Reply
  6. invertercant

    (5) У тебя по ссылке валидация форм, хотя хотелось бы иметь валидацию произвольных данных. На входе — произвольный набор проверяемых данных и правила которым он должен удовлетворять. На выходе — ошибки, если есть. Если ошибок нет — валидация прошла успешно. Например, ты грузишь что то из какого нибудь excel или внешней базы и надо чтобы данные удовлетворяли определенным условиям. Часто фарш в данных обнаруживается значительно позднее чем мог бы.

    Reply
  7. Serginio

    Это из области парсеров https://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D0%BC%D0%B0_%D0%91%D1%8D%D0%BA%D­1%83%D1%81%D0%B0_%E2%80%94_%D0%9D%D0%B0%D1%83%D1%80%D0%B0

    Reply
  8. invertercant

    (7) Это была идея для статьи чтобы не придумывать велосипед, а взять готовый на NET, если такой существует.

    Reply
  9. Serginio

    (8) Они существуют. Только не универсальные. XML, HTML, C# итд.

    Reply
  10. Serginio

    Что касается LIVR то прочтя https://github.com/koorchik/LIVR вроде как на .Net нет библиотеки

    Reply
  11. Serginio

    Вот пример парсинга мисты

     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);

    Показать

    Reply
  12. Serginio

    Обратите внимание

    Аннотация= document.QuerySelector(«a.log-in»);
    //Полный относительный путь Аннотация.PathName+Аннотация.Search
    // Но в данном случае Аннотация.Search просто нет
    // Если использовать BrowsingContext то полный путь содержится в Href
    
    res= Клиент.GetStringAsync(Аннотация.PathName).Result;
    Reply
  13. Serginio
    Reply
  14. YPermitin

    (3) можно написать обработку, которая сжимает или приводит к одному виду изображения в базе с помощью средств .NET платформы, да еще и Parallel прикрутить для ускорения.

    Еще было бы интересно рассказать про стандартные возможности .NET’а слежения за изменением в каталоге, передача файлов по сети с помощью фоновой интеллектуальной службы BITS (Windows).

    Может об этом уже писали конечно, не искал специально.

    Reply
  15. Serginio

    (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С для новичков

    Главное заинтересовать, а там дальше пойдет.

    Reply
  16. Serginio

    Еще один пример парсинга

    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);
    

    Показать

    Reply
  17. Гость

    Подскажите, пожалуйста, где взять файл AngleSharp.dll?

    Reply
  18. Serginio

    (17) http://files.rsdn.ru/19608/AndleSharpScriptDlls.zip

    Но за версиями лучше следить здесь https://www.nuget.org/packages/AngleSharp/

    https://anglesharp.github.io/

    Кстати новая версия вышла. Проверю. Обновил

    Reply
  19. Гость

    Спасибо! А то скачал сборку с Гитхаба, а как ее использовать не понял..

    Reply
  20. Serginio

    В составе AngleSharp.Scripting для парсинга сайтов с использованием JS

    https://github.com/AngleSharp/AngleSharp/wiki/Examples

    Есть парсер

    https://github.com/sebastienros/jint

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

    Engine=врап.ПолучитьТипИзСборки(«Jint.Engine»,»d:Vs2015ProgramsWpfApplication1WpfApplication1inDebugJint.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);
    

    Показать

    Reply
  21. Serginio

    Можно передавать свои объекты и и использовать их при выполнении скриптов . Пример на 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);
    

    Показать

    Reply
  22. Serginio

    Проверил работают такие конструкции поиска

    Поиск div с id начинающихся на «row»

    СелекторСтроки = «div[id#k8SjZc9Dxk=row]»;

    Найти аннотацию с классом pagenav и содеращую текст ‘Вперёд’

    a.pagenav:contains(‘Вперёд’)
    Reply
  23. Serginio

    Я бы еще добавил 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»);

    Reply
  24. Serginio

    Для получения атрибута нужно применить следующий код

    Картинки = doc.QuerySelectorAll(«img[src]»);
    
    Для каждого стр из Картинки Цикл
    адрес=Врап.ПолучитьИнтерфейс(стр,»IElement»).GetAttribute(«src»);
    
    КонецЦикла
    
    Reply
  25. user650124_dao-piglet

    Сергей, подскажите плиз, что не так в этом коде?

    Объекты HttpClient и HttpRequestMessage создаются.

    А дальше нужно добавить к запросу Method и RequestUri — это не получается.

    (В C# аналогичный код работает)

    ХТТПЗапрос.RequestUri = Врап.СоздатьОбъект(«System.Uri», адрес);

    HttpMethod = Врап.ПолучитьТипИзСборки(«System.Net.Http.HttpMethod», «System.Net.Http.dll»);

    ХТТПЗапрос.Method = HttpMethod.Get;

    И в 1-й и в 2-й строках одинаковая ошибка:

    Произошла исключительная ситуация (mscorlib): Exception has been thrown by the target of an invocation.

    Получение System.Uri видел у Вас здесь и в других публикациях. HttpMethod попробовал методом тыка.

    Reply
  26. Serginio

    А зачем для Get нужен какой то метод. Используй нужные методы с Get

    Вот здесь куча примеров

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

    Используй HTTPClient . он более продвинутый. У меня все примеры с ним.

    Reply
  27. quick

    (23) Сергей дело говорит, selenium часто спасает. Правда я его юзаю через python, но какая разница.

    Никогда не мог понять зачем делать парсер в 1С, если можно на том же .NET написать нормальный парсер и юзать его через тот же SOAP как веб сервис. А если потребуется парсить в несколько потоков или т.д.? Для чего жопаболь с 1С-языком?

    Reply
  28. s_vidyakin

    А как тестировать и отлаживать парсинг? Компилить каждый раз прогу на C# или дергать весь сайт заново из 1С?

    Не надо извращений со скрещиванием C# и 1С. Берете python и пишете на BeautyfulSoup парсинг в реальном времени в консольке.

    Изучается за час-два, получаете результат в JSON и передаете в 1С.

    Можно и прямо из 1С запускать готовые py-скрипты, даже хранить их в базе, а не зашивать в хардкоде алгоритмы.

    Уже не один год вижу статьи про .NET+1C, такое ощущение что автору в какой то глубинке нечем заняться и он упорно шлифует «свою прелесть»

    Reply
  29. Denanhel

    (15) Пилил сжатие изображений.

    ИмяВходногоФайлаКартинки — путь к файлу картинки
    
    ИмяВыходногоФайлаКартинки = ПолучитьимяВременногоФайла(«.jpg»)
    
    Врап = новый COMОбъект(«NetObjectToIDispatch45»);
    
    Picture = Врап.СоздатьОбъект(«System.Drawing.Bitmap», ИмяВходногоФайлаКартинки);
    
    Множитель = Макс(Число(Picture.Height), Число(Picture.Width)) / 500;
    
    Если Множитель >= 1 Тогда
    nPicture = Врап.СоздатьОбъект(«System.Drawing.Bitmap», Picture, Цел(Picture.Width/Множитель),
    Цел(Picture.Height/Множитель));
    Picture.Dispose();
    nPicture.Save(ИмяВыходногоФайлаКартинки);
    nPicture.Dispose();

    Показать

    КонецЕсли;

    Режет картику до размера 500 пикселей на сколько-то там…. Ну исходя из начального размера. Если размер меньше 500 пикселей то оставляет как есть, жмет неплохо, качество не теряется, быстро и сердито, можно красивее, но нужно было сделать что-то быстро, а потом оказалась и так неплохо.

    Reply
  30. Denanhel

    (23) Может кому будет полезным.

    
    Процедура GZIPРаспаковать(вхФайл, исхФайл, МассивОшибок) Экспорт
    
    Попытка
    Врап = новый COMОбъект(«NetObjectToIDispatch45»);
    MemoryStream = Врап.ПолучитьТип(«System.IO.MemoryStream»);
    UTF8 = Врап.ПолучитьТип(«System.Text.Encoding»).UTF8;
    CompressionMode = Врап.ПолучитьТип(«System.IO.Compression.CompressionMode»);
    FileMode = Врап.ПолучитьТип(«System.IO.FileMode»);
    
    ПотокСжатыхДанных = Врап.СоздатьОбъект(«System.IO.FileStream», вхФайл, FileMode.OpenOrCreate);
    GZipПоток = Врап.СоздатьОбъект(«System.IO.Compression.GZipStream», ПотокСжатыхДанных, CompressionMode.Decompress);
    ПотокРазжатыхДанных = Врап.СоздатьОбъект(«System.IO.FileStream», исхФайл, FileMode.OpenOrCreate);
    GZipПоток.CopyTo(ПотокРазжатыхДанных);
    
    ПотокСжатыхДанных.Close();
    GZipПоток.Close();
    ПотокРазжатыхДанных.Close();
    Исключение
    МассивОшибок.Добавить(«Не удалось распаковать данные» + » » + ОписаниеОшибки());
    Возврат;
    КонецПопытки;
    КонецПроцедуры
    
    

    Показать

    Reply
  31. albert.goncharov

    А есть ли какой-то способ вызывать конструкции вида =>

    ?

        //Do something with LINQ
    var blueListItemsLinq = document.All.Where(m => m.LocalName == «li» && m.ClassList.Contains(«blue»));
    
    Reply
  32. albert.goncharov
    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *