Недавно, мой постоянный клиент решил проводить маркетинговые исследования по изменению цен на товары у конкурентов... и эти данные захотел использовать в 1С в связке с его прайс-листом + куча отчетов с графиками и процентным отклонением от цен основного конкурента
В результате этого, была написана обработка собирающая данные со страниц разных сайтов. Из целей конфиденциальности - сайты раскрывать не буду...
Вид обработки загрузки данных с сайта в 1С
Ниже код загрузки данных со страницы сайта , смысл такой :
в функция передается адрес страницы сайта полученный текст страницы обрабатывается, удаляются теги из полученного текста формируется ТЗ с данными По названию ищется поставщик из вспомогательного справочника Справочники.Pr_Поставщики.НайтиПоНаименованию(, если нет - создается на выходе ТЗ с данными Код 1C v 8.х Функция ПолучитьТаблицуДанных(Строка)
тзДанных = Новый ТаблицаЗначений;
Сервер = "site.ru";
Соединение = Новый HTTPСоединение(Сервер);
Заголовки = Новый Соответствие;
Заголовки.Вставить("host", Сервер);
ТекАдрес = СтрЗаменить(Строка.Наименование.УРЛСтраницы,"http://","");
ТекАдрес = СтрЗаменить(ТекАдрес,Сервер,"");
Запрос = Новый HTTPЗапрос(ТекАдрес, Заголовки);
Ответ =Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда // Данные получены, обрабатываем их
Содержимое= Ответ.ПолучитьТелоКакСтроку();
//Выведем тест полученной страницы
НачалоБлока = "<table id=" + """" + "table-price" + """" + " cellspacing=" + """" + "0" + """" + " border=" + """" + "1" + """" + " bordercolor=" + """" + "#dedede" + """" + " class=" + """" + "tablesorter" + """" + " >";
//НачалоБлока = "<div class=" + """" + "w100fl" + """" + " id=" + """" + "kurs" + """" + ">";
КонецБлока = "</table>";
ТекстБлока = Сред(Содержимое, Найти(Содержимое, НачалоБлока) + СтрДлина(НачалоБлока), Найти(Содержимое, КонецБлока) - Найти(Содержимое, НачалоБлока) - СтрДлина(НачалоБлока));
//Обработаем тест
обрТекст = СтрЗаменить(ТекстБлока,"<tr>","");
обрТекст = СтрЗаменить(обрТекст,"</tr>",Символы.ПС);
обрТекст = СтрЗаменить(обрТекст," >", ">");
обрТекст = СтрЗаменить(обрТекст,"</th> <th>", " | ");
обрТекст = СтрЗаменить(обрТекст,"</td> <td>", " | ");
обрТекст = СтрЗаменить(обрТекст,"</th> <th class=" + """" + "sortable_header" + """" + ">", " | ");
обрТекст = СтрЗаменить(обрТекст,"</th> <th >", " | ");
обрТекст = СтрЗаменить(обрТекст,"</th><th>", " | ");
обрТекст = СтрЗаменить(обрТекст,"</td><td>", " | ");
обрТекст = СтрЗаменить(обрТекст," >", ">");
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Ложь;
RegExp.Global = Истина;
RegExp.MultiLine = Истина;
RegExp.Pattern = "<[^>]*>"; //Ищем теги HTML
обрТекст=RegExp.Replace(обрТекст, "");
//Сообщить(обрТекст);
Если СокрЛП(обрТекст) = "Ни одной позиции не найдено." Тогда
Сообщить(Строка(Строка.Наименование)+ " = Ни одной позиции не найдено! Строка " + Строка.Наименование);
Возврат тзДанных;
КонецЕсли;
Если Найти(обрТекст, "Страница не найдена") > 0 Тогда
Сообщить(Строка(ТекАдрес)+" = Страница не найдена, строка " + Строка.Наименование);
Возврат тзДанных;
КонецЕсли;
//Преобразуем полученный текст в таблицу
Для н=1 По СтрЧислоСтрок(обрТекст)Цикл
СтрТекста=СтрПолучитьСтроку(обрТекст,н);
Если Найти(СтрТекста, "Длина Цена") > 0 Тогда
СтрТекста = СтрЗаменить(СтрТекста, "Длина Цена", "Длина | Цена");
КонецЕсли;
Если Найти(СтрТекста, "Марка стали Цена") > 0 Тогда
СтрТекста = СтрЗаменить(СтрТекста, "Марка стали Цена", "Марка стали | Цена");
КонецЕсли;
Если Найти(СтрТекста, ", Поставщик") > 0 Тогда
СтрТекста = СтрЗаменить(СтрТекста, ", Поставщик", " | Поставщик");
КонецЕсли;
//Выводим текст страницы построчно
мсДанных = ПолучитьМассивИзСтрокиСРазделителем(СтрТекста, "|", Истина);
Если н=1 Тогда
Для Каждого ткЗнач из мсДанных Цикл
СтрЗапрещСимволов = " .,<>""\/-";
ИмяКолонки = ткЗнач;
Для сч33 = 1 по СтрДлина(СтрЗапрещСимволов) Цикл
ЗапрещСимв = Сред(СтрЗапрещСимволов, сч33, 1);
ИмяКолонки = СтрЗаменить(ИмяКолонки, ЗапрещСимв, "_");
КонецЦикла;
// добавим нужное количество колонок- если перед колонкой Поставщик не хватает
// бывает, что в шапке определяется меньше колонок, чем реально в таблице
Если НРег(ИмяКолонки) = "поставщик" Тогда
Для Сч_дк = (Строка.Наименование.Родитель.Поставшик - 2) по тзДанных.Колонки.Количество() Цикл
тзДанных.Колонки.Добавить("Колонка_" + (тзДанных.Колонки.Количество() + 1));
КонецЦикла;
КонецЕсли;
тзДанных.Колонки.Добавить(ИмяКолонки);
КонецЦикла;
тзДанных.Колонки.Добавить("ном", , , 7);
Иначе
НовСтр = тзДанных.Добавить(); нмас=0;
Для Каждого ткЗнач из мсДанных Цикл
НовСтр[нмас] = ткЗнач;
нмас=нмас+1;
КонецЦикла;
НовСтр.ном = н;
КонецЕсли;
//Сообщить(стр);
КонецЦикла;
тзДанных.Колонки.Добавить("ЕстьСоответствие", Новый ОписаниеТипов("Булево"));
тзДанных.Колонки.Добавить("СпрPr_Поставщики", Новый ОписаниеТипов("СправочникСсылка.Pr_Поставщики"));
Для каждого стр из тзДанных Цикл
текПоставщик = Справочники.Pr_Поставщики.НайтиПоНаименованию(стр[Строка.Наименование.родитель.Поставшик-1]);
Если текПоставщик = Справочники.Pr_Поставщики.ПустаяСсылка() Тогда
НовЭлем = Справочники.Pr_Поставщики.СоздатьЭлемент();
НовЭлем.Наименование = СокрЛП(стр[Строка.Наименование.родитель.Поставшик-1]);
НовЭлем.Записать();
текПоставщик = НовЭлем.Ссылка;
КонецЕсли;
стр.СпрPr_Поставщики = текПоставщик;
КонецЦикла;
Иначе
тзДанных.Колонки.Добавить("ЕстьСоответствие", Новый ОписаниеТипов("Булево"));
тзДанных.Колонки.Добавить("СпрPr_Поставщики", Новый ОписаниеТипов("СправочникСсылка.Pr_Поставщики"));
Сообщить("Ошибка получения данных для строки " + Строка.Наименование);
КонецЕсли;
Возврат тзДанных;
КонецФункции
В коде используется вспомогательная функция ПолучитьМассивИзСтрокиСРазделителем
Код 1C v 8.2 УП // Функция разбивает строку разделителем.
//
// Параметры:
// пСтрока - Строка - которую разбиваем;
// *пРазделитель - Строка, "." - символ-разделитель;
// *ОбрезатьНепечатныеСимволы - Булево, *Ложь.
//
// Возвращаемое значение:
// Массив - фрагментов .
//
Функция ПолучитьМассивИзСтрокиСРазделителем(Знач Стр, Разделитель = ".", ОбрезатьНепечатныеСимволы = Ложь) Экспорт
МассивСтрок = Новый Массив;
Если Разделитель = " " Тогда
Стр = СокрЛП(Стр);
Пока 1=1 Цикл
Поз = Найти(Стр,Разделитель);
Если Поз=0 Тогда
МассивСтрок.Добавить(Стр);
Возврат МассивСтрок;
КонецЕсли;
МассивСтрок.Добавить(Лев(Стр,Поз-1));
Стр = СокрЛ(Сред(Стр,Поз));
КонецЦикла;
Иначе
ДлинаРазделителя = СтрДлина(Разделитель);
Пока 1=1 Цикл
Поз = Найти(Стр,Разделитель);
Если Поз=0 Тогда
Фрагмент = Стр;
Если ОбрезатьНепечатныеСимволы Тогда
Фрагмент = СокрЛП(Фрагмент);
КонецЕсли;
МассивСтрок.Добавить(Фрагмент);
Возврат МассивСтрок;
КонецЕсли;
Фрагмент = Лев(Стр,Поз-1);
Если ОбрезатьНепечатныеСимволы Тогда
Фрагмент = СокрЛП(Фрагмент);
КонецЕсли;
МассивСтрок.Добавить(Фрагмент);
Стр = Сред(Стр,Поз+ДлинаРазделителя);
КонецЦикла;
КонецЕсли;
Возврат МассивСтрок;
КонецФункции // ПолучитьМассивИзСтрокиСРазделителем()
Конечно, перед тем как мы начали это делать - прошерстили интернет и нашли несколько решений , вот они:
Код 1C v 8.х Соединение = ПолучитьCOMОбъект("","Microsoft.XMLHTTP");
ИмяВременногоФайла=ПолучитьИмяВременногоФайла("htm");
Соединение.open("GET", "http://mamba.ru/my", 0);
Соединение.send();
//status: //404 - Not Found //200 - Ok
ТаймАут = 200;
Начало=ТекущаяДата();
Пока Соединение.readyState <> 4 И (ТаймАут=0 ИЛИ ТекущаяДата()-Начало<ТаймАут) Цикл
бфДиалоги.ксОбработкаПрерыванияПользователя();
КонецЦикла;
//Сохраняем во временный файл
обСохранитьДвоичныйБуферВФайл(Соединение.responseBody, ИмяВременногоФайла);
Соединение.open("POST", "http://mamba.ru/my");
Соединение.send("login=xxx&password=&&&");
Функция обСохранитьДвоичныйБуферВФайл(Буфер, ИмяФайла) Экспорт
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Type = 1; //Бинарный
Поток.Mode = 3;
Поток.Open();
Поток.Write(Буфер);
Поток.SaveToFile(ИмяФайла);
Поток.Close();
КонецФункции
Код 1C v 8.х НТТР = ПолучитьCOMОбъект("","Microsoft.XMLHTTP");
ИмяФайлаОтвета = КаталогВременныхФайлов() + "filename.tmp";
НТТР.open("GET", "www.google.com",0,,);
НТТР.send();
StreamTypeEnum = Новый Структура("adTypeBinary, adTypeText", 1, 2);
ConnectModeEnum = Новый Структура(
"adModeRead, adModeReadWrite, adModeRecursive, adModeShareDenyNone,
|adModeShareDenyRead, adModeShareDenyWrite, adModeShareExclusive,
|adModeUnknown, adModeWrite", 1, 3, 4194304, 16, 4, 8, 12, 0, 2);
SaveOptionsEnum = Новый Структура("adSaveCreateNotExist, adSaveCreateOverWrite", 1, 2);
StreamOut = Новый COMОбъект("ADODB.Stream");
StreamOut.Type = StreamTypeEnum.adTypeBinary;
StreamOut.Mode = ConnectModeEnum.adModeReadWrite; //Нужны и чтение и запись
StreamOut.Open(); //Открыли на чтение и запись
StreamOut.Write(НТТР.responseBody);
StreamOut.SaveToFile(ИмяФайлаОтвета, SaveOptionsEnum.adSaveCreateOverWrite); //"2" - перезапись файла
StreamOut.Close();
И даже не этом сайте есть статья : Парсер сайта связного на 1С
Нужен парсер сайта в 1С!? - Обращайтесь, контакты в
профиле Код 1C v 8.х // Функция разбивает строку разделителем.
//
// Параметры:
// пСтрока - Строка - которую разбиваем;
// *пРазделитель - Строка, "." - символ-разделитель;
// *ОбрезатьНепечатныеСимволы - Булево, *Ложь.
//
// Возвращаемое значение:
// Массив - фрагментов .
//
Функция ПолучитьМассивИзСтрокиСРазделителем(Знач Стр, Разделитель = ".", ОбрезатьНепечатныеСимволы = Ложь) Экспорт
МассивСтрок = Новый Массив;
Если Разделитель = " " Тогда
Стр = СокрЛП(Стр);
Пока 1=1 Цикл
Поз = Найти(Стр,Разделитель);
Если Поз=0 Тогда
МассивСтрок.Добавить(Стр);
Возврат МассивСтрок;
КонецЕсли;
МассивСтрок.Добавить(Лев(Стр,Поз-1));
Стр = СокрЛ(Сред(Стр,Поз));
КонецЦикла;
Иначе
ДлинаРазделителя = СтрДлина(Разделитель);
Пока 1=1 Цикл
Поз = Найти(Стр,Разделитель);
Если Поз=0 Тогда
Фрагмент = Стр;
Если ОбрезатьНепечатныеСимволы Тогда
Фрагмент = СокрЛП(Фрагмент);
КонецЕсли;
МассивСтрок.Добавить(Фрагмент);
Возврат МассивСтрок;
КонецЕсли;
Фрагмент = Лев(Стр,Поз-1);
Если ОбрезатьНепечатныеСимволы Тогда
Фрагмент = СокрЛП(Фрагмент);
КонецЕсли;
МассивСтрок.Добавить(Фрагмент);
Стр = Сред(Стр,Поз+ДлинаРазделителя);
КонецЦикла;
КонецЕсли;
Возврат МассивСтрок;
КонецФункции // ПолучитьМассивИзСтрокиСРазделителем()
// Функция собирает строку из элементов массива с разделителем.
//
// Параметры:
// пМассив - Массив - из которого формируем строку;
// *пРазделитель - Строка - символ-разделитель.
//
// Возвращаемое значение:
// Строка.
//
Функция ПолучитьСтрокуСРазделителемИзМассива(пМассив, пРазделитель = ", ") Экспорт
Результат = "";
Для Каждого Элемент Из пМассив Цикл
Результат = Результат + пРазделитель + Строка(Элемент);
КонецЦикла;
Возврат Сред(Результат, СтрДлина(пРазделитель) + 1);
КонецФункции // ПолучитьСтрокуСРазделителемИзМассива()
// Получает первый фрагмент, отделяемый разделителем от строки.
// Написана для оптимизации по скорости.
//
// Параметры:
// пСтрока - Строка - которую разбиваем;
// *пРазделитель - Строка, "." - символ-разделитель;
// *пЛиИспользоватьГраницуЕслиМаркерНеНайден - Булево, *Истина.
//
// Возвращаемое значение:
// - Строка - первый фрагмент строки;
// Неопределено - в строке не обнаружен разделитель.
//
Функция ПолучитьПервыйФрагмент(пСтрока, пРазделитель = ".",
пЛиИспользоватьГраницуЕслиМаркерНеНайден = Истина) Экспорт
Позиция = Найти(пСтрока, пРазделитель);
Если Позиция > 0 Тогда
Возврат Лев(пСтрока, Позиция - 1);
Иначе
Если пЛиИспользоватьГраницуЕслиМаркерНеНайден Тогда
Возврат пСтрока;
Иначе
Возврат пСтрока;
КонецЕсли;
КонецЕсли;
КонецФункции // ПолучитьПервыйФрагмент()
// Получает последний фрагмент, отделяемый разделителем от строки.
//
// Параметры:
// пСтрока - Строка - в которой ищем;
// *пМаркер – Строка, "." – отсекающий маркер;
// *пЛиИспользоватьГраницуЕслиМаркерНеНайден - Булево, *Истина - разрешение использования границ строки
// в случае, если маркер не найден.
//
// Возвращаемое значение:
// Неопределено - маркер не найден;
// – Число – позиция маркера.
//
Функция ПолучитьПоследнийФрагмент(пСтрока, пМаркер = ".",
пЛиИспользоватьГраницуЕслиМаркерНеНайден = Истина) Экспорт
Подстрока = пСтрока;
МаркерНайден = Ложь;
Пока пМаркер <> "" Цикл
Позиция = Найти(Подстрока, пМаркер);
Если Позиция = 0 Тогда
Прервать;
КонецЕсли;
МаркерНайден = Истина;
Подстрока = Сред(Подстрока, Позиция + СтрДлина(пМаркер));
КонецЦикла;
Если Истина
И Не МаркерНайден
И пЛиИспользоватьГраницуЕслиМаркерНеНайден
Тогда
Возврат пСтрока;
ИначеЕсли МаркерНайден Тогда
Возврат Подстрока;
Иначе
Возврат "";
КонецЕсли;
КонецФункции // ПолучитьПоследнийФрагмент()
// Получает подстроку заключенную между первым вхождением начального маркера и первым вхождением
// в правой части конечного маркера. Сами маркеры не включаются в результат. Опционально - если
// маркер не найден, то границей считается граница строки.
//
// Параметры:
// пСтрока - Строка - в которой ищем;
// *пНачальныйМаркер - Строка, *Неопределено - начальный маркер подстроки;
// *пКонечныйМаркер - Строка, *Неопределено - конечный маркер подстроки;
// *пЛиИспользоватьГраницуЕслиМаркерНеНайден - Булево, *Истина - разрешение использования границ строки
// в случае, если маркер не найден;
// *пЛиВключатьМаркеры - Булево, *Ложь - включение маркеров в результат.
//
// Возвращаемое значение:
// Неопределено - обязательные условия не выполнены;
// Строка – найденная подстрока.
//
Функция ПолучитьСтрокуМеждуМаркерами(пСтрока, пНачальныйМаркер = Неопределено, пКонечныйМаркер = Неопределено,
пЛиИспользоватьГраницуЕслиМаркерНеНайден = Истина, пЛиВключатьМаркеры = Ложь) Экспорт
ПозицияНачальногоМаркера = Найти(пСтрока, пНачальныйМаркер);
Если Истина
И ПозицияНачальногоМаркера = 0
И пЛиИспользоватьГраницуЕслиМаркерНеНайден = Ложь
Тогда
Возврат Неопределено;
КонецЕсли;
Если Ложь
ИЛИ пНачальныйМаркер = Неопределено
ИЛИ ПозицияНачальногоМаркера = 0
Тогда
ПозицияНачальногоМаркера = - СтрДлина(пНачальныйМаркер);
КонецЕсли;
Стр = Сред(пСтрока, ПозицияНачальногоМаркера + СтрДлина(пНачальныйМаркер));
ПозицияКонечногоМаркера = Найти(Стр, пКонечныйМаркер);
Если Истина
И ПозицияКонечногоМаркера = 0
И пЛиИспользоватьГраницуЕслиМаркерНеНайден = Ложь
Тогда
Возврат Неопределено;
КонецЕсли;
Если Ложь
ИЛИ пКонечныйМаркер = Неопределено
ИЛИ ПозицияКонечногоМаркера = 0
Тогда
ПозицияКонечногоМаркера = СтрДлина(Стр) + 1;
КонецЕсли;
Результат = Лев(Стр, ПозицияКонечногоМаркера - 1);
Если пЛиВключатьМаркеры Тогда
Если пНачальныйМаркер <> Неопределено Тогда
Результат = пНачальныйМаркер + Результат;
КонецЕсли;
Если пКонечныйМаркер <> Неопределено Тогда
Результат = Результат + пКонечныйМаркер;
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции // ПолучитьСтрокуМеждуМаркерами()
Текст запроса может содержать предопределенные данные конфигурации, такие как:
- значения перечислений;
- предопределенные данные:
- справочников;
- планов видов характеристик;
- планов счетов;
- планов видов расчетов;
- пустые ссылки;
- значения точек маршрута бизнес-процессов.
Также текст запроса может содержать значения системных перечислений, которые могут быть присвоены полям в таблицах базы данных:
ВидДвиженияНакопления, ВидСчета и
ВидДвиженияБухгалтерии .
Обращение в запросах к предопределенным данным конфигурации и значениям системных перечислений осуществляется с помощью литерала функционального типа:
ЗНАЧЕНИЕ(<ПредставлениеЗначения>)
Для системных перечислений
<ПредставлениеЗначения> имеет вид:
<ИмяСистемногоПеречистления>.<Значение>
Допустимые имена системных перечислений приведены выше, с перечнем допустимых для каждого из них значений можно ознакомиться в его описании.
Для предопределенных данных конфигурации
<ПредставлениеЗначения> имеет вид:
<ТипПредопределенногоЗначения>.<ИмяОбъектаМетаданных>.<Значение>
<ТипПредопределенногоЗначения> может быть:
- Справочник (Catalog);
- ПланВидовХарактеристик (ChartOfCharacteristicTypes);
- ПланСчетов (ChartOfAccounts);
- ПланВидовРасчета (ChartOfCalculationTypes);
- Перечисление (Enum).
В качестве
<ИмяОбъектаМетаданных> указывается имя объекта метаданных, как оно задано в конфигураторе.
Для определенных в конфигурации перечислений <Значение> указывается как имя соответствующего объекта метаданных типа ЗначаниеПеречисления. Для всех остальных типов предопределенных значений <Значение> указывается как имя предопределенного элемента данных, как оно указано в конфигураторе, или
ПустаяСсылка (EmptyRef) для указания пустой ссылки.
Для точек маршрутов бизнес-процессов
<ПредставлениеЗначения> имеет вид:
БизнесПроцесс.<ИмяОбъектаМетаданных>.Точка.<ИмяТочкиМаршрута>.
Ниже приведены несколько
фрагментов запросов, поясняющих использование предопределенных данных в запросах:
Код 1C v 8.х ГДЕ Город = ЗНАЧЕНИЕ(Справочник.Города.Москва)
ГДЕ Город = ЗНАЧЕНИЕ(Справочник.Города.ПустаяСсылка)
ГДЕ ТипТовара = ЗНАЧЕНИЕ(Перечисление.ВидыТоваров.Услуга)
ГДЕ ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ГДЕ ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.БизнесПроцесс1.Точка.Действие1)
еще пример:
Код 1C v 8.х Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ВидыНоменклатуры.Ссылка как ссылка,
| ВидыНоменклатуры.Порядок
|ИЗ
| Перечисление.ВидыНоменклатуры КАК ВидыНоменклатуры
|ГДЕ
| ВидыНоменклатуры.Ссылка = Значение(Перечисление.ВидыНоменклатуры.Услуга) ";
ТЗ = Новый ТаблицаЗначений;
ТЗ = запрос.Выполнить().Выгрузить();
ЭлементыФормы.ТабличноеПоле1.Значение = ТЗ;
ЭлементыФормы.ТабличноеПоле1.СоздатьКолонки();