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




Я уже не буду перегружать статью. Но можно использовать скрипты для заполнения данных 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,Тз); тз.ВыбратьСтроку();Показать
(1) классные статьи, тоже интересная.
Но есть парочку НО:
1. Материал подается в неудобочитаемом виде.
2. Мало практических примером, близких к реальности.
ИМХО. Без обид.
(2) Спасибо. Может подкинешь идею? Могу в свободное время взяться за конкретный пример или кому то помочь с реальной задачей
(3) я был бы рад увидеть статью про валидаторы данных применительно к 1с, особенно приятно если бы это получилось хорошо. Понимаю что тема большая, но хоть что то. Вот например по этой тематике
(4) Да уж. Можно смотреть на валидаторы внутри ASP.Net MVC. Там генерятся валидаторы как на JavaScript так и на уровне HTML5
(5) У тебя по ссылке валидация форм, хотя хотелось бы иметь валидацию произвольных данных. На входе — произвольный набор проверяемых данных и правила которым он должен удовлетворять. На выходе — ошибки, если есть. Если ошибок нет — валидация прошла успешно. Например, ты грузишь что то из какого нибудь excel или внешней базы и надо чтобы данные удовлетворяли определенным условиям. Часто фарш в данных обнаруживается значительно позднее чем мог бы.
Это из области парсеров
(7) Это была идея для статьи чтобы не придумывать велосипед, а взять готовый на NET, если такой существует.
(8) Они существуют. Только не универсальные. XML, HTML, C# итд.
Что касается LIVR то прочтя вроде как на .Net нет библиотеки
Вот пример парсинга мисты
Показать
Обратите внимание
(3) можно написать обработку, которая сжимает или приводит к одному виду изображения в базе с помощью средств .NET платформы, да еще и Parallel прикрутить для ускорения.
Еще было бы интересно рассказать про стандартные возможности .NET’а слежения за изменением в каталоге, передача файлов по сети с помощью фоновой интеллектуальной службы BITS (Windows).
Может об этом уже писали конечно, не искал специально.
(14) Большое спасибо за совет.
Там есть подписка на события System.IO.FileSystemWatcher
Насчет картинок была статья Elisy
Я на мисте предлагал
Но как то мало это народ интересует.
Нужны какие то простые, но понятные примеры типа
Например больше всего популярностью пользуется
Главное заинтересовать, а там дальше пойдет.
Еще один пример парсинга
Показать
Подскажите, пожалуйста, где взять файл AngleSharp.dll?
(17)
Но за версиями лучше следить здесь
Кстати новая версия вышла. Проверю. Обновил
Спасибо! А то скачал сборку с Гитхаба, а как ее использовать не понял..
В составе AngleSharp.Scripting для парсинга сайтов с использованием JS
Есть парсер
Вот пример использования
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);Показать
Можно передавать свои объекты и и использовать их при выполнении скриптов . Пример на 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);Показать
Проверил работают такие конструкции поиска
Поиск div с id начинающихся на «row»
Найти аннотацию с классом pagenav и содеращую текст ‘Вперёд’
Я бы еще добавил Silenium прежде всего как построитель DOM в заскриптованных таблицах
Для того что бы добраться до сформированного DOM можно использовать вместо PageSource
вычисляемый скрипт
var pageSource = (string)driver.ExecuteScript(«return document.body.outerHTML»);
Для получения атрибута нужно применить следующий код
Сергей, подскажите плиз, что не так в этом коде?
Объекты 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 попробовал методом тыка.
А зачем для Get нужен какой то метод. Используй нужные методы с Get
Вот здесь куча примеров
Используй HTTPClient . он более продвинутый. У меня все примеры с ним.
(23) Сергей дело говорит, selenium часто спасает. Правда я его юзаю через python, но какая разница.
Никогда не мог понять зачем делать парсер в 1С, если можно на том же .NET написать нормальный парсер и юзать его через тот же SOAP как веб сервис. А если потребуется парсить в несколько потоков или т.д.? Для чего жопаболь с 1С-языком?
(27) На самом деле и в 1С можно запустить несколько задач
А как тестировать и отлаживать парсинг? Компилить каждый раз прогу на C# или дергать весь сайт заново из 1С?
Не надо извращений со скрещиванием C# и 1С. Берете python и пишете на BeautyfulSoup парсинг в реальном времени в консольке.
Изучается за час-два, получаете результат в JSON и передаете в 1С.
Можно и прямо из 1С запускать готовые py-скрипты, даже хранить их в базе, а не зашивать в хардкоде алгоритмы.
Уже не один год вижу статьи про .NET+1C, такое ощущение что автору в какой то глубинке нечем заняться и он упорно шлифует «свою прелесть»
(15) Пилил сжатие изображений.
Показать
КонецЕсли;
Режет картику до размера 500 пикселей на сколько-то там…. Ну исходя из начального размера. Если размер меньше 500 пикселей то оставляет как есть, жмет неплохо, качество не теряется, быстро и сердито, можно красивее, но нужно было сделать что-то быстро, а потом оказалась и так неплохо.
(23) Может кому будет полезным.
Показать
А есть ли какой-то способ вызывать конструкции вида =>
?