Получение html-кода страницы. JS из 1С

18.02.20

Интеграция - WEB-интеграция

Получение исходника страницы, выполнение произвольного js-кода. Теперь с WebKit от 1С.

Всё нижеописанное уже так или иначе общеизвестно и опубликовано. Поскольку я ни разу не знаток javascript, данная заметка не претендует на "статью" и носит скорее антисклерозный характер. Основная задача - парсинг сайтов (кто о чём, а я всё о том же), а значит, получение исходного текста страницы.

 

Мы имеем дело с HTML5 и спецификациями этого поколения, и с WebKit, которое с релиза 8.3.14.1565 вместо старого IE (подробнее об эпичном переходе здесь). Но приходится учитывать различные "особенности реализации" в 1С, которые иногда приводят к неожиданным результатам.

Работа ведётся на клиенте 1С в управляемой форме.

Работа с HTML-документом (далее "документ") может осуществляться либо через com-объект HTMLFile, либо через элемент формы HTML-документа (не путать с ActiveX или ОболочкаHTMLДокумента). В первом случае это делается так:

// ПолучитьCOMОбъект не применять, чаще вызывает ошибку создания объекта по классу
хтмл=Новый COMОбъект("HTMLFile");
хттп=Новый COMОбъект("winhttp.winhttprequest.5.1");
хттп.Open("GET", "httрs://infostart.ru");
хттп.Send();
хтмл.Write(хттп.Responsetext); // синхронная обработка
стрТело=хтмл.body.outerText; // и так далее, обрабатываем как COM-объект

Следует учитывать, что используемые объекты зависят от MSIE и зарегистрированного класса, обрабатывается всё средствами ОС и может быть устаревшей версии; ну и COM не кроссплатформенно к тому же. Поэтому далее мы такое не рассматриваем.

Во втором случае на форме размещается реквизит типа "Строка" неограниченной длины, с видом "Поле HTML документа", и ему присваивается локальный или сетевой URL. В момент присвоения начинается загрузка документа. Здесь и далее документ определяется как

хтмл=Элементы.ПолеХТМЛ.Документ;

Окончание загрузки правильнее всего определять по совокупности двух факторов: возникновения события ЭУ 1С "ДокументСформирован" и по выставлении свойства "readyState", равному "complete". Замечено, что, несмотря на теорию, в текущих релизах эти события не синонимичны; полагаю, потому, что событие 1С срабатывает при "interactive", когда уже загружена страница и построено DOM-дерево, но картинки и айфреймы ещё догружаются (т.е. вызвано не Load, а DOMContentLoaded). Либо, возможны новые изменения-догрузки сразу после первой загрузки. Разумно делать обработчик ожидания с выключением в момент полной загрузки.

Мы можем оперировать DOM-моделью документа, методами документа и его элементов. Проверено, что все методы HTML5 работают корректно, с двумя скверными особенностями - они далеко не всегда вызывают ошибки (так, "removeAttribute" при указании несуществующего атрибута никак не ругается) и "пустое" значение js не синонимично пустому в 1С (так, правильнее проверять не "ЗначениеЗаполнено(хтмл)", а "ТипЗнч(хтмл)=Тип("Неопределено") и т.д.)

Мы не можем работать с глобальными переменными - они не сохраняются между сеансами обращения к документу. Т.е. в некоем контексте, в т.ч. контексте документа в целом, можно объявить переменную, и в рамках фрагмента кода js она будет, но следующее обращение столкнётся даже не с пустой переменной, а с её отсутствием.

Не рекомендую ни в каком месте js-кода использовать this - оно или пусто, или некорректно. Возможно, это исправят в других релизах, но надёжнее указывать полный путь к объекту. Если объект создан динамически, тоже пишите его явно.

Вместо "parentWindow" используем "DefaultView", хорошо известное как Document.Window, и можем вызывать скрипты страницы. Также, можем добавлять собственные скрипты (что мне представляется более верным и менее травматичным для документа, нежели подвешивание кусков кода на события и вызов этих событий, как предложено в статье 2016 года). Делается это так:

&НаКлиенте
Функция ДобавитьСкрипт(рТекстСкрипта,рИдСкрипта="")
	хтмлДокумент=Элементы.ПолеХТМЛ.Документ;
	Если ПустаяСтрока(рИдСкрипта) Тогда
		рИдСкрипта="Add"+Строка(хтмлДокумент.scripts.length+1);
	КонецЕсли;
	//
	хтмлСкрипт=хтмлДокумент.getElementById(рИдСкрипта);
	Если ТипЗнч(хтмлСкрипт)=Тип("Неопределено") Тогда
		хтмлСкрипт=хтмлДокумент.createElement("script");
		хтмлСкрипт.id=рИдСкрипта;
		хтмлСкрипт.setAttribute("type","text/javascript"); // в хтмл5 не надо, но пусть...
		хтмлСкрипт.setAttribute("async",Ложь); // т.к. по умолчанию создаёт Истина
		хтмлСкрипт.innerText=рТекстСкрипта;
		// вносим
		хтмлДокумент.head.appendChild(хтмлСкрипт);
	КонецЕсли;	
	//
	Возврат хтмлСкрипт;
КонецФункции

Можно вносить не в скрипты head'a, а в конец body. Документ уже загрузился, всякие асинхроны своё отрабатывают без учёта наших скриптов, Defer всё равно ни на что не влияет. Перезагрузка страницы при этом не происходит, designMode="on" включать не обязательно. Но важно учитывать, что, если текст скрипта не обёрнут в функцию, то он выполнился сразу в момент срабатывания appendChild, поэтому советую в скрипты класть именованные функции js или осознанно применять эту фичу. А вообще см. тут.

Поместив в код js функцию, например вида "function Math1(a, b) {return a+b;}", мы далее в любой момент этого и другого фрагментов кодов можем обращаться к этой функции:

рез=Элементы.ПолеХТМЛ.Документ.DefaultView.Math1(100,50); // и получим 150

Это удобнее, чем присваивать результаты неким свойствам неких объектов и вычитывать их оттуда, или ловить в параметрах событий через createEventObject() и брать из Event.data. Кроме того, не все типы данных могут пережить это преобразование (так, для ArrayBuffer или Blob у меня не сработало, да и про объекты js есть сомнения). А так мы имеем прямое обращение к функции js из языка 1С. Для входных параметров ограничений или искажений экспериментально не обнаружено.

Замечу, что можно многократно добавлять функцию с тем же именем, js просто перезаписывает её код поверх старого (и, кстати, известная разница между "a=func1 и a=func1()" наблюдается и в 1С). Также замечу, что можно добавить одним куском кода в один скрипт сразу несколько функций. Если скрипт добавлен, а обращение по имени вызывает ошибку (1С пишет, что метод не найден), значит, где-то в коде js ошибка, он не скомпилировался и не добавился - так можно себя проверять "на лету".

Задействовать Eval мне не удалось - ошибки не происходит, но и ничего не делает.

 

Теперь переходим к решению заявленной задачи.

1. Можно обратиться к свойству outerHTML:

// получим всё, кроме самых базовых объявлений и узлов вроде DOCUMENT_TYPE_NODE
стрИсходник=Элементы.ПолеХТМЛ.Документ.documentElement.outerHTML;

// или так:
// получим только head и body
стрИсходник=Элементы.ПолеХТМЛ.Документ.head.outerHTML+Элементы.ПолеХТМЛ.Документ.body.outerHTML;

// или так (хотя не лучший вариант)
стрИсходник=Элементы.ПолеХТМЛ.Документ.getElementsByTagName('html')[0].innerHTML;

Но нам-то надо получить вообще всё, все объявления.

Конечно, можно кропотливо собрать эту информацию по свойствам и подузлам, вроде Документ.contentType, doctype.name, по nodeType, но, например, коллекции вроде doctype.childNodes пусты, да и вообще есть шанс что-то потерять.

2. Можно использовать Node.js - сильно могучую штуковину, имеющую возможности различной сериализации и выгрузки, которую, однако, надо инсталлировать; потому отпадает. Тем, кто соберётся: обязательно всюду, где только можно, явно указывайте всякие encoding и charset по Документ.inputEncoding, взятому из документа, иначе жесть.

3. webBrowser.DocumentText неприменимо, т.к. нет такого ЭУ - webBrowser (если, конечно, не мучиться долго и старательно с обёртками и актив-иксинами), и опять же, он не кроссплатформенный. Можно, конечно, сделать com-объект MSIE.Application и дёргать его, но зачем?..

4. Можно так:

рАсинхронно=Истина;
рОбъект=Новый COMОбъект("MSXML2.XMLHTTP");
рОбъект.Open("GET","httрs://infostart.ru", рАсинхронно);

Но капризничает, если неправильно написан адрес в части префиксов, доменов, протоколов; и любит, чтоб сайт был по возможности статичным, чей хтмл известен уже прямо целиком, что в наше время редкость.

 

5. Можно использовать (и мне понравился этот способ) объект XMLSerializer. Это часть современной js, и у неё есть метод сериализации всея документа в строку:

var S1 = new XMLSerializer();
var StrSource = S1.serializeToString(document); // или любой элемент-подузел

В этом случае будет возвращено всё, включая декларацию <!DOCTYPE html>, хтмл с его объявлениями пространств имён, скриптами и т.д., словом, то, что надо. Кодировка UTF-8, и, судя по утверждениям гуру с хабра, с кодировкой он не лажает. Правда, немного жаль, что для этого же сериализатора метод serializeToStream, описанный здесь, более не работает. Кстати, теперь у нас есть возможность обработать двоичный поток, и можно было бы сделать нечто такое:

рЧтение=Новый ЧтениеHTML;
рЧтение.ОткрытьПоток(рПоток,"UTF-8"); // где рПоток напрямую получен из js-функции, а там из serializeToStream

но увы, это уже не поддерживается.

Правда, можно поиграть с XMLHttpRequest, вместо .responseType="text" и responseText читая результат из собственно response в виде arraybuffer или blob (в 1С уже есть инструменты работы с ними), или даже в виде "ms-stream", если только для IE. Но: этот объект требует асинхронного вызова, а с этим у js-в-1С некоторые трудности. Проще говоря, ни fetch, ни промисы адекватно не отрабатывают либо даже не компилируются. Мне не удалось даже подвесить функцию на событие OnLoad для ХHR, и ничего лучше тупого цикла по статусу реквеста не взлетело.

Таким образом, синхронное получение исходного текста документа загруженной страницы html это:

function getContentAsString(elem) {return new XMLSerializer().serializeToString(elem);}

Добавляем скрипт с этой функцией. Вызываем:

рИсходныйКод=Элементы.ПолеХТМЛ.Документ.DefaultView.getContentAsString(Элементы.ПолеХТМЛ.Документ);

Наблюдаем свежедобавленный скрипт среди прочих в head...

Вот, собственно, и всё. В прежние времена писали ОболочкаХТМЛ.ПолучитьТекст() и были счастливы, но прогресс не стоит)

 

...и я грустно пошёл переделывать свои старые публикации под новую механику... Потому что беда общая. Буде кто готов сию пещерную дикость развить и дополнить, всецело приветствую.

 

P.S. Если вдруг захочется использовать возможности js, можно сделать совсем пустой документ (благо, теперь это всего лишь 6 тегов без хитрых объявлений), и добавлять вышеописанным способом свои скрипты в него. По сути, динамически набросать модуль из js-функций, сохранить в файл и юзать по необходимости.

Опыты проводились на 1С х86 8.3.16.1063

html парсинг source исходник сайта разбор

См. также

Интеграция Альфа Авто 5 / Альфа Авто 6 и AUTOCRM / Инфотек

Сайты и интернет-магазины WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    16191    14    18    

14

Интеграция 1С — Битрикс24. Обмен задачами

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс24. Разработка имеет двухстороннюю синхронизацию 1С и Битрикс24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    18311    10    15    

16

Модуль для обмена "1С:Предприятие 8. УАТ. ПРОФ" с FortMonitor

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    13050    34    8    

13

Автоматическая загрузка файлов (например, прайс-листов) из электронной почты, FTP, HTTP, их обработка и выгрузка на FTP (на сайт) и для других целей

Прайсы WEB-интеграция Ценообразование, анализ цен Файловый обмен (TXT, XML, DBF), FTP Автомобили, автосервисы Оптовая торговля, дистрибуция, логистика Управленческий учет Платные (руб)

Программа с заданным интервалом времени (или по ручной команде) скачивает файлы (например, прайс-листы поставщиков) из различных источников: письма электронной почты, FTP или HTTP-адреса, и сохраняет их в каталог упорядоченной структуры. При этом извлекает файлы из архивов, может переименовывать файлы и менять их формат (csv, xls, txt). Можно настроить выгрузку обработанных файлов на сайт (через FTP-подключение). Программа будет полезна компаниям, у которых есть большое количество поставщиков и/или прайс-листы поставщиков обновляются часто (необязательно прайс-листы, файлы могут быть любого назначения). Собранные таким образом актуальные версии прайс-листов можно выгрузить с помощью программы себе на сайт (или на любой FTP-сервер) или выполнить другие необходимые задачи.

25200 руб.

28.05.2015    85606    26    51    

50

Интеграция с сервисом vetmanager

WEB-интеграция Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Внешняя обработка разрабатывалась для загрузки документов из Ветменеджер в 1С: Бухгалтерия 3.0

12000 руб.

02.02.2021    16693    43    49    

24
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. andrei.k 28.02.20 08:55 Сейчас в теме
Вот это уже интересно, спасибо за статью.
2. maxx 993 15.07.21 14:54 Сейчас в теме
рез=Элементы.ПолеХТМЛ.Документ.DefaultView.Math1(100,50);

выдаёт, что такого метода не обнаружено? что может быть . Скрипт с функцией точно добавлен.
3. Yashazz 4734 15.07.21 15:13 Сейчас в теме
(2)
Если скрипт добавлен, а обращение по имени вызывает ошибку (1С пишет, что метод не найден), значит, где-то в коде js ошибка, он не скомпилировался и не добавился
Думаю, ошибка где-то у вас. Или попробуйте объявить функцию как public, или ещё посмотрите насчёт доступа и настроек браузера, мало ли что.
4. maxx 993 15.07.21 17:38 Сейчас в теме
(3) да спасибо. действительно была ошибка в сам скрипт добавлял конструкцию <sc ript> ещё раз
5. maxx 993 15.07.21 17:42 Сейчас в теме
Подскажите может сталкивались.

Есть страница где есть кнопка на скачивание файла. Ссылка не статичная, просто input и где-то как-то скрипты срабатывают открывается видимо поток скачивания и в браузере начинает скачиваться файл.

Если эту страницу загрузить HTML документ, то как этот файл скачать/ получить там?

Добавлю что при нажатии на такой ссылке в HTML содержимое такое становится ( в браузере скачивается PDF):

<ht ml><body marginwidth="0" marginheight="0" style="background-color: rgb(38,38,38)"><emb ed width="100%" height="100%" name="plugin" src="blob:https://XXXXXXXXXXXXXXXXX/8865afc6-27cf-4781-b5ba-e358761ae3a5" type="application/pdf"></body></html>
6. Yashazz 4734 15.07.21 18:05 Сейчас в теме
если есть некая внятная ссылка на файл, то качнуть с помощью хттп-механизма 1С, штатного платформенного, нечто вроде
хттпОтвет=хттпСоединение.Получить(хттпЗапрос, ПутьИмяФайла);
7. maxx 993 15.07.21 18:06 Сейчас в теме
(6) нет ссылки. к тому же ещё авторизация, сессия, т.е. сам по себе файл не качается отдельно вне страницы
8. Yashazz 4734 15.07.21 19:56 Сейчас в теме
(7) а разве то, что в теге src, это не адрес?
9. maxx 993 15.07.21 22:05 Сейчас в теме
(8) я видимо не понимаю как сохранить файл в html документе выраженный blob ( https://learn.javascript.ru/blob ), т.е. как получить такой файл из html документа 1с
10. Yashazz 4734 16.07.21 08:14 Сейчас в теме
(9) я бы попробовал для начала качнуть то, что после blob: идёт, а уж дальше попробовал что-нибудь из https://qna.habr.com/q/301282
11. maxx 993 16.07.21 09:46 Сейчас в теме
(10) спасибо, что отвечаете.

Думал задача будет решаться просто, а она такая:
"Пользователь в 1С открывает карточку договора и к нему нужно прикрепить скан-договора. Скан договора лежит в веб-системе (внешняя, нельзя на неё влиять), в ней авторизуются , находят договор и там сканы. Так вот хочется, чтобы пользователь из 1С , открывая карточку договору, далее нажал кнопку "Найти и прикрепить файл" выходил в обработку с HTML документом скачивал файл и файл прикреплялся к справочнику договоры".

В итоге ковыряясь в хроме выяснилось следующее, что на странице нажимая "скачать файл pdf" делается XHR (XMLHttpRequest) запрос GET на скачку файла с передачей кучи всех параметров включая токены авторизация, куки, однако в HTML документе 1С непонятно как-то это отрабатывается, файла нигде не вижу, ошибок тоже никаких не. Веб-система на движке каком-то написано js , так что там всё сложно разобраться.
12. Yashazz 4734 16.07.21 09:51 Сейчас в теме
(11)
запрос GET на скачку файла с передачей кучи всех параметров включая токены авторизация, куки
Так вам тогда не надо с хтмл-полем связываться, говорю же. Делаете такой же запрос средствами хттп, и дёргаете его чисто 1С-ным образом, в т.ч. при работе с полем. Если все параметры и куки аккуратно повторить, то должен качать. И вообще с этим лучше в личку.
13. maxx 993 16.07.21 10:00 Сейчас в теме
(12)
Так вам тогда не надо с хтмл-полем связываться, говорю же. Делаете такой же запрос средствами хттп, и дёргаете его чисто 1С-ным образом, в т.ч. при работе с полем. Если все параметры и куки аккуратно повторить, то должен качать.


Так я не знаю параметров скачивания файла заранее , не знаю ссылки , там похоже скрипт всё это делает и всё. А параметры заголовков запроса откуда брать (токены, сессии), вот на скриншоте прикладываю.
Прикрепленные файлы:
Оставьте свое сообщение