Глава 2. Как я написал (собрал с инфостарта) свой парсер сайта 1С Releases

31.08.21

Интеграция - Сайты и интернет-магазины

Путешествие начинающего программиста 1С. Глава вторая. Призываем экспертов в комментарии, оптимизируем код, ругаемся на кривые руки автора. Автор же, в свою очередь, делится своим опытом, читает умные комментарии, стремится дать те знания, за которыми он сидел ночами тем, кто их хочет найти.

Глава 2. Как я написал (собрал с инфостарта) свой парсер сайта 1С Releases

Программист 1С или Туда и обратно. Глава вторая. Призываем экспертов в комментарии, оптимизируем код, ругаемся на кривые руки автора.
Автор же, в свою очередь, делится своим опытом, читает умные комментарии, стремится дать те знания, за которыми он сидел ночами тем, кто их хочет найти.

Итак, приступим. В принципе - да, не зная броду, в этом проекте сразу пришлось окунуться в воду, а именно, имея за плечами не такой большой багаж косвенной вычитки и правки кода за местным программистом, мне захотелось написать что-то свое. Ну как свое, конечно что-то такое, что будет чужое, но будет переписано под меня. И не эти ваши новичковые программы типа "справочника номенклатуры для хозрасчетного учета" *произносит закатив глаза* (о да, автор еще и работает с бюджетными конфигурациями), а сразу ПАРСЕР на 1С.

Идея пришла совершенно косвенными путями. Мне хотелось написать программу, которая будет меня оповещать где и у каких моих клиентов давно не обновлялась база, сколько еще я могу им позволить сидеть на старых релизах, стоит ли уделить кому-то больше внимания, а с кем то наоборот притормозить. И для этого, пришла в голову мысль сделать парсер сайта 1C Releases (ага, конечно, мы не ищем легких путей, и не ждем подачек от менеджеров). Как я узнал спустя пару месяцев после написания данного парсера, у 1С есть базовый механизм API для выдачи партнерам данных о вышедших конфигурациях (со всей интересующей вас информацией), но под нее уже код я не переделывал, в перспективе возможно, но уже не сейчас. Сегодня я попытаюсь сосредоточиться и рассказать вам, как все-таки мне удалось победить сайт 1С, расскажу про свою дырявую память. Так как части моего кода будут заимствованы из комментариев с инфостарта, заведомо прошу прощения у тех, кого я не упомяну в статье. Я честно потратил час пытаясь отыскать ваши посты, но не нашел. Напишите в комментариях если узнаете свой код и я обязательно вас поблагодарю лично с упоминанием в статье, или сниму ее с публикации, если вы будете недовольны этим. Все чего я бы хотел, это поделиться знаниями с тем, кому предстоит сделать то, что я благодаря вам воплотил в реальность.

Для начала, рыская повсюду в поисках информации, я читал статьи на инфостарте, чтобы понять как все-таки формируются http запросы и как мне получить эти данные. В принципе все было весьма очевидно, нужно было произвести логин на сайт 1С Releases с логином - паролем моего ИТС, скачать титульную страницу и по косвенным признакам вывести все наименования конфигураций (платформ), которые так или иначе доступны. Главная трудность - пройти двойную аутентификацию.

Статья оригинал

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

Так вот, для реализации данного механизма мне понадобилось изначально найти JSESSIONID. Как описал автор статьи выше, эта штука, должна была помочь мне подтверждать, что это именно я (с моим набором логина и пароля) продолжаю находиться на данном сайте.

Для анализа запросов я использовал браузер Google Chrome и клавишу F12 (раздел Network, внизу выбираем имя запроса и читаем все что в нем есть, сравниваем со своим, четкой инструкции вряд-ли вы найдете, да еще и такой, чтобы обойтись просто копи-пастом)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

&НаСервере
Процедура СпарситьНаСервере()
    
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
                   |    ДанныеИТС.Ссылка КАК Ссылка
                   |ИЗ
                   |    Справочник.ДанныеИТС КАК ДанныеИТС";
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        
        ИспользуемыеДанныеИТС = Выборка.Ссылка;    
        НайтиJSESSIONID();
    
    // Здесь были методы загрузки конфигураций и версий, которые мы рассмотрим чуть ниже 
    
    КонецЦикла;         
                              
КонецПроцедуры


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Запускал всю эту красоту я вот таким кодом. В базе я сделал справочник ИТС, в котором хранились логины-пароли ИТС, которые мне приходилось использовать (почему их много, я расскажу, но не в этой статье). В данном случае реализация подразумевает, что у вас будет больше 1 логина пароля ИТС. Переменная "ИспользуемыеДанныеИТС" находится на форме невидимая и позволяет получать данные по текущему ИТС, парсить его страницы-конфигурации-версии.
Для каждой позиции словаря, вызывается свой JSESSIONID (это важно). Описание метода его поиска будет приведена ниже.

Оригинал

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

&НаСервере
Функция НайтиJSESSIONID()
	
	Попытка
		
		// Для GET-запросов
		HTTPЗапрос1 = Новый HTTPЗапрос;
		HTTPЗапрос1.Заголовки.Вставить("Connection", "keep-alive");
		
		// Для POST-запросов
		HTTPЗапрос2 = Новый HTTPЗапрос;
		HTTPЗапрос2.Заголовки.Вставить("Connection", "keep-alive");
		HTTPЗапрос2.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
		
		// Создаем 2 соединения. Первое для места, где будем логиниться, второе для места куда хотим зайти
		Соединение1 = Новый HTTPСоединение("login.1c.ru",,,,Новый ИнтернетПрокси,,Новый ЗащищенноеСоединениеOpenSSL);
		Соединение2 = Новый HTTPСоединение("releases.1c.ru",,,,Новый ИнтернетПрокси,,Новый ЗащищенноеСоединениеOpenSSL);
		
		/////////////////////////////
		// Запрос 1 //                  
		/////////////////////////////
		//
		// Здесь небольшая путаница с цифрами, старайтесь абстрагироваться от них
		// код по максимуму оставался без изменений как рабочий
		HTTPЗапрос1.АдресРесурса = "/total";  
		
		// Соединяемся (смотри соединение 2) по адресу releases.1c.ru/total и получаем ответ
		ОтветHTTP1 = Соединение2.Получить(HTTPЗапрос1);
		
		// Выдираем с сайта айди текущей сессии, по сути все до точки с запятой из текущих кукисов
		JSESSIONID = ОтветHTTP1.Заголовки.Получить("Set-Cookie");
		JSESSIONID = Лев(JSESSIONID, Найти(JSESSIONID, ";") - 1);
		
		// Убираем из строки в заголовке Location "https://login.1c.ru" и присваиваем это в переменную 
		LOCATION1 = СтрЗаменить(ОтветHTTP1.Заголовки.Получить("Location"), "https://login.1c.ru", "");
		
		/////////////////////////////
		// Запрос 2 //
		/////////////////////////////
		//
		// Снова вспоминаем что абстрагируемся от цифр, снова меняем адрес. На сей раз уже для соединения 1
		// Сейчас это должен быть адрес для формы логина
		HTTPЗапрос1.АдресРесурса = LOCATION1;    
		
		// Соединяемся (смотри соединение 1) по адресу login.1c.ru + LOCATION1 (содержимое посмотрите дебаггером)
		// и получаем ответ
		// Если вы не уверены, что знаете как пользоваться дебаггером - напишите об этом
		ОтветHTTP2 = Соединение1.Получить(HTTPЗапрос1);                                        
		
		// Снова ищем кукисы и дергаем их, они понадобятся нам для логина
		JSESSIONID1 = ОтветHTTP2.Заголовки.Получить("set-cookie");                             		
		JSESSIONID1 = Прав(JSESSIONID1, СтрДлина(JSESSIONID1) - Найти(JSESSIONID1, "SESSION") + 1); 
		JSESSIONID1 = Лев(JSESSIONID1, Найти(JSESSIONID1, ";") - 1);
		
		// Создаем структуру инвайт кода по аналогии с тем, что автоматически создает нам сервер аутентификации
		// Если вы используете этот код в далеком будущем, проверьте структуру его формирования
		inviteCode = РаспарситьinviteCode(ОтветHTTP2.ПолучитьТелоКакСтроку());
		
		/////////////////////////////
		// Запрос 3 //
		/////////////////////////////
		//
		// Собственно логинимся, по адресу https://login.1c.ru/login отправляем наши данные кукисов
		// с зашитыми в нем инвайт кодами в открытую (а вы как думали? и логин и пароль в запросе открытые!)
		HTTPЗапрос2.АдресРесурса = "/login";
		HTTPЗапрос2.Заголовки.Вставить("Cookie", JSESSIONID1);
		HTTPЗапрос2.УстановитьТелоИзСтроки(inviteCode);
		ОтветHTTP3 = Соединение1.ОтправитьДляОбработки(HTTPЗапрос2);                                     
		
		// Получаем страницу перенаправления (в браузере вы также его видите, если достаточно внимательны)
		LOCATION3 = СтрЗаменить(ОтветHTTP3.Заголовки.Получить("location"), "https://releases.1c.ru", "");
		
		/////////////////////////////
		// Запрос 4 //
		/////////////////////////////
		//               
		// Здесь мы просто переходим по нашему адресу перенаправления (можно сказать говорим релизам, что мы свои)
		// и как результат - завершаем эпопею с логином подставляя наш с вами айди, полученый в самом начале
		HTTPЗапрос1.АдресРесурса = LOCATION3;
		HTTPЗапрос1.Заголовки.Вставить("Cookie", JSESSIONID); 
		
		// Мы на месте! Логин прошел!
		ОтветHTTP4 = Соединение2.Получить(HTTPЗапрос1);
		
	Исключение
		
		Возврат Ложь;
		
	КонецПопытки;
	
	Возврат Истина;
	
КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Опытный пользователь сразу поймет, что в коде выше чего-то не хватает, а именно функции РаспарситьinviteCode . Что-ж, вот и она на подходе:


Оригинал

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

&НаСервере
Функция РаспарситьinviteCode(Текст)
    
    ЧтениеHTML = Новый ЧтениеHTML;
    ЧтениеHTML.УстановитьСтроку(Текст);
    
    Построитель = Новый ПостроительDOM;
    ДокументHTML = Построитель.Прочитать(ЧтениеHTML);
    
    loginForm = ДокументHTML.ПолучитьЭлементПоИдентификатору("loginForm");
    
    inviteCode = "inviteCode=&username=" + ИспользуемыеДанныеИТС.Логин 
    + "&password=" + ИспользуемыеДанныеИТС.Пароль
    + "&execution=" + loginForm.Элементы[6].Значение
    + "&_eventId=submit"
    + "&geolocation="
    + "&submit=Войти"
    + "&rememberMe=on"
    ;
    
    Возврат inviteCode;
    
КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Если вы хотите полного описания механизмов и того, как это все собиралось - думаю лучше почитать первоисточник. На моей же стороне были куски неработающего кода из комментариев и метод обратного инжениринга. 
Перечитывая по 20-50 раз статью и комментарии, я пытался увязать это все вместе. 

И в принципе, как говорилось в одноименном кино "He's alive!". Мы получили наш айди авторизации и теперь можем творить на сайте 1С что захотим, подставляя его повсюду!

Главный принцип по которому вы должны ориентироваться - это проверять на каждом шаге, где мы делаем с вами метод ".Получить" - это ответ сервера. Наша задача, чтобы статус коды, которые возвращает нам сервер были в диапазонах, соответствующих данной мини-таблице (естественно в нашем случае успех это 200, в некотором случае (когда происходит перенаправление это 300):

Информационные 100 - 199
Успешные 200 - 299
Перенаправления 300 - 399
Клиентские ошибки 400 - 499
Серверные ошибки 500 - 599

Попрошу заметить, что в коде выше, есть переменная ИспользуемыеДанныеИТС. У нее есть параметры логин и пароль. В принципе вы уже догадались если внимательно читали мое описание, что это объект справочника "Данные ИТС", в котором содержатся поля логин и пароль. В вашем случае вы можете использовать константы.

Ну и самое для меня главное, оказалось что это не так удобно, искать закономерности для парсинга в дебаггере 1С. Поэтому мне очень помогла процедура, которую я положу чуть ниже:
Оригинал потерялся, но если ты меня найдешь - напиши о себе, я дам ссылку.

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

&НаСервере
Процедура ЗаписатьHTML(ДокументHTML)
    
    ЗапиcьHTML = Новый ЗаписьHTML;
    
    ЗапиcьHTML.ОткрытьФайл("c:\temp\text_html2.html", "UTF-8");
    
    ЗаписьDOM = Новый ЗаписьDOM;
    
    ЗаписьDOM.Записать(ДокументHTML, ЗапиcьHTML);
    
    ЗапиcьHTML.Закрыть()
    
КонецПроцедуры


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

На вход она принимает полученный документ HTML и сохраняет его в том виде, который спарсил 1С. Возможно, открыв его, вы сильно удивитесь тому, что нет многого, к чему вы привыкли на Releases, но оно и не нужно на самом то деле. В таком виде гораздо проще анализировать код страницы для парсинга и воочию видеть то, что вы парсите собственно говоря. Потому, что вдруг, вы пишете парсинг Releases, а аутентификация не проходит и в итоге ваш цикл парсера ходит по кругу и не выдает результата.

Для анализа страниц я использовал браузер Google Chrome и клавишу F12 (раздел Elements)

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

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

&НаСервере
Функция ЗагрузитьПрограммыНаСервере(ГиперссылкаСтраницы = "",Конфигурация = "")
	//Нужно разбить на 2 функции, чтобы не была побочность
	
	// Проверяю гиперссылку по которой буду искать
	Если ЗначениеЗаполнено(Конфигурация) Тогда 
		
		ГиперссылкаСтраницы = Конфигурация.Гиперссылка;  
		
	КонецЕсли;   
	
	Попытка

		// По аналогии с запросами в НайтиJSESSIONID() пишем новый запрос для получения документа HTML
		// Включаем в него наши кукисы в виде JSESSIONID, который хранится на форме
		Запрос = Новый HTTPЗапрос;
		Запрос.Заголовки.Вставить("Connection", "keep-alive");
		Запрос.Заголовки.Вставить("Cookie", JSESSIONID);                           
		
		// Подставляем ссылку вида "/total" или "/yourConfigName", так как адрес описываем ниже
		Запрос.АдресРесурса = ГиперссылкаСтраницы;
		Соединение2 = Новый HTTPСоединение("releases.1c.ru",,,,Новый ИнтернетПрокси,,Новый ЗащищенноеСоединениеOpenSSL);
		
		// Получаем ответ с которого будем тащить страницу
		ОтветHTTP = Соединение2.Получить(Запрос);
		
		// Тащим страницу
		ЧтениеHTML = Новый ЧтениеHTML;
		ЧтениеHTML.УстановитьСтроку(ОтветHTTP.ПолучитьТелоКакСтроку());
		Построитель = Новый ПостроительDOM;
		
		// Создаем документ HTML из того, что прочитали
		ДокументHTML = Построитель.Прочитать(ЧтениеHTML);	
		
		// Процедура записи в файл, в идеале могла бы принимать путь на вход
		// Но используется только для теста - посему осталась такой какой была
		ЗаписатьHTML(ДокументHTML);                                           
		
		// Оповещение пользователя с самописного модуля (переделать под БСП если будете использовать)
		ОбщийФункционалСервер.СообщитьССервера("Загрузка HTML прошла успешно");			
		
	Исключение
		
		ОбщийФункционалСервер.СообщитьССервера("Загрузка HTML не прошла");
		Возврат Ложь;
		
	КонецПопытки;
	
	// Попытка распарсить список конфигураций
	// Бесполезная проверка гиперссылки, адаптируете под себя этот хардкод
	// Если "/total", тогда парсится список конфигураций
	Если ГиперссылкаСтраницы = "/total" Тогда           
		
		Попытка			                  
			
			РекурсивныйПарсингСпискаКонфигураций(ДокументHTML);			
			ОбщийФункционалСервер.СообщитьССервера("Парсинг конфигураций прошел успешно");	
			
		Исключение           
			
			ОбщийФункционалСервер.СообщитьССервера("Парсинг конфигураций не прошел");	 
			
		КонецПопытки;      
		
	Иначе    
		
		Попытка	    
			
			РекурсивныйПарсингВерсийКонфигураций(ДокументHTML, Конфигурация);		
			ОбщийФункционалСервер.СообщитьССервера("Парсинг списка конфигураций прошел успешно");	      
			
		Исключение        
			
			ОбщийФункционалСервер.СообщитьССервера("Парсинг списка конфигураций не прошел");	  
			
		КонецПопытки;     
		
	КонецЕсли; 
	
	Возврат Истина;	 
	
КонецФункции


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

В данном случае, я бы хотел заранее извиниться перед теми, кто будет читать этот кошмар, тут я если честно не сильно знал каким образом стоит писать процедуры и функции по методологии 1С. Чтобы переписать код, мне потребуется время, поэтому пока положу данную сырую ерунду со своими замечаниями.

ЖИРНЫЙ МИНУС НОМЕР РАЗ: В данной функции я совместил парсинг списка конфигураций и списка версий. Это плохо. Не стоит делать обобщенные функции. В перспективе, буду их разбивать.

Суть функции заключается в том, что на вход функции подается гиперссылка или ссылка на конфигурацию (из справочника уже спарсенных в конфигурацию). Если подается гиперссылка, то скорее всего это будет ссылка "/total". Это заглавная страница Releases, на которой есть все конфигурации. Если на вход подается объект справочника Конфигурация, то из нее собирается ссылка для обработки. Первая попытка пытается скачать HTML документ. Здесь же мы видим процедуру записи документа (для проверки толи качает наш парсер), в перспективе можете ее убрать.

Перед очередной попыткой стоит проверка "/total" это или нет, но в принципе, как вы понимаете она весьма бесполезна, так как можно проверять подалась ли конфигурация на вход, в общем глупая проверка, которую надо сесть и переделать.
Далее, программа пытается ее распарсить (ниже дам код, который был переделан под мою цель, но он не стал лучше и свою главную ошибку я опишу.
Естественно, пользователь получает сообщения для отладки, вы их можете убрать. Писал я без БСП, поэтому немного кривые названия.
Если же это не список конфигураций, то программа пытается спарсить список версий.
Ниже прикладываю последнюю процедуру.

Оригинал

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

&НаСервере
Процедура РекурсивныйПарсингСпискаКонфигураций(ДокументHTML)
	//Нужно изолировать запрос, чтобы он не был в рекурсии
	
	// Инциализируем переменные
	// В данном случае я использую группу справочника ЗагруженныеКонфигурации
	// для сравнения с текстом наименования группы
	// По умолчанию, до проверок, первая строка - активная
	ТекИмяГруппы = "";
	ТекГруппа = Справочники.ФИКС_ЗагруженныеКонфигурации.ПустаяСсылка();
	НеактивнаяСтрока = ЛОЖЬ;
	
	// Пробиваем дочерние узлы HTML документа. Наша задача наткнуться на узел с текстом "Название"
	// От него отталкиваться удобнее, так как этот текст встречается сугубо в шапке и более нигде в такой вариации
	// Регистр учитывается
	Для Каждого ЭлДок Из ДокументHTML.ДочерниеУзлы Цикл
		
		Если СокрЛП(ЭлДок.ТекстовоеСодержимое) = "Название" Тогда
			
			// Количество строк в таблице дистрибутивов. Если этот код перестанет работать,
			// а судя по его кривизне и хардкодности это весьма вероятно, если 1С решит сменить что-то на сайте
			// этот код также необходимо будет пересмотреть. В данном случае он отсматривает
			// на 3 родительских элемента назад, выбирает его дочерний узел (второй по порядку)
			// и выдает нам узел, в котором находятся все конфигурации
			СписокУзловHTML = ЭлДок.РодительскийУзел.РодительскийУзел.РодительскийУзел.ДочерниеУзлы[2].ДочерниеУзлы;
			КолЭлементов = СписокУзловHTML.Количество();
			
			Для А=0 По КолЭлементов-1 Цикл
				
				// Здесь мы отсекаем группы по понятному признаку
				Если СписокУзловHTML[А].Атрибуты[0].Имя = "group" Тогда
					ТекИмяГруппы = СокрЛП(СписокУзловHTML[А].ДочерниеУзлы[0].ДочерниеУзлы[3].ТекстовоеСодержимое);
				КонецЕсли;
				
				// Здесь отсекаем неактивные строки (недоступные нам конфигурации)
				// В принципе все просто, у неактивных строк не будет дочерних узлов кроме текста
				// по такому признаку я их признаю неактивными
				Если СписокУзловHTML[А].ДочерниеУзлы[0].ДочерниеУзлы.Количество() = 1 Тогда
					НеактивнаяСтрока = ИСТИНА;
				КонецЕсли;
				
				//Создание групп, исключая те, которые уже есть (пока без родителя и фильтров)
				Если СокрЛП(ТекГруппа.Наименование) <> ТекИмяГруппы Тогда
				
					//ЗАПРОСЫ В ЦИКЛАХ ЭТО ПЛОХО. В ОРИГИНАЛЕ ОНИ ИСПОЛЬЗОВАЛИСЬ ТОЖЕ. НУЖНА ПЕРЕДЕЛКА ЭТОГО МЕТОДА
					
					Запрос = Новый Запрос;
					Запрос.УстановитьПараметр("Наименование", ТекИмяГруппы);
					Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
					|	ФИКС_ЗагруженныеКонфигурации.Ссылка КАК Ссылка
					|ИЗ
					|	Справочник.ФИКС_ЗагруженныеКонфигурации КАК ФИКС_ЗагруженныеКонфигурации
					|ГДЕ
					|	ФИКС_ЗагруженныеКонфигурации.Наименование ПОДОБНО &Наименование
					|	И ФИКС_ЗагруженныеКонфигурации.ЭтоГруппа";
					Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
					
					Если Выборка.Следующий() Тогда
						ТекГруппа = Выборка.Ссылка;
					Иначе
						// Создаем группу
					КонецЕсли;
					
				Иначе
					
					// Проверили что строка неактивна и идем мимо нее (для следующей сбрасываем активность)
					Если НеактивнаяСтрока Тогда
						НеактивнаяСтрока = ЛОЖЬ;
						
					// Если строка активная анализируем
					Иначе
						
						// Проверяем является ли этот элемент конфигурацией
						ТекЯчейка = СписокУзловHTML[А].ДочерниеУзлы[0].ДочерниеУзлы[1];	
						Если ТипЗнч(ТекЯчейка) = Тип("ЭлементЯкорьHTML") Тогда
							
							// Получаем данные наименования и гиперссылки
							СтрТЧ = Объект.Программы.Добавить();
							СтрТЧ.Наименование = СокрЛП("" + ТекЯчейка.ТекстовоеСодержимое);
							СтрТЧ.ГиперСсылка = СокрЛП(ТекЯчейка.ГиперСсылка);
							
							// Получаем дату и номер версии конфигурации
							Попытка
								СтрТЧ.НомерВерсии = СокрЛП(СписокУзловHTML[А].ДочерниеУзлы[2].ДочерниеУзлы[1].ТекстовоеСодержимое);
								СтрТЧ.ДатаВыхода = ФИКС_ОбщийФункционалСервер.РазобратьДату(СокрЛП(СписокУзловHTML[А].ДочерниеУзлы[4].ДочерниеУзлы[1].ТекстовоеСодержимое));	
							Исключение
								СтрТЧ.НомерВерсии = "";
								СтрТЧ.ДатаВыхода = "";
							КонецПопытки;                              
							
							// Присваиваем ей группу
							СтрТЧ.ГруппаКонфигураций = ТекГруппа;
							
							//ЗАПРОСЫ В ЦИКЛАХ ЭТО ПЛОХО. В ОРИГИНАЛЕ ОНИ ИСПОЛЬЗОВАЛИСЬ ТОЖЕ. НУЖНА ПЕРЕДЕЛКА ЭТОГО МЕТОДА
							
							Запрос = Новый Запрос;
							Запрос.УстановитьПараметр("Наименование", СокрЛП("" + ТекЯчейка.ТекстовоеСодержимое));
							Запрос.УстановитьПараметр("Родитель", ТекГруппа);
							Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
							|	ФИКС_ЗагруженныеКонфигурации.Ссылка КАК Ссылка
							|ИЗ
							|	Справочник.ФИКС_ЗагруженныеКонфигурации КАК ФИКС_ЗагруженныеКонфигурации
							|ГДЕ
							|	ФИКС_ЗагруженныеКонфигурации.Наименование ПОДОБНО &Наименование
							|	И НЕ ФИКС_ЗагруженныеКонфигурации.ЭтоГруппа
							|	И ФИКС_ЗагруженныеКонфигурации.Родитель = &Родитель";
							Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
							
							Если Выборка.Следующий() Тогда
								
								// Получение ссылки в программе, если ее нет, возвращает пустую
								СтрТЧ.СсылкаВПрограмме = ФИКС_ОбщийФункционалСервер.ОпределитьКонфигурацию(Выборка.Ссылка);
								
								// Если нет даты и версии - мой код считает, что данная конфигурация не обновлялась
								// В данном случае попытка обусловлена (очевидно кривизной кода) тем, что я зачем-то
								// храню список версий в самом объекте конфигурации, что очевидно неверно
								// В перспективе есть желание реализовать красивый механизм через регистр сведений
								Попытка
									СтрТЧ.СсылкаНомерВерсии = СтрТЧ.СсылкаВПрограмме.ВерсииКонфигураций[0].Версии.Ссылка;
									СтрТЧ.СсылкаДата = СтрТЧ.СсылкаВПрограмме.ВерсииКонфигураций[0].ДатаВерсии;
								Исключение
									ФИКС_ОбщийФункционалСервер.СообщитьССервера(Выборка.Ссылка.Наименование + " - На эту конфигурацию еще не скачивались обновления");
								КонецПопытки;
							Иначе
								// Создаем объект справочника конфигураций
							КонецЕсли;
							
							// Если то, что спарсилось больше по дате выхода, чем то что в базе - ставится пометка на обновление версий. Без него не парсится
							Если (СтрТЧ.ДатаВыхода <> СтрТЧ.СсылкаДата) И ЗначениеЗаполнено(СтрТЧ.ДатаВыхода) И ЗначениеЗаполнено(СтрТЧ.СсылкаВПрограмме) Тогда
								ФИКС_ОбщийФункционалСервер.КонфигОбновитьВерсии(СтрТЧ.СсылкаВПрограмме.Ссылка, ИСТИНА);
							КонецЕсли;
							
						КонецЕсли;
					КонецЕсли;
				КонецЕсли;
			КонецЦикла;
			
		КонецЕсли;
		
		// Если у документа есть дочерние узлы - рекурсия
		Если ЭлДок.ЕстьДочерниеУзлы() Тогда
			
			РекурсивныйПарсингСпискаКонфигураций(ЭлДок);
			
		КонецЕсли;
		
	КонецЦикла;
	


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ЖИРНЫЙ МИНУС НОМЕР ДВА: запросы в цикле - это ужасно. Когда я вернулся к этому коду, чтобы поделиться им здесь, я нашел очень неприятные для себя моменты, но пока решил написать, а в дальнейшем уже поправить. Если в этом будет еще смысл в перспективе. 

Как вы успели заметить, я использую в своей конфигурации 2 справочника. Это "ЗагруженныеКонфигурации" и просто "Конфигурации". Смысл этого в том, что я хотел сократить количество запросов к серверу 1С и переборов циклов. В итоге у меня парсится весь список конфигураций в справочник "ЗагруженныеКонфигурации", затем он сопоставляет себя с объектами, которые находятся в справочнике "Конфигурации" и если все ок - парсинг закончен, если нет - ставит пометку на обновление и идет второй цикл итераций (такая же рекурсивная процедура), но уже с версиями. Смысл там такой же, код также близок к текущему.

Таким образом, благодаря поддержке участников сообщества инфостарта, идей многих люедй из интернета, различным инструкциям, я смог осуществить свой первый парсинг в 1С, который к тому же очень не плохо разносился по справочникам и был весьма удобен в эксплуатации. Далее, я столкнулся уже с другими проблемами, но это совсем другая история

 

P.S. Я надеюсь, что эта статья поможет тем, кто захочет ее прочесть, ну или будет занятна тем, кому совсем грустно сидеть вечером. Критику касательно кода готов принимать. Если вы пишете замечание, напишите свое решение или заметки из области где смотреть. Думаю это поможет тем, кто будет читать не допускать тех ошибок, которые допустил я.

конфигурация http запросы парсинг ИТС программирование проза легкий сай-фай киберпанк

См. также

Сайты и интернет-магазины Платформа 1С v8.3 1С:Розница 2 Розничная и сетевая торговля (FMCG) Россия Платные (руб)

Готовое интеграционное решение для оплаты покупок Долями в 1C:Розница 2.3. Реализовано в виде расширения. Интеграция сервиса dolyame.ru для приема платежей в рассрочку. Поддерживает работу от разных юридических лиц. Работа: в составе РИБ, отдельно от РИБ, тонкий, толстый клиент, web-клиент (через интернет-браузер).

20400 руб.

19.12.2023    4320    29    9    

28

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

В настоящее время система СБП очень часто стала использоваться в повседневной жизни. Одна из систем интеграции СБП через СБЕР. Данная конфигурация является инструментом интеграции СБП в Альфа Авто. Данная система не просто формирует статический QR, а динамический, а значит, в системе будет привязка и на покупателя, и на документ.

6000 руб.

25.10.2022    6181    31    4    

10

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

ВАЖНО: расширения не предназначены для модуля обмена Битрикс 8.х. Полный список ограничений см. ниже в разделе ОГРАНИЧЕНИЯ. Расширение предназначено для выгрузки на сайт Битрикс сопутствующих товаров, аналогов, рекомендованных и прочих связанных товаров.

3600 руб.

25.07.2018    45755    87    77    

95

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

Данный модуль предоставляет возможность выгружать заказы из 1С: Управление торговлей 11 версии в "MEASOFT" (ранее "Курьерская служба 2008").

9600 руб.

27.04.2022    11308    19    3    

10
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Steelvan 305 01.09.21 11:31 Сейчас в теме
Способ получения html через "взять" запрос с дальнейшим разбором перестанет работать, когда 1Сники перейдут на ОСП (одностраничное приложение) и начнут отрисовывать html на клиенте.

Например, через react или vue.
Уже сейчас половина сайтов на ОСП и это тупиковый способ разбора. Особенно для информативных сайтов, которым плевать на продвижение в поисковиках.


Гораздо прямее пользовать https://infostart.ru/public/1492489/ для разбора через devtools приблуды разраба в обозревателе через веб-гнезда.
ldmonster; +1 Ответить
2. ldmonster 99 01.09.21 11:50 Сейчас в теме
(1)
Спасибо, ваша статья очень интересная. Мне очень импонирует ваша работа. Читаю с упоением. В перспективе планирую освоить ваш метод.
3. MamZhan 08.09.21 19:31 Сейчас в теме
Спасибо, статья очень понравилась.
А есть ссылка на первую главу. Хотел почитать
4. ldmonster 99 08.09.21 20:00 Сейчас в теме
(3)
Идея была такова, что я сначала напишу все главы, а затем первую, в которой познакомлюсь с читающими. Будут еще статьи, касательно того, как я писал свою CRM систему (хорошую-ли, плохую-ли, решат читатели). Я очень хочу, чтобы мои знания помогли кому-то в его работе-деятельности.

Еще добавлю, что уже переписал код для уменьшения количества запросов к базе, а также дописал сигнализацию к обновлениям по текущим базам, которые отслеживаются в конфигурации. Реализацию этого всего положу в следующих статьях.
5. AlexPrikh 02.12.22 14:36 Сейчас в теме
Я уже на этом моменте

// Соединяемся (смотри соединение 2) по адресу releases.1c.ru/total и получаем ответ
ОтветHTTP1 = Соединение2.Получить(HTTPЗапрос1);

получаю код ответа 302 ....
6. AlexPrikh 02.12.22 15:25 Сейчас в теме
(5) Поправка, то что тут ОтветHTTP1 = Соединение2.Получить(HTTPЗапрос1); получаю 302 я так понимаю это нормально, но почему то я и тут ОтветHTTP4 = Соединение2.Получить(HTTPЗапрос1); - то же получаю 302..
8. ldmonster 99 05.12.22 08:23 Сейчас в теме
(6)
Скорее всего поменялся процесс авторизации на сайте. Если вы не будете смотреть в сторону реализации парсера через партнерское API от 1C, то здесь единственный путь - отлаживать код в браузере через меню разработчика. Смотреть на запросы и ответы и куда бросает сам сайт браузер. В коде 1С мы лишь дотошно следуем алгоритму, по которому ходит браузер.

(7)
Это GET запрос. Параметры идентичные тем, которые шлёт браузер. Все можно увидеть в режиме разработчика.

Сразу хочу сказать, что в связи с тем, что мне пришлось покинуть сферу 1с спустя 3 месяца, после написания данной статьи, то очевидно, что решение из статьи сейчас не поддерживается, поэтому скопировать рабочий код я не могу физически (да и подозреваю, что мои наработки работодатель уже грохнул сам за ненадобностью).
Из советов, могу дать такие:
1) Попробуйте посмотреть в сторону партнерского api, когда я ещё писал, говорили что оно существует и достаточно простое. Сам не видел, но это очень хороший и простой вариант.
2) Если все-же решились мучаться, изучите что такое GET и POST запрос, как они собираются, для чего нужны. Эта информация не просто набор беспорядочных данных, а очень нужная штука. Познакомьтесь с заголовками и кодами ответов. Для любого разработчика сейчас это базовые знания.
9. AlexPrikh 05.12.22 18:28 Сейчас в теме
(8) Я уже разобрался)) все работает)
7. AlexPrikh 05.12.22 07:28 Сейчас в теме
Подскажи пожалуйста, как ты собирал эту строку? По какому принципу? Где можно посмотреть как она на сайт передается? через исследовать в браузере?

inviteCode = "inviteCode=&username=" + ИспользуемыеДанныеИТС.Логин
+ "&password=" + ИспользуемыеДанныеИТС.Пароль
+ "&execution=" + loginForm.Элементы[6].Значение
+ "&_eventId=submit"
+ "&geolocation="
+ "&submit=Войти"
+ "&rememberMe=on"
;
Оставьте свое сообщение