.Net в 1С. На примере использования HTTPClient, AngleSharp. Удобный парсинг сайтов с помощью библиотеки AngleSharp, в том числе с авторизацией аля JQuery с использованием CSS селекторов. Динамическая компиляция
Часто приходится парсить сайты, в том числе с авторизацией, перескакивая со страницы на страницу по ссылкам.
Тот, кто занимался вэб программированием, знает, как удобно использовать JQuery и CSS селекторы. На .Net написана очень удобная библиотека AngleSharp.
Я покажу, как с её помощью можно значительно облегчить себе труд.
(3) я был бы рад увидеть статью про валидаторы данных применительно к 1с, особенно приятно если бы это получилось хорошо. Понимаю что тема большая, но хоть что то. Вот например по этой тематике https://habrahabr.ru/post/246521/
(5) У тебя по ссылке валидация форм, хотя хотелось бы иметь валидацию произвольных данных. На входе - произвольный набор проверяемых данных и правила которым он должен удовлетворять. На выходе - ошибки, если есть. Если ошибок нет - валидация прошла успешно. Например, ты грузишь что то из какого нибудь excel или внешней базы и надо чтобы данные удовлетворяли определенным условиям. Часто фарш в данных обнаруживается значительно позднее чем мог бы.
(3) можно написать обработку, которая сжимает или приводит к одному виду изображения в базе с помощью средств .NET платформы, да еще и Parallel прикрутить для ускорения.
Еще было бы интересно рассказать про стандартные возможности .NET'а слежения за изменением в каталоге, передача файлов по сети с помощью фоновой интеллектуальной службы BITS (Windows).
Может об этом уже писали конечно, не искал специально.
ИмяВходногоФайлаКартинки - путь к файлу картинки
ИмяВыходногоФайлаКартинки = ПолучитьимяВременногоФайла(".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 пикселей то оставляет как есть, жмет неплохо, качество не теряется, быстро и сердито, можно красивее, но нужно было сделать что-то быстро, а потом оказалась и так неплохо.
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&select_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&unit_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);
Аннотация= document.QuerySelector("a.log-in");
//Полный относительный путь Аннотация.PathName+Аннотация.Search
// Но в данном случае Аннотация.Search просто нет
// Если использовать BrowsingContext то полный путь содержится в Href
res= Клиент.GetStringAsync(Аннотация.PathName).Result;
[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);
}
(23) Сергей дело говорит, selenium часто спасает. Правда я его юзаю через python, но какая разница.
Никогда не мог понять зачем делать парсер в 1С, если можно на том же .NET написать нормальный парсер и юзать его через тот же SOAP как веб сервис. А если потребуется парсить в несколько потоков или т.д.? Для чего жопаболь с 1С-языком?
Для получения атрибута нужно применить следующий код
Картинки = doc.QuerySelectorAll("img[src]");
Для каждого стр из Картинки Цикл
адрес=Врап.ПолучитьИнтерфейс(стр,"IElement").GetAttribute("src");
КонецЦикла
25.
user650124_dao-piglet
09.12.16 18:43 Сейчас в теме
Сергей, подскажите плиз, что не так в этом коде?
Объекты HttpClient и HttpRequestMessage создаются.
А дальше нужно добавить к запросу Method и RequestUri - это не получается.
(В C# аналогичный код работает)
А как тестировать и отлаживать парсинг? Компилить каждый раз прогу на C# или дергать весь сайт заново из 1С?
Не надо извращений со скрещиванием C# и 1С. Берете python и пишете на BeautyfulSoup парсинг в реальном времени в консольке.
Изучается за час-два, получаете результат в JSON и передаете в 1С.
Можно и прямо из 1С запускать готовые py-скрипты, даже хранить их в базе, а не зашивать в хардкоде алгоритмы.
Уже не один год вижу статьи про .NET+1C, такое ощущение что автору в какой то глубинке нечем заняться и он упорно шлифует "свою прелесть"
33.
albert.goncharov
8721.07.19 03:08 Сейчас в теме
Потребовалось находить дженерик-функцию SubmitAsync, у которой больше 2-х параметров, которая видимо в современных редакциях AngleSharp превратилась в дженерик.
Вот как она выглядит в файле AngleSharp.xml:
Если обратить внимание на её описание - то можно заметить что ниже по тексту идут ИМЕНОВАННЫЕ параметры, с которыми она вызывается.
// Возвращает метод дженерик-фунции
//
// Параметры:
// Врап - COMОбъект("NetObjectToIDispatch45") - первый параметр при работе с библиотеками .NET из 1С
// тип - COMОбъект - тип того объекта, который на C# пишется перед точкой при вызове дженерик-метода
// ИмяМетода - Строка - собственно тот дженерик-метод, который мы ищем
// имяПараметра1...имяПараметра9 - подсмотренные в AngleSharp.xml названия параметров дженерик-метода, по которым мы можем отличить его от других похожих
//
// Возвращаемое значение:
// COMОбъект - который потом может приниматься в качестве параметра при вызове Врап.MethodInfo_Invoke(COMОбъект, null, Параметр1, Параметр2, ...)
// Неопределено - в случае, когда метод найти не удалось
//
Функция ПолучитьМетодИнфоМногоПараметров(Врап, тип, ИмяМетода, имяПараметра1 = Неопределено, имяПараметра2 = Неопределено, имяПараметра3 = Неопределено,
имяПараметра4 = Неопределено, имяПараметра5 = Неопределено, имяПараметра6 = Неопределено,
имяПараметра7 = Неопределено, имяПараметра8 = Неопределено, имяПараметра9 = Неопределено) Экспорт
method = Неопределено;
КоличествоПараметров = ?(имяПараметра1 = Неопределено, 0,
?(имяПараметра2 = Неопределено, 1,
?(имяПараметра3 = Неопределено, 2,
?(имяПараметра4 = Неопределено, 3,
?(имяПараметра5 = Неопределено, 4,
?(имяПараметра6 = Неопределено, 5,
?(имяПараметра7 = Неопределено, 6,
?(имяПараметра8 = Неопределено, 7,
?(имяПараметра9 = Неопределено, 8,
9)))))))));
Для каждого m in Врап.ТипКакОбъект(тип).GetMethods() Цикл
параметры = m.GetParameters();
if (m.Name = ИмяМетода) И (параметры.Length = КоличествоПараметров) Тогда
КоличествоСовпавших = 0;
Для каждого параметр Из параметры Цикл
Если (параметр.Name = имяПараметра1)
Или (параметр.Name = имяПараметра2)
Или (параметр.Name = имяПараметра3)
Или (параметр.Name = имяПараметра4)
Или (параметр.Name = имяПараметра5)
Или (параметр.Name = имяПараметра6)
Или (параметр.Name = имяПараметра7)
Или (параметр.Name = имяПараметра8)
Или (параметр.Name = имяПараметра9) // поскольку когда имяПараметра9 = Неопределено - то совпасть они не могут
Тогда
КоличествоСовпавших = КоличествоСовпавших + 1;
КонецЕсли;
КонецЦикла;
Если КоличествоСовпавших = КоличествоПараметров Тогда
method = m;
Прервать;
КонецЕсли;
КонецЕсли;
КонецЦикла;
return method;
КонецФункции // ПолучитьМетодИнфоМногоПараметров()