Получение 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 разных брендов в одной информационной базе в ручном и автоматическом режиме. Без существенных изменений типовой конфигурации. Проверено с брендами: Интеграция 1С и GEELY Интеграция 1С и HAVAL Интеграция 1С и KIA Интеграция 1С и FORD Интеграция 1С и LADA ГАРАНТИЯ 100% ВНЕДРЕНИЯ!

36000 руб.

03.08.2020    15744    10    17    

11

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

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

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

5040 руб.

04.05.2021    17547    6    15    

13

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

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

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

12000 руб.

02.02.2021    16358    42    49    

23

[Расширение] БОР-Навигатор.Культура

Зарплата Бюджетный учет WEB-интеграция Обмен с ГосИС Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бюджетный учет Платные (руб)

Расширение конфигурации, включающее в себя объекты, необходимые для подготовки и сдачи отчета "Штатная численность" системы "БОР-Навигатор.Культура" в программе "1С:Зарплата и кадры государственного учреждения", редакция 3.1.

8400 руб.

01.02.2019    25736    9    0    

7

Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС

Обмен с ГосИС WEB-интеграция Платформа 1С v8.3 Управляемые формы 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия государственного учреждения 1С:Документооборот 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Платные (руб)

Обработка является альтернативой механизму, разработанному фирмой 1С и заполняющему реквизиты контрагента по ИНН или наименованию. Не требуется действующей подписки ИТС. Вызывается как внешняя дополнительная обработка, т.е. используется, непосредственно, из карточки контрагента. Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС (egrul.nalog.ru) для БП 2.0, БП 3.0, БГУ 1.0, БГУ 2.0, УТ 10.3, УТ 11.x, КА 1.1, КА 2.x, УПП 1.x, ERP 2.x, УНФ 1.5, УНФ 1.6, УНФ 3.0, ДО 2.1

2400 руб.

28.04.2016    88578    160    215    

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

выдаёт, что такого метода не обнаружено? что может быть . Скрипт с функцией точно добавлен.
3. Yashazz 4709 15.07.21 15:13 Сейчас в теме
(2)
Если скрипт добавлен, а обращение по имени вызывает ошибку (1С пишет, что метод не найден), значит, где-то в коде js ошибка, он не скомпилировался и не добавился
Думаю, ошибка где-то у вас. Или попробуйте объявить функцию как public, или ещё посмотрите насчёт доступа и настроек браузера, мало ли что.
4. maxx 991 15.07.21 17:38 Сейчас в теме
(3) да спасибо. действительно была ошибка в сам скрипт добавлял конструкцию <sc ript> ещё раз
5. maxx 991 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 4709 15.07.21 18:05 Сейчас в теме
если есть некая внятная ссылка на файл, то качнуть с помощью хттп-механизма 1С, штатного платформенного, нечто вроде
хттпОтвет=хттпСоединение.Получить(хттпЗапрос, ПутьИмяФайла);
7. maxx 991 15.07.21 18:06 Сейчас в теме
(6) нет ссылки. к тому же ещё авторизация, сессия, т.е. сам по себе файл не качается отдельно вне страницы
8. Yashazz 4709 15.07.21 19:56 Сейчас в теме
(7) а разве то, что в теге src, это не адрес?
9. maxx 991 15.07.21 22:05 Сейчас в теме
(8) я видимо не понимаю как сохранить файл в html документе выраженный blob ( https://learn.javascript.ru/blob ), т.е. как получить такой файл из html документа 1с
10. Yashazz 4709 16.07.21 08:14 Сейчас в теме
(9) я бы попробовал для начала качнуть то, что после blob: идёт, а уж дальше попробовал что-нибудь из https://qna.habr.com/q/301282
11. maxx 991 16.07.21 09:46 Сейчас в теме
(10) спасибо, что отвечаете.

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

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


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