Парсинг многостраничного сайта "Рейтинг Центров Компетенции 1С"


После прочтения интересной статьи http://infostart.ru/public/88106/ от opx, у меня осталось несколько вопросов, связанных с парсингом многостраничных сайтов. Кроме того, хотелось закрепить полученные навыки.

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

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

В обработке создадим две дополнительные формы: ДопФорма (размещен элемент ПолеHTML документа, имя Сайт), ДопФорма2 (размещен элемент ПолеHTML документа, имя Сайт)

//описание переменных главной формы
Перем ТЗСтраниц;
Перем НТТР; // - переменная для хранения соединения с сервером
Перем ИмяВходящегоФайла;
Перем СерверИсточник;
Перем Адрес; // - переменная для хранения адреса начальной страницы
Перем НомСтр; // - переменная для подсчета строк в таблице
Перем ТекНом; // - текущий номер страницы франчайзи
Перем ФлагОперации; //- переменная определяет, что нужно грузить (всех франчей или тех что выбрал пользователь)

 Устанавливаем соединение с сайтом

СерверИсточник = "http://www.1c.ru/";
Адрес = "/rus/partners/ckp.jsp";
ИмяВходящегоФайла= КаталогВременныхФайлов()+"input.txt";
Если прокси Тогда
ПроксиСервер = Новый ИнтернетПрокси;
ПроксиСервер.Пользователь = Пользователь;
ПроксиСервер.Пароль = Пароль ;
ПроксиСервер.Установить(Протокол, АдресПрокси,  Порт);

// Укажем в HTTP соединении что у нас есть прокси
НТТР = Новый HTTPСоединение(СерверИсточник,,,, ПроксиСервер);
иначе
НТТР = Новый HTTPСоединение(СерверИсточник,,,,);
КонецЕсли;

НТТР.Получить(Адрес, ИмяВходящегоФайла);
ТекстИзФайла = Новый ТекстовыйДокумент;
ТекстИзФайла.Прочитать(ИмяВходящегоФайла);
Тест = ТекстИзФайла.ПолучитьТекст();
ФлагОперации="ПервичнаяЗагрузка";
ЭлементыФормы.Сайт.УстановитьТекст(Тест);

Заполняем список регионов на форме

Процедура ЗаполнитьРегионы()
//очищаем поле выбора
ЭлементыФормы.ПолеВыбораРегион.СписокВыбора.Очистить();
//цикл по всей страничке
Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
//ищем элемент с именем тэга "SELECT"  и именем "r"
Если Стр.tagName = "SELECT" и Стр.name="r" Тогда
//ищем регионы цикл по подчиненным элементам списка
Для каждого Стр1 из Стр.all Цикл
//для всех подчиненных элементов получаем значения, кроме пустых
Если Стр1.value<>"" Тогда
СтрСп=ЭлементыФормы.ПолеВыбораРегион.СписокВыбора.Добавить();
СтрСп.Значение=Стр1.value;
СтрСп.Представление=Стр1.text;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры

Заполняем список городов на форме.

Процедура ЗаполнитьГорода()
//очищаем поле выбора
ЭлементыФормы.ПолеВыбораГород.СписокВыбора.Очистить();
//цикл по всей страничке
Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
//ищем элемент с именем тэга "SELECT"  и именем " city "
Если Стр.tagName = "SELECT" и Стр.name="city" Тогда
//ищем города цикл по подчиненным элементам списка
Для каждого Стр1 из Стр.all Цикл
//для всех подчиненных элементов получаем значения, кроме пустых
Если Стр1.value<>"" Тогда
СтрСп=ЭлементыФормы.ПолеВыбораГород.СписокВыбора.Добавить();
СтрСп.Значение=Стр1.value;
СтрСп.Представление=Стр1.text;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры

Заполняем отрасли. 

Процедура ЗаполнитьОтрасли()
//очищаем поле выбора
ЭлементыФормы.ПолеВыбораОтрасль.СписокВыбора.Очистить();
Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
//ищем элемент с именем тэга "SELECT"  и именем " branch_ckp "
Если Стр.tagName = "SELECT" и Стр.name="branch_ckp" Тогда
//ищем отрасли цикл по подчиненным элементам списка
Для каждого Стр1 из Стр.all Цикл
Если Стр1.value<>"" Тогда
СтрСп=ЭлементыФормы.ПолеВыбораОтрасль.СписокВыбора.Добавить();
СтрСп.Значение=Стр1.value;
СтрСп.Представление=Стр1.text;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры

Считываем количество страниц и заполняем доп данные для отбора.

Процедура СайтДокументСформирован(Элемент)
фл=0;
Если НомСтр=1 Тогда
ОпределитьКоличествоСтраниц();
ЗаполнитьРегионы();
ЗаполнитьОтрасли();
КонецЕсли;
КонецПроцедуры



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

Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
Если Стр.tagName = "P" и Найти(Стр.innerText,"Страницы:")>0 Тогда  //ищем раздел страниц
стррез=Стр.innerHTML;
поз=Найти(стррез,"href=");
стрперехода=Прав(стррез,СтрДлина(стррез)-поз-5);
поз=Найти(стрперехода,"""");
стрПерехода=Лев(стрперехода,поз);
Если стрПерехода<>Неопределено Тогда
//определим тело ссылки
ТелоСсылки=Лев(стрПерехода,Найти(стрПерехода,"=")-1);
колссылок=СтрЧислоВхождений(стррез,ТелоСсылки);
СтрТЗ=ТЗСтраниц.Добавить();
СтрТЗ.Ссылка=ТелоСсылки;
СтрТЗ.Номер=1;

Пока колссылок>0 Цикл
СтрТЗ=ТЗСтраниц.Добавить();
стррез=Сред(стррез,Найти(стррез,"href=")+5);
стррез=Сред(стррез,СтрДлина(ТелоСсылки)+2);
СтрТЗ.Ссылка=ТелоСсылки;
Если СокрЛП(стррез)<>"" Тогда
СтрТЗ.Номер=число(сред(стррез,2,Найти(стррез,"""")-2));//Лев(стррез,Найти(стррез,"""")-1));
КонецЕсли;
колссылок=колссылок-1;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;


ТЗСтраниц.Свернуть("ССылка,Номер");
//дополняем недостающие страницы
РазностьСтраниц=число(СтрЗаменить(ТЗСтраниц[ТЗСтраниц.Количество()-1].Номер,"=",""))-число(СтрЗаменить(ТЗСтраниц[0].Номер,"=",""))+1;
Пока РазностьСтраниц>1 Цикл
РазностьСтраниц=РазностьСтраниц-1;

СтрокаТЗ=ТЗСтраниц.Найти(РазностьСтраниц,"Номер");
Если СтрокаТЗ<>Неопределено Тогда
Продолжить;
иначе
СтрТЗ=ТЗСтраниц.Добавить();
СтрТЗ.Ссылка=ТелоСсылки;
СтрТЗ.Номер=число(РазностьСтраниц);
КонецЕсли;
КонецЦикла;
ТЗСтраниц.Сортировать("Номер возр");
ИначеЕсли ФлагОперации="ВыборРегиона" Тогда
ЗаполнитьГорода();
КонецЕсли;
КонецПроцедуры 

Затем у нас два варианта: заполнить по всем франчам без отбора или заполнить с отбором.

//заполняем таблицу данными с указанынми регионом/городом/отраслью
Процедура ОсновныеДействияФормыЗагрузитьВыбранные(Кнопка)
ЭтотОбъект.Франчайзи.Очистить();
НТТР.Получить(Адрес+"?armRange=&r="+?(ВыбранныйРЕгион="","",ВыбранныйРЕгион)+"&city="+?(ВыбранныйГород="","",ВыбранныйГород)+"&branch_ckp="+ЭлементыФормы.ПолеВыбораОтрасль.Значение, ИмяВходящегоФайла);
ТекстИзФайла = Новый ТекстовыйДокумент;
ТекстИзФайла.Прочитать(ИмяВходящегоФайла);
Тест = ТекстИзФайла.ПолучитьТекст();
Форма = ПолучитьФорму("ДопФорма");
Форма.Город=ЭлементыФормы.ПолеВыбораГород.Значение;
Форма.ЭлементыФормы.Сайт.УстановитьТекст(Тест);
Форма.ОткрытьМодально();
КонецПроцедуры



//загружаем всех франчей без отбора
Процедура ОсновныеДействияФормыЗагрузитьДанные(Кнопка)
ЗаполнитьТаблФранчей();
КонецПроцедуры

Обработать все страницы и заполнить табличную часть

Процедура ЗаполнитьТаблФранчей()
кол=0;
Для каждого НомСтр из ТЗСтраниц Цикл
ЗаполнитьДопТаблицу(НомСтр.Номер,НомСтр.ссылка);
КонецЦикла;
ЗаписатьДанныеВРегистр()
КонецПроцедуры


//открываем форму, которая читает следующую страницу
Процедура ЗаполнитьДопТаблицу(Номер,Ссылка)
//считываем очередную по номеру страницу, если выбрана полная загрузка
НТТР.Получить(Адрес+Ссылка+"="+строка(Номер), ИмяВходящегоФайла);
ТекстИзФайла = Новый ТекстовыйДокумент;
ТекстИзФайла.Прочитать(ИмяВходящегоФайла);
Тест = ТекстИзФайла.ПолучитьТекст();
Форма = ПолучитьФорму("ДопФорма");// устанавливаем значения доп формы: номер страницы и текст страницы
Форма.ЭлементыФормы.Сайт.УстановитьТекст(Тест);
Форма.ПереданныйФлаг=Номер;
Форма.ОткрытьМодально(); // открываем форму в этот момент загружается страница и происходит ее обработка
КонецПроцедуры

Код первой допформы:

Перем ТекущийФранчайзи,Город Экспорт;
Перем флЗагрузки;

Процедура ГрузимДанные()
строканом=0;

Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
Если Стр.tagName = "TABLE" и Стр.classname = "content" Тогда
фл=1;
ИначеЕсли Стр.tagName = "TABLE" и Стр.classname <> "content" Тогда
фл=0;
КонецЕсли;
Если Стр.tagName = "TR" и фл=1 Тогда
строканом=строканом+1;
Если строканом>1 тогда
Если Стр.childNodes.length>1 Тогда //если узлов больше то это данные
новСтр = Франчайзи.Добавить();

Для каждого стрТЧ из Стр.childNodes Цикл
//сообщить(стрТЧ.innerText);
Если стрТЧ.cellIndex=1 Тогда
Если НЕ ЗначениеЗаполнено(Город) Тогда
новСтр.Город=Лев(стрТЧ.innerText,Найти(стрТЧ.innerText,"/")-1);
стрТЧ.innerText=Прав(стрТЧ.innerText,СтрДлина(стрТЧ.innerText)-Найти(стрТЧ.innerText,"/"));
новСтр.Наименование =Лев(стрТЧ.innerText,Найти(стрТЧ.innerText,"/")-1);
Иначе
новСтр.Город =Город;
новСтр.Наименование =Лев(стрТЧ.innerText,Найти(стрТЧ.innerText,"/")-1);
КонецЕсли;
//регион ищем в адресном классификаторе
Запрос=Новый Запрос;
Запрос.Текст="ВЫБРАТЬ
| АдресныйКлассификатор1.Наименование,
| АдресныйКлассификатор1.Сокращение,
| АдресныйКлассификатор1.КодРегионаВКоде,
| АдресныйКлассификатор1.ТипАдресногоЭлемента
|ИЗ
| РегистрСведений.АдресныйКлассификатор КАК АдресныйКлассификатор1
|  ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.АдресныйКлассификатор КАК АдресныйКлассификатор
|  ПО АдресныйКлассификатор1.КодРегионаВКоде = АдресныйКлассификатор.КодРегионаВКоде
|ГДЕ
| АдресныйКлассификатор.Наименование = &Наим
| И АдресныйКлассификатор.Сокращение = ""г""
| И АдресныйКлассификатор1.ТипАдресногоЭлемента = 1";
Запрос.УстановитьПараметр("Наим",сокрлп(новСтр.Город));
ТЗЗапрос=Запрос.Выполнить().Выгрузить();
Если ТЗЗапрос.Количество()>0 Тогда
новСтр.Регион=ТЗЗапрос[0].Наименование+" "+ТЗЗапрос[0].Сокращение;
КонецЕсли;

ИначеЕсли стрТЧ.cellIndex=2 Тогда
новСтр.Спец=стрТЧ.innerText;
ИначеЕсли стрТЧ.cellIndex=3 Тогда
новСтр.АРМ =Лев(стрТЧ.innerText,Найти(стрТЧ.innerText,"/")-1);
новСтр.Внедр = Прав(стрТЧ.innerText,Найти(стрТЧ.innerText,"/")-3);
ИначеЕсли стрТЧ.cellIndex=4 Тогда
//новСтр.Отрасль=стрТЧ.innerText;
ИначеЕсли стрТЧ.cellIndex=5 Тогда
ЛевЧасть=Лев(стрТЧ.innerHTML,Найти(стрТЧ.innerHTML,""""));
стрТЧ.innerHTML=СтрЗаменить(стрТЧ.innerHTML,ЛевЧасть,"");
новСтр.Ссылка=Лев(стрТЧ.innerHTML,Найти(стрТЧ.innerHTML,"""")-1);
//парсим страницы со ссылками
СерверИсточник = "www.1c.ru";
Адрес = "/rus/partners/";
ИмяВходящегоФайла= КаталогВременныхФайлов()+"input.txt";
Если прокси Тогда
ПроксиСервер = Новый ИнтернетПрокси;
ПроксиСервер.Пользователь = Пользователь;
ПроксиСервер.Пароль = Пароль;
ПроксиСервер.Установить(Протокол,  АдресПрокси,  Порт);
// Укажем в HTTP соединении что у нас есть прокси
НТТР = Новый HTTPСоединение(СерверИсточник,,,, ПроксиСервер);
иначе
НТТР = Новый HTTPСоединение(СерверИсточник,,,,);
КонецЕсли;
НТТР.Получить(Адрес+новСтр.Ссылка, ИмяВходящегоФайла);
ТекстИзФайла = Новый ТекстовыйДокумент;
ТекстИзФайла.Прочитать(ИмяВходящегоФайла);
Тест = ТекстИзФайла.ПолучитьТекст();

Форма = ПолучитьФорму("ДопФорма2");
Форма.СтрокаТЗ=новСтр;
Форма.ЭлементыФормы.Сайт.УстановитьТекст(Тест);
Форма.ОткрытьМодально();
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Закрыть();
КонецПроцедуры

Процедура СайтДокументСформирован(Элемент)
флЗагрузки=флЗагрузки+1;
Если флЗагрузки=1 Тогда
Если ЭлементыФормы.Сайт.Документ.body.all.length>1 Тогда
ГрузимДанные();
КонецЕсли;
КонецЕсли;
КонецПроцедуры

Процедура ПриОткрытии()
флЗагрузки=0;
КонецПроцедуры

Код ДопФорма2 (переход по ссылке для получения показателей):

Перем СтрокаТЗ Экспорт;
Перем флЗагрузки;

Процедура ГрузимДанные()
Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
Если Стр.tagName = "TABLE" и Стр.classname = "content" Тогда
фл=1;
Если фл=1 Тогда
Для каждого стр2 из Стр.all Цикл
СтрТекст="";
ЗначениеСтр="";
//Число стандартных внедрений, выполненных по технологии СМК:
Если Стр2.tagName = "TR" И Найти(Стр2.innerText,"Число стандартных внедрений, выполненных по технологии СМК:")>0 Тогда
//сообщить(Стр2.innerText);
СтрТекст=СтрЗаменить(Стр2.innerText,"Число стандартных внедрений, выполненных по технологии СМК:","");
ЗначениеСтр=Лев(СтрТекст,Найти(СтрТекст,"/")-1);
СтрокаТЗ.ЧислоСтандартныхВнедрений=?(СокрЛП(ЗначениеСтр)="",0,число(ЗначениеСтр));
КонецЕсли;
//Число проектных внедрений, выполненных по технологии СМК:
Если Стр2.tagName = "TR" И Найти(Стр2.innerText,"Число проектных внедрений, выполненных по технологии СМК:")>0 Тогда
СтрТекст=СтрЗаменить(Стр2.innerText,"Число проектных внедрений, выполненных по технологии СМК:","");
ЗначениеСтр=Лев(СтрТекст,Найти(СтрТекст,"/")-1);
СтрокаТЗ.ЧислоПроектныхВнедрений=?(СокрЛП(ЗначениеСтр)="",0,число(ЗначениеСтр));
КонецЕсли;
Если Стр2.tagName = "TR" И Найти(Стр2.innerText,"Количество отчетов о ходе внедрения УПП и отраслевых решений на основе УПП, вошедших в число 10% наиболее полезных по содержанию для фирмы «1С»: ")>0 Тогда
СтрТекст=СтрЗаменить(Стр2.innerText,"Количество отчетов о ходе внедрения УПП и отраслевых решений на основе УПП, вошедших в число 10% наиболее полезных по содержанию для фирмы «1С»:","");
ЗначениеСтр=СокрЛП(СтрТекст);//Лев(СтрТекст,Найти(СтрТекст,"/")-1);
СтрокаТЗ.КоличествоОтчетовОВнедрении=?(СокрЛП(ЗначениеСтр)="",0,число(ЗначениеСтр));
КонецЕсли;

//РАЗРАБОТКА 1C-СОВМЕСТНЫХ РЕШЕНИЙ НА БАЗЕ УПП
//Число разработанных отраслевых решений на базе УПП:
Если Стр2.tagName = "TR" И Найти(Стр2.innerText,"Число разработанных отраслевых решений на базе УПП:")>0 Тогда
СтрТекст=СтрЗаменить(Стр2.innerText,"Число разработанных отраслевых решений на базе УПП:","");
ЗначениеСтр=Лев(СтрТекст,Найти(СтрТекст,"/")-1);
СтрокаТЗ.ЧислоРазработанныхОтраслевыхРешенийУПП=?(СокрЛП(ЗначениеСтр)="",0,число(ЗначениеСтр));
КонецЕсли;

//"Число разработанных модулей, интегрирующихся в УПП и расширяющих возможности УПП (не включая модули, входящие в состав отраслевых решений на УПП): "
Если Стр2.tagName = "TR" И Найти(Стр2.innerText,"Число разработанных модулей, интегрирующихся в УПП и расширяющих возможности УПП (не включая модули, входящие в состав отраслевых решений на УПП)")>0 Тогда
СтрТекст=СтрЗаменить(Стр2.innerText,"Число разработанных модулей, интегрирующихся в УПП и расширяющих возможности УПП (не включая модули, входящие в состав отраслевых решений на УПП):","");
ЗначениеСтр=Лев(СтрТекст,Найти(СтрТекст,"/")-1);
СтрокаТЗ.ЧислоРазработанныхМодулейУПП=?(СокрЛП(ЗначениеСтр)="",0,число(ЗначениеСтр));
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;

Закрыть();
КонецПроцедуры

Процедура ПолеHTMLДокумента1ДокументСформирован(Элемент)
флЗагрузки= флЗагрузки+1;
Если  флЗагрузки=1 Тогда
Если ЭлементыФормы.Сайт.Документ.body.all.length>1 Тогда
ГрузимДанные();
Иначе
Закрыть();
КонецЕсли;
КонецЕсли;
КонецПроцедуры

Процедура ПриОткрытии()
флЗагрузки=0;
КонецПроцедуры

Для получения названия города и некоторых регионов нужно перевести символы в 16-тиричную систему, в обработке это есть. Полученные даныне можно сохранить в регистр сведений и затем анализировать динамику, стоить графики.

4 Comments

  1. TheGrr

    Познавательно! Скоро мне это пригодится.

    PS Разукрасьте код, пожалуйста. )

    Reply
  2. tolyan_ekb

    (1) TheGrr, спасибо за совет, обязательно раскрашу. Буду рад, если публикация пригодиться.

    Reply
  3. graphbuh

    Прикольно! Помню мой экс-шеф во франчайзи проводил подобную работу. Было бы неплохо сделать мини конфигурацию, чтобы можно было увидеть динамику.

    Reply
  4. tolyan_ekb

    (3) graphbuh, на самом деле достаточно будет добавить регистр сведений, где хранить результаты. Тогда можно будет следить за динамикой.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *